С++ двоичная константа/литерал

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

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

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

Итак, вы можете сделать что-то вроде двоичного < 101011011 > :: value. К сожалению, для длинной длинной строки без знака это ограничение составляет 20 цифр.

Есть ли у кого-то лучшее решение?

Ответ 1

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

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

Ответ 2

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

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", но это может быть перебор: -)

Ответ 3

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

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

Ответ 4

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

// 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 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 бит для воспроизведения. При необходимости вы можете добавить еще больше параметров.

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

Ответ 5

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;
};

Разрешение:

Бен:: О:: I:: I:: О:: О:: Значение

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

Ответ 6

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

 The following statements are identical:

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

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

Ответ 7

Простой #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)

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