Keith M Keith M - 3 months ago 20
C++ Question

Packing bools with bit field (C++)

I'm trying to interface with Ada code using C++, so I'm defining a struct using bit fields, so that all the data is in the same place in both languages. The following is not precisely what I'm doing, but outlines the problem. The following is also a console application in VS2008, but that's not super relevant.

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;
}


(If it wasn't clear, in the real program, the basic idea is that the program gets a
null*
from something communicating with the Ada code and casts it to a
testStruct*
to access the data, I think.)

With
#pragma pack(1)
commented out, the output is:

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


Obviously 4 words (indexed 0-3) should be 4*4*8 = 32*4 = 128 bits, not 224. The other output lines were to help confirm the size of types under the VS2008 compiler.

With
#pragma pack(1)
uncommented, that number (on the last two lines of output) is reduced to
176
, which is still greater than 128. It seems that the bools aren't being packed together with the unsigned ints in "Word 0".

Note: a&b, c, d, e, f, packaged in different words would be 5, +2 for the array = 7 words, times 32 bits =
224
, the number we get with
#pragma pack(1)
commented out. If c and e (the bools) instead take up 8 bits each, as opposed to 32, we get
176
, which is the number we get with
#pragma pack(1)
uncommented. It seems
#pragma pack(1)
is only allowing the bools to be packed into single bytes by themselves, instead of words, but not the bools with the unsigned ints at all.

So my question, in one sentence: Is there a way to force the compiler to pack a through e into one word? Related is this question: C++ bitfield packing with bools , but that doesn't answer my question; it only points out the behavior I'm trying to force to go away.

If there is literally no way to do this, does anyone have any ideas for workarounds? I'm at a loss, because:


  1. I was asked to avoid changing the struct format that I'm copying (no re-ordering).

  2. I don't want to change the bools to unsigned ints because it may cause problems down the road with constantly having to re-cast it to bool and maybe accidentally using the wrong version of an overloaded function, not to mention making the code more obscure for others who read it later.

  3. I don't want to declare them as private unsigned ints then make public accessors or something because all other members of all other structs in the project are accessed directly without
    ()
    afterward, so it would seem a bit hacky and obtuse, and one would almost NEED the IntelliSense or trial-and-error to remember which needs
    ()
    and which doesn't.

  4. I would like to avoid creating another struct type just for the data conversion (and e.g. make a constructor for
    testStruct
    that takes in a single
    testStructImport
    -type object) because the actual struct is very long with lots of bit-field-specified variables.


Answer

There is no easy, elegant method without using accessors or an interface layer. Unfortunately, there is nothing like a #pragma thing to fix this. I ended up just converting the bools to unsigned int and renaming variables from e.g. f to f_flag or f_bool to encourage correct usage and make it clear what the variables contained. It's lower-effort than Thomas's solution, but not as robust, obviously, and still gets around some of the main drawbacks with any of the easier methods.