Упаковка bools с битовым полем (C ++)

Я пытаюсь взаимодействовать с кодом Ada с помощью C ++, поэтому я определяю структуру с использованием битовых полей, чтобы все данные на обоих языках находились в одном месте. Следующее - не совсем то, что я делаю, но обрисовывает проблему. Следующее также является консольным приложением в VS2008, но это не очень актуально.

using namespace System;
int main() {
    int array1[2] = {0, 0};
    int *array2 = new int[2]();
    array2[0] = 0;
    array2[1] = 0;

    #pragma pack(1)
    struct testStruct {
        // Word 0 (desired)
        unsigned a : 8;
        unsigned b : 1;
        bool c : 1;
        unsigned d : 21;
        bool e : 1;

        // Word 1 (desired)
        int f : 32;

        // Words 2-3 (desired)
        int g[2]; //Cannot assign bit field but takes 64 bits in my compiler
    };
    testStruct test;

    Console::WriteLine("size of char: {0:D}", sizeof(char) * 8);
    Console::WriteLine("size of short: {0:D}", sizeof(short) * 8);
    Console::WriteLine("size of int: {0:D}", sizeof(int) * 8);
    Console::WriteLine("size of unsigned: {0:D}", sizeof(unsigned) * 8);
    Console::WriteLine("size of long: {0:D}", sizeof(long) * 8);
    Console::WriteLine("size of long long: {0:D}", sizeof(long long) * 8);
    Console::WriteLine("size of bool: {0:D}", sizeof(bool) * 8);
    Console::WriteLine("size of int[2]: {0:D}", sizeof(array1) * 8);
    Console::WriteLine("size of int*: {0:D}", sizeof(array2) * 8);
    Console::WriteLine("size of testStruct: {0:D}", sizeof(testStruct) * 8);
    Console::WriteLine("size of test: {0:D}", sizeof(test) * 8);

    Console::ReadKey(true);

    delete[] array2;
    return 0;
}

(Если это было непонятно, в реальной программе основная идея состоит в том, что программа получает null* от чего-то, взаимодействующего с кодом Ada, и переводит его в testStruct* для доступа к данным, я думаю.)

Если #pragma pack(1) закомментировано, результат будет следующим:

size of char: 8
size of short: 16
size of int: 32
size of unsigned: 32
size of long: 32
size of long long: 64
size of bool: 8
size of int[2]: 64
size of int*: 32
size of testStruct: 224
size of test: 224

Очевидно, 4 слова (с индексами 0–3) должны быть 4 * 4 * 8 = 32 * 4 = 128 бит, а не 224. Остальные выходные строки должны были помочь подтвердить размер типов в компиляторе VS2008.

Если #pragma pack(1) раскомментировано, это число (в последних двух строках вывода) уменьшается до 176, которое по-прежнему больше 128. Похоже, что bools не упаковываются вместе с целыми числами без знака в "Word 0".

Примечание: a & b, c, d, e, f, упакованные в разные слова, будут равны 5, +2 для массива = 7 слов, умноженное на 32 бита = 224, число, которое мы получаем с закомментированным #pragma pack(1). Если c и e (bools) вместо 32 занимают по 8 бит, мы получим 176, то есть число, которое мы получаем с #pragma pack(1) без комментариев. Кажется, что #pragma pack(1) позволяет упаковывать только буллы в отдельные байты вместо слов, но не в буллы с целыми числами без знака вообще.

Итак, мой вопрос в одном предложении: есть ли способ заставить компилятор упаковать через e в одно слово? Связанный с этим вопрос: Упаковка битового поля C ++ с помощью bools, но это не отвечает на мои вопрос; это только указывает на поведение, которое я пытаюсь заставить уйти.

Если буквально нет способа сделать это, есть ли у кого-нибудь идеи обходных путей? Я в растерянности, потому что:

  1. Меня попросили не изменять формат структуры, который я копирую (без переупорядочивания).
  2. Я не хочу менять bools на неподписанные целые числа, потому что это может вызвать проблемы в будущем с постоянным повторным преобразованием его в bool и, возможно, случайным использованием неправильной версии перегруженной функции, не говоря уже о том, что код становится более неясным для тех, кто прочтет это позже.
  3. Я не хочу объявлять их как частные беззнаковые целые числа, а затем делать общедоступные средства доступа или что-то в этом роде, потому что все остальные члены всех других структур в проекте доступны напрямую без () впоследствии, поэтому это может показаться немного взломанным и тупым, и можно было бы почти НЕОБХОДИМО использовать IntelliSense или метод проб и ошибок, чтобы запомнить, что нужно (), а что нет.
  4. Я бы хотел избежать создания другого типа структуры только для преобразования данных (и, например, создания конструктора для testStruct, который принимает один объект типа testStructImport), потому что фактическая структура очень длинная с большим количеством переменных, указанных в битовых полях.

