Search code examples
c++dynamic-memory-allocationunions

Dynamically allocate memory to arrays in a union


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

Solution

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