Отличным примером способности D генерировать код во время компиляции при помощи mixin'ов является манипулирование битами.
D предоставляет следующие операторы для манипулирования битами:
&
побитовое "и"
|
побитовое "или"
~
побитовое отрицание
<<
побитовый знаковый сдвиг влево
>>
побитовый знаковый сдвиг вправо (сохраняет знак в старшем бите)
>>>
побитовый беззнаковый сдвиг вправо
Часто встречающийся пример работы с битами - чтение значения одного бита.
D предоставляет core.bitop.bt
для наиболее часто встречающихся задач,
однако для начала, чтобы привыкнуть к работе с битами, давайте рассмотрим
полную реализацию методики проверки значения бита:
enum posA = 1;
enum maskA = (1 << posA);
bool getFieldA()
{
return _data & maskA;
}
Обобщением данной задачи является проверка блоков длиной более 1. Для этого потребуется специальная маска требуемой длины и соответствующий сдвиг блока перед наложением маски:
enum posA = 1;
enum lenA = 3;
enum maskA = (1 << lenA) - 1; // ...0111
uint getFieldA()
{
return (_data >> posA) & maskA;
}
Установку битов в таком блоке так же можно описать как отрицание маски, что позволит записывать значения только внутри заданного блока:
void setFieldA(bool b);
{
return (_data & ~maskAWrite) | ((b << aPos) & maskAWrite);
}
std.bitmanip
на помощьКонечно, всегда весело работать с битами вручную, и D предлагает
для этого полный набор инструментов. Однако в большинстве случаев
такой код придётся постоянно копировать, а это чревато ошибками
и с трудом поддаётся поддержке.
И здесь на помощь приходит модуль std.bitmanip
, позволяющий писать
поддерживаемый, легко читаемый код, управляющий битами, посредством
мощного механизма mixin'ов, не теряя при этом в производительности.
Взгляните на раздел с упражнениями. В нём определён BitVector
,
который использует лишь X битов, но при этом едва отличается от
обыкновенной структуры.
Модули std.bitmanip
и core.bitop
предоставляют значительный объём
вспомогательного кода для приложений, требующих малого потребления памяти.
Поскольку компилятор будет добавлять отступы для переменных размером
меньше, чем определено схемой размещения в текущей OS (size_t.sizeof
),
например для типов bool
, byte
, char
, рекомендуется начинать с полей
с бо́льшим выравниванием.