person Keith M    schedule 15.07.2014    source источник
comment
Может быть, вы могли бы изменить соответствующую запись Ada, чтобы она соответствовала этим выравниваниям?   -  person Holt    schedule 15.07.2014
comment
Сделайте все члены этого битового поля закрытыми и сделайте доступными для записи - в любом случае это деталь реализации.   -  person    schedule 15.07.2014
comment
Я посмотрю подробности, но все сводится к тому, что битовые поля разных типов не объединяются, и каждый новый тип битового поля выравнивается для базового типа. Вы захотите сохранить каждый из них как unsigned и при необходимости выполнять приведение.   -  person Mooing Duck    schedule 15.07.2014
comment
Кроме того, битовые поля находятся в порядке, определенном реализацией, поэтому в любом случае лучше делать это вручную.   -  person Mooing Duck    schedule 15.07.2014
comment
@Holt: Это то, что я имел в виду под №1 в списке. Меня попросили не изменять запись Ады. (Это также, вероятно, заняло бы несколько часов, даже если бы я это сделал. Как я уже сказал, очень долго и беспорядок, чтобы реорганизовать.)   -  person Keith M    schedule 15.07.2014
comment
@KeitM Хорошо, я думал, вы говорите о структуре C. Кстати, в окнах с g ++ v4.6.2 (MinGW) размер вашей структуры составляет 128 бит, даже без #pragma pack(1).   -  person Holt    schedule 15.07.2014
comment
В качестве обходного пути я бы попробовал маски и аксессуары.   -  person Logicrat    schedule 15.07.2014
comment
Дитер Люкинг: Это то, что я имел в виду под № 3. Все остальные структуры в (большом) проекте не используют методы доступа. @MooingDuck: Это то, что я имел в виду под №2. Потенциально это может привести к большому количеству приведений и вызвать проблемы с перегруженными функциями, если приведение будет забыто. Интересно, есть ли способ заставить это более прямо. Что касается порядка, что вы подразумеваете под «вручную»?   -  person Keith M    schedule 15.07.2014
comment
@Holt Ну, технически я говорил об обоих. Мне пришлось бы переупорядочить оба, чтобы местоположения данных совпадали.   -  person Keith M    schedule 15.07.2014
comment
Вы ориентируетесь на Microsoft C ++ или C ++ / CLI (который используется в вашем примере)? Поведение примерно такое же (упаковываются только битовые поля без знака), но при использовании C ++ / CLI вы действительно можете использовать свойства для инкапсуляции ваших распаковываемых bools (и imho это было бы лучше, чем аксессоры)   -  person Luis    schedule 16.07.2014
comment
@ Луис: Честно говоря, я понятия не имею, что использую. Я думаю, что, возможно, смешал и сопоставил, исходя из статьи в Википедии.   -  person Keith M    schedule 16.07.2014
comment
Visual Studio 2015 также воспроизводит проблему в дополнение к VS 2008, упомянутому в вопросе.   -  person Keith M    schedule 23.01.2019


Ответы (4)


Я рекомендую вам создать «нормальную» структуру без какой-либо битовой упаковки. Используйте для членов типы POD по умолчанию.

Создайте интерфейсные функции для загрузки «обычных» полей из буфера (uint8_t) и сохранения в буфер.

Это позволит вам использовать элементы данных разумным способом в вашей программе. Упаковкой и распаковкой битов занимается функция интерфейса. Вращение битов должно использовать поразрядное И и побитовое ИЛИ, а не полагаться на нотацию битового поля в структуре. Это позволит вам настроить битрейт и будет более переносимым среди компиляторов.

Вот как я разработал свои классы протокола. И мне не нужно беспокоиться о позиционировании битового поля, порядке байтов или тому подобном.

Также я могу использовать блочный ввод-вывод для чтения и записи буфера.

person Thomas Matthews    schedule 15.07.2014
comment
Спасибо, это еще один способ обхода. Кстати, uint8_t не существует в Visual Studio 2008 (моя IDE, как я уже упоминал). Кажется, это вещь C ++ 11. - person Keith M; 16.07.2014
comment
uint8_t является частью C99 и определен в stdint.h, вы можете либо обновить его до Visual Studio 2010, либо загрузить этот файл заголовка: см. этот вопрос. - person Étienne; 16.07.2014
comment
@ Étienne Да, я пытался включить его, но VS2008 не смог его найти. Обновление - не лучший вариант, поскольку мы коммерческие и не хотим, чтобы наши новые проекты были несовместимы со старыми машинами, поэтому спасибо за ссылку! - person Keith M; 16.07.2014
comment
Или вы можете использовать unsigned char с Visual Studio 2008. - person Thomas Matthews; 16.07.2014

Попробуйте упаковывать вещи следующим образом:

    #pragma pack( push, 1 )
struct testStruct {
    // Word 0 (desired)
    unsigned a : 8;
    unsigned b : 1;
    unsigned c : 1;
    unsigned d : 21;
    unsigned e : 1;

    // Word 1 (desired)
    unsigned f : 32;

    // Words 2-3 (desired)
    unsigned g[2]; //Cannot assign bit field but takes 64 bits in my compiler
};
#pragma pack(pop)
person lsalamon    schedule 15.07.2014
comment
О, вы это редактировали. Да, это работает, как и без изменения строк pragma. Я обратился к этому в № 2 в списке обходных путей, которые мне не нравились. - person Keith M; 16.07.2014

Нет простого и элегантного метода без использования средств доступа или уровня интерфейса. К сожалению, ничего похожего на #pragma вещь, чтобы исправить это. В итоге я просто преобразовал bools в unsigned int и переименовал переменные, например, из От f до f_flag или f_bool, чтобы стимулировать правильное использование и прояснить, что содержат переменные. Это менее трудоемкое решение, чем решение Томаса, но, очевидно, не такое надежное, и все же позволяет обойти некоторые из основных недостатков с помощью любого из более простых методов.

person Keith M    schedule 25.08.2016

Спустя годы после того, как я разместил этот вопрос, пользователь @WaltK добавил этот комментарий к связанному связанному вопросу:

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

person Community    schedule 23.01.2019
comment
Ненавижу давать ответ, который представляет собой почти просто ссылку на ответ, но это длинный PDF-файл, и я не могу скопировать его и вставить, поэтому я не думаю, что имеет смысл цитировать все это в ответ . Если ссылка когда-либо прерывается, попробуйте поискать битовые поля библиотеки C plus plus или пользователя github по адресу github.com/wkaras . - person Keith M; 23.01.2019