2009-10-01 3 views
1

У меня есть структура, содержащая битовое поле, которое может отличаться по размеру. Пример:Битовые поля с переменным размером с псевдонимом

struct BitfieldSmallBase { 
    uint8_t a:2; 
    uint8_t b:3; 
    .... 
} 

struct BitfieldLargeBase { 
    uint8_t a:4; 
    uint8_t b:5; 
    .... 
} 

и объединение, чтобы получить доступ ко всем битам сразу:

template<typename T> 
union Bitfield 
{ 
    T bits; 
    uint8_t all; // <------------- Here is the problem 

    bool operator & (Bitfield<T> x) const { 
     return !!(all & x.all); 
    } 
    Bitfield<T> operator + (Bitfield<T> x) const { 
     Bitfield<T> temp; 
     temp.all = all + x.all; //works, because I can assume no overflow will happen 
     return temp; 
    } 
    .... 
} 

typedef Bitfield<BitfieldSmallBase> BitfieldSmall; 
typedef Bitfield<BitfieldLargeBase> BitfieldLarge; 

Проблема заключается в том: Для некоторых битовых базовых классов, uint8_t не является достаточным. BitfieldSmall действительно вписывается в uint8_t, но BitfieldLarge этого не делает. Данные должны быть упакованы как можно плотнее (это будет обрабатываться инструкциями SSE позже), поэтому всегда использовать uint16_t не может быть и речи. Есть ли способ объявить поле «все» с интегральным типом, размер которого совпадает с битовым полем? Или другой способ доступа к битам в целом?

Я могу, конечно, отказаться от использования шаблона и явно объявить все виды битового поля, но я бы хотел избежать повторения кода (имеется довольно полный список операторов и функций-членов).

ответ

5

Вы также можете сделать интегральный тип параметром шаблона.

template<typename T, typename U> 
union Bitfield 
{ 
    T bits; 
    U all; 
} 

typedef Bitfield<BitfieldSmallBase, uint8_t> BitfieldSmall; 
typedef Bitfield<BitfieldLargeBase, uint16_t> BitfieldLarge; 
+0

Иногда решение так просто :-) – hirschhornsalz

1

Я узнал, трудный путь, что в то время как ширина битового на Варс, что вы используете это удобный способ получить компилятор, чтобы сделать вашу маскировку и смещение для вас, вы не можете сделать предположения о порядок и заполнение элементов в структуре. Его компилятор зависит и компилятор действительно меняет порядок и зависит от другого кода вашего проекта.

Если вы хотите рассматривать байт как дискретные поля, вам действительно нужно сделать это сложным путем.

+0

Портативность является вторичным, потому что мне нужно в любом случае использовать SSE, так что ограничивается x86_64 (и, возможно, x86). Для компиляторов, которые я использую (gcc, icc), я могу сделать безопасные предположения о макете структур, он определен в ABI. – hirschhornsalz

+0

, но не обязательно будет одинаковым в двух компиляторах. Или в * следующей * версии этих компиляторов. – jalf

+0

Оба icc и gcc соответствуют x86_64 ABI. Время от времени были поломки ABI, но они обычно очень стараются свести к минимуму воздействие. Если вы обнаружите случай, когда структура структуры icc отличается от gcc, вы даже можете зарегистрировать ошибку, потому что icc пытается быть как можно более gcc-совместимым. – hirschhornsalz

1

вы можете использовать шаблон метапрограммирование определить шаблон функции, которая отображает из BitfieldSmallBase, BitfieldLargeBase и т.д. в другой тип - uint8_t по умолчанию и uint16_t для BitfieldLargeBase в качестве специализации шаблона, а затем использовать его как это:

union Bitfield 
{ 
    T bits; 
    typename F<T>::holder_type all; 
}; 
+0

Еще одно приятное решение. Но я предпочитаю решение Джона Кугельманса, его еще проще. – hirschhornsalz

+0

У John есть лучшее решение, если есть несколько классов BitfieldSmall *; мой лучше, если их много, но один или два выделяются и требуют, скажем, uint16_t по умолчанию uint8_t. – catwalk

+0

Существует 3 класса битовых полей, один из которых имеет размер в два байта (до сих пор ..). Поскольку объявление F :: holder_type и его специализация более 3 строк, решение Johns лучше в этом случае, хотя и с очень небольшим отрывом ;-) – hirschhornsalz

1

Возможно, вы захотите рассмотреть std::bitset или boost::dynamic_bitset вместо того, чтобы кататься самостоятельно. В любом случае избегать std::vector<bool>!

+0

Спасибо за предупреждение ;-) Массивы, которые я использую в этой части программы очень фиксированы по размеру (в отличие от базовых структур данных), поэтому я все равно избегаю std :: vector. И boost не упростит ситуацию, потому что мне нужно получить доступ к данным через SSE (__m128i и т. П.), Чтобы выполнить фактическую работу. – hirschhornsalz

0

Сделать число байтов, необходимо часть параметров шаблона:

template <typename T, int S=1> 
struct BitField 
{ 
    union 
    { 
     T bits; 
     unsigned char bytes[S]; 
    }; 
}; 

typedef Bitfield<BitfieldSmallBase, 1> BitfieldSmall; 
typedef Bitfield<BitfieldLargeBase, 2> BitfieldLarge; 
0

Как насчет этого?

#include <limits.h> 

template <class T> 
union BitField 
{ 
    T bits; 
    unsigned all : sizeof(T) * CHAR_BIT; 
}; 
Смежные вопросы