1

I am trying to get a custom bit field I tried this method:

struct foo
{   
    unsigned z : 10;
    unsigned y : 16;
    unsigned x : 1;
    unsigned w : 16;
};


int main()
{
    foo test({0x345, 0x1234, 0x1 ,0x1234});
    char bytes[8] = {0};

    std::cout << sizeof(test) << std::endl;

    memcpy(bytes, &test, 8);

    std::cout << sizeof(bool)  << std::endl;

    for (int i = 0; i < sizeof(bytes) / sizeof(char); i++)
    {
        std::cout << std::bitset<8>(bytes[sizeof(bytes) / sizeof(char) - i - 1]);
    }
    std::cout << "" << std::endl;

    return 0;
}

With the test I am trying it returns me:

0000000000000000000100100011010000000100010010001101001101000101

(00000000000000000 | 0010 010 0011 0100 | 000001 | 0001 0010 0011 0100 |11 0100 0101 should correspond to: 0x1234 |0x1 | 0x1234 | 0x345)

I am reading it from right to left, in the right side I have the 10 first bits ( 11 0100 0101), then I have next 16 bits (0001 0010 0011 0100). After that field I am expecting just one bit for the next data, but I have 6 bits (000001) instead of (1) before the last 16 bits (0001 0010 0011 0100).

Do you have any insight for this please ?

yat
  • 35
  • 5
  • 1
    Compilers are free to order, pad, and align bitfields however they like. In this case it seems that you compiler decided to add 5 bits of padding to `x` so that the overall structure would be 32-bit aligned. – Brian61354270 Dec 07 '21 at 19:59
  • How can I solve that ? It's very odd situation, specially since I want to have a certain definition in my bits because I am willing to use it to define a hardware message. – yat Dec 07 '21 at 20:04
  • 2
    Little Endian might also skew the "expected" view of the bytes. But what problem are you trying to solve? If you are trying to guarantee a specific bit order (say for a network protocol or bus communication), **write your own serialization (bit packing) code.** – selbie Dec 07 '21 at 20:08
  • @selbie I am trying to define a code to setup a CAN bus message, but I have fields of `1` bit to define in the whole message. What do you mean by "serialization code" please ? – yat Dec 07 '21 at 20:10
  • 1
    You literally use the bit manipulation operators like `<<`, `>>`, `|` or `&` to pack a binary message into a byte array instead of relying on the compiler to do the work for you. – selbie Dec 07 '21 at 20:11
  • @yacth Bitfields are the wrong tool for the job then. You can't enforce any particular layout in a portable way. See also [C/C++: Force Bit Field Order and Alignment](https://stackoverflow.com/q/1490092/11082165) – Brian61354270 Dec 07 '21 at 20:12
  • @selbi Since my whole message is 128 bits, I couldn't use any `type` to store my data in a convenient way, that is why I wanted to break it in bytes using the process I did. – yat Dec 07 '21 at 20:16
  • @Brian Ok this means using a struct with bitpacking is a bad idea for that manner ? Thing is my CAN message has a size of 128 bits, and I don't have any type in which I can store my message. – yat Dec 07 '21 at 20:24
  • 1
    `unsigned char buffer[16]` is 128 bits. – sweenish Dec 07 '21 at 20:32
  • Or `std::array` if you're using a modern compiler and want something better behaved. – Brian61354270 Dec 07 '21 at 20:33
  • @sweenish of course, but how can I fill it with bit manipulation ? – yat Dec 07 '21 at 20:35
  • @yacth That depends on how you're reading the CAN message in the first place. For general bit manipulation techniques, see [How do you set, clear, and toggle a single bit?](https://stackoverflow.com/q/47981/11082165) – Brian61354270 Dec 07 '21 at 20:35
  • I'm less familiar with `std::bitset`, would it fit the bill in this scenario? – sweenish Dec 07 '21 at 20:40
  • @Brian Actually I didn't work on the reading side yet, but I thought about different way creating my message either having a data that can store 128 bit and filling the whole data bits by bits depending on the fields. Or having an array of 128 booleans, but here I will have a size of 128 bytes which is heavy for an embedded code and the CRC calculation will be quite expensive. (@Brian I have worked with bit manipulation for data of 32bits using `unsigned` but with an `unsigned char buffer[16]`, I don't see how I can use `>>`, `<<`, `|` and `&`) – yat Dec 07 '21 at 20:41
  • @sweenish `std::bitset` would work here if one's okay with the performance hit associated with reading and writing each bit individually. Re: using bitwise operations on an array @yacth, you just need to apply the required bitwise operations to each entry manually, keeping in mind the endianess of your system. For example, assuming little-endian representation, setting the lower-most 10 bits would involve setting the 8 bits in `buffer[0]` and the lower 2 bits in `buffer[1]`, using the exact same bitwise operations as you were using with the `unsigned int`s before. – Brian61354270 Dec 07 '21 at 20:56
  • @sweenish @Brian actually I have a restriction on the librairies because of certification thus I cannot use `bitset` . But I will give at shot using an array of bytes but this means that I’ll have to hard code each field. (That is why is used the memcopy technique in my example to not have the hard coding part). – yat Dec 07 '21 at 21:40
  • @yacth using structs for serialization if a perfectly fine solution. Though with its limitations – Sergey Kolesnik Dec 08 '21 at 00:30
  • Just create a class wrapping the byte array and bitwise operations on it to make things easier – phuclv Dec 08 '21 at 00:45

