Search code examples
cc99designated-initializer

Combine two designated initializers via a macro


In an embedded project, I use a library that provides a macro for initializing a structure. This provides reasonable defaults, but the defaults depend on other parameters. I want to override one or more values of this designated initializer, because initializing the values afterwards generates overhead.

Ideally, I don't want to copy-paste all of the macro's, because I then have to manage third-party code. If the library changes it's defaults, I don't want to do that too.

Is there a way of combining or overriding designated initializers, so there is no overhead? The code has to be C99 compliant and portable.

Some example code to demonstrate the issue:

#if SITUATION
#define LIBRARY_DEFAULTS \
{ \
  .field_a = 1, \
  .field_b = 2, \
  .field_c = 3 \
}
#else
#define LIBRARY_DEFAULTS \
{ \
  .field_a = 100, \
  .field_b = 200, \
  .field_c = 300, \
  .field_d = 400, \
  .field_e = 500 \
}
#endif

/* The following is what I want (or similar), but (of course) doesn't 
   work. */
// #define MY_DEFAULTS = LIBRARY_DEFAULTS + { .field_a = 100 }

int main(void) {
    /* The exact definition of something also depends on situation. */
    struct something library_thing = LIBRARY_DEFAULTS;

    /* This generates overhead, and I want to avoid this. It is certain
       that the field exists. */
    library_thing.field_a = 100;
}

Solution

  • You could wrap your library_thing in an outer structure, and do your overrides from the outer structure's initializer:

    #include <stdio.h>
    
    struct foo {
        int a,b,c;
    };
    
    #define FOO_DEFAULTS { .a = 1, .b = 2, .c = 3 }
    
    int main() {
        struct {
            struct foo x;
        } baz = {
            .x = FOO_DEFAULTS,
            .x.a = 4,
        };
    
        printf("%d\n", baz.x.a); // prints 4
    }
    

    In fact, you can even do

    .x = FOO_DEFAULTS,
    .x = {.a = 4},
    

    if you need to really "merge" two initializers.

    This compiles fine on Clang (7.0.2) but generates a warning under -Winitializer-overrides. Checking the generated code confirms that the structure is initialized with 4, 2, 3 so there is no additional overhead from this trick.