Сообщений 13    Оценка 105 [+1/-0]         Оценить  
Система Orphus

Как вставлять в программу на C++ двоичные константы?

Автор: Александр Шаргин
Опубликовано: 01.12.2001
Исправлено: 13.03.2005
Версия текста: 1.0

В языке C++ есть восьмеричные, десятичные и шестнадцатеричные константы. А двоичных - нет. Тем не менее, при помощи препроцессора можно соорудить макрос, который позволит нам смоделировать такие константы. Основная идея - преобразовывать восьмеричную константу в двоичную, выделяя из неё отдельные цифры и умножая их на соответствующий весовой коэффициент. Есть только одна проблема: в тип long влезет не более десяти цифр, а этого хватит только на формирование двоичных констант длиной в байт. Хотелось бы иметь и более длинные двоичные константы. Чтобы решить эту проблему, можно ввести дополнительные макросы, которые будут склеивать короткие двоичные последовательности в более длинные. Эти макросы могут выглядеть примерно так.

#define BIN8(x) BIN___(0##x)
#define BIN___(x)                                        \
	(                                                \
	((x / 01ul) % 010)*(2>>1) +                      \
	((x / 010ul) % 010)*(2<<0) +                     \
	((x / 0100ul) % 010)*(2<<1) +                    \
	((x / 01000ul) % 010)*(2<<2) +                   \
	((x / 010000ul) % 010)*(2<<3) +                  \
	((x / 0100000ul) % 010)*(2<<4) +                 \
	((x / 01000000ul) % 010)*(2<<5) +                \
	((x / 010000000ul) % 010)*(2<<6)                 \
	)

#define BIN16(x1,x2) \
    ((BIN(x1)<<8)+BIN(x2))

#define BIN24(x1,x2,x3) \
    ((BIN(x1)<<16)+(BIN(x2)<<8)+BIN(x3))

#define BIN32(x1,x2,x3,x4) \
    ((BIN(x1)<<24)+(BIN(x2)<<16)+(BIN(x3)<<8)+BIN(x4))

Для компиляторов, поддерживающих 64-разрядные целые, можно также ввести дополнительный макрос BIN64. Для Visual C++ он будет выглядеть так.

#define BIN64(x1,x2,x3,x4,x5,x6,x7,x8) \
    ((__int64(BIN32(x1,x2,x3,x4)) << 32) + __int64(BIN32(x5,x6,x7,x8)))

Обратите внимание, что к параметру макроса BIN8 при помощи оператора ## принудительно дописывается ведущий ноль, чтобы с ним можно было работать как с восьмеричной константой. Благодаря этому пользователь может смело применять макрос BIN8 как к числу с ведущими нулями, так и без них, и всё будет работать именно так, как он ожидает.

Использовать наши макросы можно примерно так.

    char  i1 = BIN8 (101010);
    short i2 = BIN16(10110110,11101110);
    long  i3 = BIN24(10110111,00010111,01000110);
    long  i4 = BIN32(11101101,01101000,01010010,10111100);

Самое замечательное состоит в том, что, хотя выражения для пересчёта из десятичной системы в двоичную получаются очень громоздкими, они остаются константными, а значит будут вычисляться в процессе компиляции. Это, в частности, означает, что наши двоичные числа можно использовать везде, где требуется константа (для задания размера массива, в метках case оператора switch, для указания ширины битового поля и т. д.).

Реализация макроса BIN8, показанная выше, достаточно прямолинейна. Этот же макрос можно реализовать более элегантными способами. Например, вот вариант, предложенный Игорем Ширкалиным.

#define BIN__N(x) (x) | x>>3 | x>>6 | x>>9
#define BIN__B(x) (x) & 0xf | (x)>>12 & 0xf0

#define BIN8(v) (BIN__B(BIN__N(0x##v)))

Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 13    Оценка 105 [+1/-0]         Оценить