Search code examples
cembeddedc99literalscompound-literals

strategy to declare complex C structured const data?


I have a complex data structure (with lots of incomplete array types / heterogenous length arrays of structures and pointer to structures to arrays of structs ...)

I would like to put those in flash memory, so I thought about putting them in const objects of static storage (they would be stored on flash) and let the compiler do its job.

I'm working on embedded environment, where ROM==flash==data that i cannot physically change. I have few RAM and certainly not enough to store all my data. GCC can be told to put store static const data to ROM with no issues.

The data cannot be constructed dynamically, as it should stay in flash.

I'm currently using C (not C++), with a 4.8 gcc, and don't mind about using GCCisms.

However I keep encoutering error messages like :

  • initializer element is not constant
  • incompatible pointer types
  • ...

differently with different very recent gcc releases, suggesting this functionality (mixing compound literals, designated initializers , ...) is recent or not mainline.

Note that this code is generated by a program (script).

Besides the errors I keep making (I could be more specific and ask for help there), what strategy would you suggest :

  • keep trying using complex, nested literal structures using compound literals
    • having a massive type compound literal
    • having several compound literals types pointing to each other
  • building many intermediary objects with their names (rendering the whole thing quite unreadable)
  • building a big uint32_t datablob[] and casting my structures appropriately (with bonus unability to not be able to store pointers between objects as my linker will specify where this will end up)

  • any other options ?

(edit : added specifics)

Well, my question was more about a generic strategy, but here is an example :

struct A
{
    int id; 
    int codes[]; 
};

struct B 
{
    int b_member;
    struct A *a[]; // array of ptr to A objects
};


struct C 
{
    int c_member;
    struct B *objects[]; // array of ptrs on B
};

const struct A rom_data = { .id=4, .codes = {1,2,3,4}}; // this is in ROM

int main(void) {}

I would like to declare like I did for A an array of C structs. (That means I don't want to copy data, read it from disk or malloc it, just declare a const with the data in place.)

All examples I have about literals are very simple.

The specifics of my platform is an ARM microcontroller, but just consider I want to declare a const.


Solution

  • You should use const-qualified pointers instead of flexible array members.

    Have some example code:

    #include <stddef.h>
    
    struct A
    {
        int id; 
        const int *codes; 
    };
    
    struct B 
    {
        int b_member;
        const struct A *const *a;
    };
    
    struct C 
    {
        int c_member;
        const struct B *const *objects;
    };
    
    static const struct C ROOT = { 0, (const struct B *[]){
        &(const struct B){ 0, (const struct A *[]){
            &(const struct A){ 0, (const int []){ 1, 2, 3 } },
            &(const struct A){ 1, (const int []){ 0 } },
        } },
        &(const struct B){ 42, NULL },
    } };
    

    As mentioned in the comments, it seems to be unnecessary to reference the structures by pointers. This simplifies the code:

    #include <stddef.h>
    
    struct A
    {
        int id; 
        const int *codes; 
    };
    
    struct B 
    {
        int b_member;
        const struct A *a;
    };
    
    struct C 
    {
        int c_member;
        const struct B *objects;
    };
    
    static const struct C ROOT = { 0, (const struct B []){
        { 0, (const struct A []){
            { 0, (const int []){ 1, 2, 3 } },
            { 1, (const int []){ 0 } },
        } },
        { 42, NULL },
    } };
    

    If you want or need C90 compatibility, you could flatten your tree and have the generating script keep track of offsets within the corresponding arrays:

    static const int ARRAY_OF_INT[] = {
        1, 2, 3,
        0,
    };
    
    static const struct A ARRAY_OF_A[] = {
        { 0, ARRAY_OF_INT + 0 },
        { 1, ARRAY_OF_INT + 3 },
    };
    
    static const struct B ARRAY_OF_B[] = {
        { 0, ARRAY_OF_A + 0 },
        { 42, NULL },
    };
    
    static const struct C ROOT = { 0, ARRAY_OF_B + 0 };