1 Answers1

1

You have 5 spare bits, because the next bitfield occupies too much space to fit inside the remaining space (unsigned is 8 bits)

#include <cstdint> // types with fixed bit sizes

// force to remove padding
#pragma pack(push, 1) 

struct foo
{   
    // make bitsets occupy one address space
    uint32_t z : 10;
    uint32_t y : 16;
    uint32_t x : 1;
    // until now you have 27 bits, another 16 will not fit, 
// thus adding another 5 bits for padding. Nothing you can do.
    uint32_t w : 16; // or you can have uint16_t
    // 
};

#pragma pack(pop)

Also, bitsets can't share address space of different types of neighboring members.

Sergey Kolesnik
  • 3,009
  • 1
  • 8
  • 28
  • But there is no way to make that `w` "breaks" automatically in `5` bits to complete the first part from 27 to 32 instead of having 5 bits of padding and then the `11` following bits of `w` complete the rest ? I could do a hard coding with a union or another struct but I don't think it will be a clean code. – yat Dec 08 '21 at 13:50
  • @yacth you can fit several bitsets within one byte (for padding == 1 byte). But you can't fit one bitset (smaller than byte) across two bytes. Can you add a message scheme (lsb to msb) to your question? Also, you can add empty bits explicitly within one byte via `uint32_t : 2` - this will add 2 bits between your bitsets. – Sergey Kolesnik Dec 08 '21 at 14:08
  • I want the following scheme (MSB on left side, LSB on right side): `W= 16 bits | X = 1 bit | Y = 16 bits | Z = 10 bits` If I understood what you told me, the compiler groups the `bitset` by 32 bits and since X,Y and Z adds up to `27 bits` then it completes to `32 bits` by adding `5 bits` and then writes W. I wanted to know if I can write create a structure `struct sW{uint8_t firstFiveBits : 5; uint16_t remainingElevelBits : 11;}` and declare W as `sW` would that solve my issue here ? – yat Dec 08 '21 at 14:28
  • Actually this is an example that I was working on to know better about `bitsets` but my real data is devided by 1 bit, 32 bits, 6 bits, 64 bits, 16 bits, 2 bits and 7 bits (MSB → LSB), which will be even more trickier with these padding issues. – yat Dec 08 '21 at 14:31
  • @yacth yes, such struct would work since the sum of 16 bits fits. Don't forget to pack them – Sergey Kolesnik Dec 08 '21 at 14:33
  • actually using the struct as I explained earlier still adds padding before `W` but also after the 5 first bits of `W` which is worse in this case. I have no clue about how to add directly `W` right after `X` without having that 5 bits padding. – yat Dec 08 '21 at 15:06
  • 1
    Ok strangely if I define every `bitfield` as `uint64_t` it won't do a padding. – yat Dec 08 '21 at 15:20