1

I'm using union to fill some message fields in a char type message buffer. If the length of the message is constant, it works correctly. See the simplified code sample below.

The problem is, my message can have variable length. Specifically, the const N will be decided on runtime. Is there a way to keep using unions by dynamically allocating memory for buf?

I'm exploring smart pointers but haven't had any luck so far.

const int N = 4;

struct repeating_group_t {
  uint8_t field1;
  uint8_t field2;
}rpt_group;

struct message_t
{
    union
    {
        char buf[2 + 2*N];
        struct {
          uint8_t header;
          uint8_t block_len;
          std::array<repeating_group_t, N> group;
        };
    };
};

int main()
{ 
    message_t msg;

    msg.header    = 0x32;
    msg.block_len = 8;

    for (auto i = 0; i < N; i++)
    {
      msg.group[i].field1 = i;
      msg.group[i].field2 = 10*i;
    } 

    // msg.buf is correctly filled

    return 0;
}
timrau
  • 22,578
  • 4
  • 51
  • 64
yabbasi
  • 43
  • 5
  • 1
    No. Do not use an union. Do not use `std::array`. No, unions do not work that way in C++. `msg.buf is correctly filled` No, using non-active member of an union `msg.buf` is undefined behavior. `msg.buf` is not filled as it is not active union member. Just use `std::vector` and `.push_back` your data... – KamilCuk Apr 09 '20 at 20:58
  • was going to write an answer, but it was basically just a "don't do it" and the details on that are already available here: https://stackoverflow.com/questions/25664848/unions-and-type-punning. The tl;dr of it is "don't do it" ;) – 463035818_is_not_an_ai Apr 09 '20 at 21:29
  • acutally I dont understand why you want to use a union here. You have bytes that you want to write into an array of bytes, why use a union for that? – 463035818_is_not_an_ai Apr 09 '20 at 21:32
  • @KamilCuk std::vector won't apply here because the values to be added into the buffer all have different data types; uint64_t, uint32_t,uint16_t, char. I posted an oversimplified example just to give an idea. – yabbasi Apr 10 '20 at 04:55
  • 1
    @idclev463035818 my comment above might answer your question too. I have to fill a bunch of uint64_t, uint32_t, uint16_t and char type fields in a char buffer and unions do this conversion on their own. – yabbasi Apr 10 '20 at 04:59
  • `buffer all have different data types` - so write a serialization function to put them in the endianess you desire. – KamilCuk Apr 10 '20 at 06:58

2 Answers2

2

As said in the comments, use std::vector.

int main() {
     // before C++17 use char
     std::vector<std::byte> v.
     v.push_back(0x32);
     v.push_back(8);
     for (auto i = 0; i < N; i++) {
         v.push_back(i);
         const uint16_t a = 10 * i;
         // store uint16_t in big endian
         v.push_back(a >> 16);
         v.push_back(a & 0xff);
     } 
}

For custom datatypes, you could provide your own stream-like or container-like container and overload operator>> or another custom function of your choice for your datatypes.

struct Message{
    std::vector<std::byte> v; 
    Message& push8(uint8_t t) { ... }
    // push 16 bits little endian
    Message& push16le(uint16_t t) { ... }
    // push 16 bits big endian
    Message& push16be(uint16_t t) { ... }
    // etc
    Message& push(const Repeating_group& t) {
       v.push_back(t.field1);
       v.push_back(t.field2);
       return v;
    }
    // etc.
};

int main(){ 
     Message v;
     v.push8(0x32).push8(8);
     for (...) {
         v.push(Repeating_group(i, i * 10));
     }
 }
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
0

You can't have N evaluated at runtime because both c-array (your buf) and std::array have size information in its type.

Also - using union for (de)serialization is not a good practice - size of your structure will depend on alignment needed on given machine it is compiled for and so on... You could add packed attribute to overcome it, but you still have plenty of platform dependency problems here.

Regarding variable length - you'd need to write custom (de)serializer that will understand and store/read that size information to recreate that container on the other end.

Where do you want to pass these messages?