Двоичная константа/литерал C++

Я использую известный шаблон, чтобы разрешить двоичные константы

template< unsigned long long N >
struct binary
{
  enum { value = (N % 10) + 2 * binary< N / 10 > :: value } ;
};

template<>
struct binary< 0 >
{
  enum { value = 0 } ;
};

таким образом, вы можете сделать что-то вроде binary::value. К сожалению, это имеет предел 20 цифр без знака долго долго.

кто-нибудь есть лучшее решение?

7 ответов


работает ли это, если у вас есть начальный ноль на вашем двоичном значении? Начальный ноль делает константу восьмеричной, а не десятичной.

что приводит к способу выжать еще пару цифр из этого решения-всегда начинайте двоичную константу с нуля! Затем замените 10 в вашем шаблоне на 8.


подходы, которые я всегда использовал, хотя и не такие элегантные, как у вас:

1 / Просто используйте hex. Через некоторое время вы просто узнаете, какие шестнадцатеричные цифры представляют собой какие битовые шаблоны.

2 / Используйте константы и или или добавьте их. Например (может потребоваться квалификаторы на битовых шаблонах, чтобы сделать их беззнаковыми или длинными):

#define b0  0x00000001
#define b1  0x00000002
: : :
#define b31 0x80000000

unsigned long x = b2 | b7

3 / Если производительность не критична и важна читаемость, вы можете просто сделать это во время выполнения с помощью такой функции, как "x = fromBin("101011011");".

4 / в качестве подлого решения вы можете написать предварительный процессор, который проходит через ваш *.cppme файлы и создает *.cpp, заменив все строки типа " 0b101011011 "на эквивалентные строки" 0x15b"). Я бы не сделал этого легко, так как есть всевозможные сложные комбинации синтаксиса, о которых вам, возможно, придется беспокоиться. Но это позволит вам написать свою строку так, как вы хотите, не беспокоясь о причудах компилятора, и вы можете ограничьте хитрость синтаксиса тщательным кодированием.

конечно, следующим шагом после этого будет исправление GCC для распознавания констант "0b", но это может быть излишним : -)


C++0x имеет пользовательские литералы, который можно использовать для реализации того, о чем вы говорите.

в противном случае, я не знаю, как улучшить этот шаблон.


вы можете добавить больше параметров шаблона не-типа для "имитации" дополнительных битов:

// Utility metafunction used by top_bit<N>.
template <unsigned long long N1, unsigned long long N2>
struct compare {
    enum { value = N1 > N2 ? N1 >> 1 : compare<N1 << 1, N2>::value };
};

// This is hit when N1 grows beyond the size representable
// in an unsigned long long.  It's value is never actually used.
template<unsigned long long N2>
struct compare<0, N2> {
    enum { value = 42 };
};

// Determine the highest 1-bit in an integer.  Returns 0 for N == 0.
template <unsigned long long N>
struct top_bit {
    enum { value = compare<1, N>::value };
};

template <unsigned long long N1, unsigned long long N2 = 0>
struct binary {
    enum {
        value =
            (top_bit<binary<N2>::value>::value << 1) * binary<N1>::value +
            binary<N2>::value
    };
};

template <unsigned long long N1>
struct binary<N1, 0> {
    enum { value = (N1 % 10) + 2 * binary<N1 / 10>::value };
};

template <>
struct binary<0> {
    enum { value = 0 } ;
};

вы можете использовать это как раньше, например:

binary<1001101>::value

но вы также можете использовать следующие эквивалентные формы:

binary<100,1101>::value
binary<1001,101>::value
binary<100110,1>::value

В основном, дополнительный параметр дает вам еще 20 бит для игры. При необходимости можно добавить еще больше параметров.

потому что значение места второго числа используется, чтобы выяснить, как далеко влево первое число нужно сдвинуть, второе число должно начинаться с единицы. (Это требуется в любом случае, так как запуск его с 0 приведет к тому, что число будет интерпретироваться как восьмеричное число.)


template<unsigned int p,unsigned int i> struct BinaryDigit 
{
  enum  { value = p*2+i };
  typedef BinaryDigit<value,0> O;
  typedef BinaryDigit<value,1> I;
};
struct Bin
{
  typedef BinaryDigit<0,0> O;
  typedef BinaryDigit<0,1> I;
};

позволяет:

Bin::O::I::I::O::O:: value

гораздо более подробный, но без ограничений (пока вы не нажмете размер unsigned int, конечно).


технически это не C и не C++, это конкретное расширение GCC, но GCC позволяет двоичные константы как видно здесь:

 The following statements are identical:

 i =       42;
 i =     0x2a;
 i =      052;
 i = 0b101010;

надеюсь, что это поможет. Некоторые компиляторы Intel и, я уверен, другие реализуют некоторые расширения GNU. Может, тебе повезло.


простое #define работает очень хорошо:

#define HEX__(n) 0x##n##LU

#define B8__(x) ((x&0x0000000FLU)?1:0)\
               +((x&0x000000F0LU)?2:0)\
              +((x&0x00000F00LU)?4:0)\
               +((x&0x0000F000LU)?8:0)\
               +((x&0x000F0000LU)?16:0)\
               +((x&0x00F00000LU)?32:0)\
               +((x&0x0F000000LU)?64:0)\
               +((x&0xF0000000LU)?128:0)

#define B8(d) ((unsigned char)B8__(HEX__(d)))
#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) + B8(dlsb))
#define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) + ((unsigned long)B8(db2)<<16) + ((unsigned long)B8(db3)<<8) + B8(dlsb))

B8(011100111)
B16(10011011,10011011)
B32(10011011,10011011,10011011,10011011)

Не мое изобретение, я видел это на форуме давным-давно.