C++ data array initialization

95 Views Asked by At

I need to initialize an uint8_t array, most of it is constant (fixed identifiers), some elements encode length of next section, and some elements are variable.

uint8_t data[] = {
    0x01, 0x05, 0x06, SomeVariable, 0x13,0x09, OtherVariable 
  };

I would like to compose it from named components, something like this

uint8_t data[] = MyData(DataHeader(), DataValue(SomeVariable), OtherValue(OtherVariable))

MyData would concatenate any number of parameters and produce

{
  0x01, length of all parameters, 
  data from first parameter..., 
  data from second parameter..., 
  ...
}

DataValue would produce {0x06, SomeVariable}, OtherValue would produce {0x13, 0x09, OtherVariable}, etc

How to do it in c++, without dynamic allocation (the total size of resulting array is known at compile time) ?

I tried using template with constexpr and parameter packs, but failed as the values in the array are not constexprs. However the sizes of the arrays are constexprs, so I hope it can be done.

1

There are 1 best solutions below

0
lematthias On

While it is not clear to me what benefit you would have with what you are describing there are a couple of things to address. Let's start with the syntax:

uint8_t data[] = MyData()

This is only possible if MyData() returns an initializer-list. This however is not possible because initializer-lists do not extend the lifetime of the underlying array (reference). If you return std::initializer_list<uint8_t>{1,2,3} then it will be destroyed before the return statement completes.

You could however let MyData allocate memory and return the address:

uint8_t* MyData() { uint8_t data = new uint8_t[16]; }
uint8_t* data = MyData();

But then you need to destroy the data. An alternative would be to pass the pointer to MyData:

void MyData(uint8_t* ptr) { ptr[0] = 0x13; }
uint8_t data[16];
MyData(data);

Now with that out of the way you may write MyData to take variadic arguments:

void MyData(uint8_t* ptr, std::initializer_list<uint8_t> list){
    size_t i = 0;
    for (auto element : list)
        ptr[i++] = element;
}
uint8_t data[16];
MyData(data, {0x01, 0x02});

As mentioned you cannot have functions return initalizer_lists. You may play around with this but in the end it probably won't make it any prettier.

The way you describe it, it sounds like you could use the preprocessor to do what you want. Write macros to fill the initializer-list for you.