Получение размера отдельного поля из поля структуры c ++

Краткая версия: как узнать размер (в битах) отдельного поля поля C ++?

Чтобы уточнить, пример поля, о котором я говорю:

struct Test {
    unsigned field1 : 4;  // takes up 4 bits
    unsigned field2 : 8;  // 8 bits
    unsigned field3 : 1;  // 1 bit
    unsigned field4 : 3;  // 3 bits
    unsigned field5 : 16; // 16 more to make it a 32 bit struct

    int normal_member; // normal struct variable member, 4 bytes on my system
};

Test t;
t.field1 = 1;
t.field2 = 5;
// etc.

Получить размер всего объекта Test легко, мы просто скажем

sizeof(Test); // returns 8, for 8 bytes total size

Мы можем получить нормальный член структуры через

sizeof(((Test*)0)->normal_member); // returns 4 (on my system)

Я хотел бы знать, как получить размер отдельного поля, скажем Test :: field4. Приведенный выше пример для обычного члена структуры не работает. Любые идеи? Или кто-то знает причину, по которой не работает? Я совершенно убежден, что sizeof не поможет, поскольку он возвращает только размер в байтах, но если кто-то знает иначе, я весь уши.

Спасибо!


person Jeffrey Martinez    schedule 11.02.2009    source источник
comment
Я думаю, что тебя могут обмануть, но я бы хотел, чтобы меня доказали, что я ошибаюсь.   -  person Ryan Graham    schedule 12.02.2009
comment
Я хотел бы воспользоваться этой возможностью, чтобы продолжить свой крестовый поход против битовых полей - просто не используйте их: stackoverflow.com/questions/289900/   -  person Michael Burr    schedule 12.02.2009


Ответы (6)


Вы можете рассчитать размер во время выполнения, fwiw, например:

//instantiate
Test t;
//fill all bits in the field
t.field1 = ~0;
//extract to unsigned integer
unsigned int i = t.field1;
... TODO use contents of i to calculate the bit-width of the field ...
person ChrisW    schedule 11.02.2009
comment
В этом нет особого смысла, почему бы просто не использовать другие уже упомянутые предложения времени компиляции? - person Matt Davison; 12.02.2009
comment
Другие предложения требуют изменения исходного кода битового поля, но этого не происходит. - person Max Lybbert; 12.02.2009
comment
Ваша идея очень хороша, ChrisW. Я ответил примером того, как это использовать, сославшись на ваш ответ. - person strager; 12.02.2009

Вы не можете взять sizeof битовое поле и получить количество битов.

Лучше всего использовать #defines или enums:

struct Test {
    enum Sizes {
        sizeof_field1 = 4,
        sizeof_field2 = 8,
        sizeof_field3 = 1,
        sizeof_field4 = 3,
        sizeof_field5 = 16,
    };

    unsigned field1 : sizeof_field1;  // takes up 4 bits
    unsigned field2 : sizeof_field2;  // 8 bits
    unsigned field3 : sizeof_field3;  // 1 bit
    unsigned field4 : sizeof_field4;  // 3 bits
    unsigned field5 : sizeof_field5;  // 16 more to make it a 32 bit struct

    int normal_member; // normal struct variable member, 4 bytes on my system
};

printf("%d\n", Test::sizeof_field1); // prints 4

Я считаю, что для единообразия вы можете переместить normal_member наверх и добавить запись в Sizes, используя sizeof(normal_member). Однако это нарушает порядок ваших данных.

person strager    schedule 11.02.2009

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

http://en.wikipedia.org/wiki/Sizeof

опираясь на ответ подсчета бит, вы можете использовать.

http://www-graphics.stanford.edu/~seander/bithacks.html < / а>

person sfossen    schedule 11.02.2009

Использование идеи ChrisW (кстати, неплохо), вы можете создать вспомогательный макрос:

#define SIZEOF_BITFIELD(class,member,out) { \
    class tmp_;                             \
    tmp_.member = ~0;                       \
    unsigned int tmp2_ = tmp_.member;       \
    ++tmp2_;                                \
    out = log2(tmp2_);                      \
}

unsigned int log2(unsigned int x) {
    // Overflow occured.
    if(!x) {
        return sizeof(unsigned int) * CHAR_BIT;
    }

    // Some bit twiddling...  Exploiting the fact that floats use base 2 and store the exponent.  Assumes 32-bit IEEE.
    float f = (float)x;
    return (*(unsigned int *)&f >> 23) - 0x7f;
}

Использование:

size_t size;
SIZEOF_BITFIELD(Test, field1, size);  // Class of the field, field itself, output variable.

printf("%d\n", size);  // Prints 4.

Мои попытки использовать шаблонные функции не увенчались успехом. Однако я не эксперт по шаблонам, поэтому может по-прежнему иметь чистый метод (например, sizeof_bitfield(Test::field1)).

person strager    schedule 12.02.2009
comment
О наличии чистого метода и с учетом существования: boost.org/doc/libs/1_46_1/libs/mpl/doc/refmanual/ можно сгенерировать (с помощью макроса) шаблон по имени поля. Преимущество может заключаться в том, что вычисление должно происходить во время компиляции: поэтому можно написать нормальное рекурсивное определение log2 (следуя известной функции факториала времени компиляции) и оптимизировать его. - person Blaisorblade; 27.03.2011

Я не думаю, что ты сможешь это сделать. Если вам действительно нужен размер, я предлагаю вам использовать #define (или, еще лучше, если возможно, переменную const - я не уверен, законно ли это), вот так:

#define TEST_FIELD1_SIZE 4
struct Test {
    unsigned field1 : TEST_FIELD1_SIZE;
    ...
}
person Adam Rosenfield    schedule 11.02.2009
comment
const допустима до тех пор, пока это целочисленный тип (в конце концов, не может зарезервировать половину бита). Это лучшее, что я могу сделать. Спасибо. - person Jeffrey Martinez; 12.02.2009

Это невозможно

Ответ на комментарий: поскольку тип - это просто int, типа bit не существует. Синтаксис назначения битового поля - это всего лишь сокращение для выполнения побитового кода для чтения и записи.

person Matt Davison    schedule 11.02.2009
comment
У вас есть документация, на которую можно указать? Или хотя бы ваши рассуждения? Мне просто любопытно на данный момент. - person Jeffrey Martinez; 12.02.2009