Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;
}
like image 293
BasilFX Avatar asked Nov 03 '25 07:11

BasilFX


2 Answers

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.

like image 178
nneonneo Avatar answered Nov 06 '25 01:11

nneonneo


Here's one possible solution. First remove the braces from the macro

#define LIBRARY_DEFAULTS .a=1, .b=2, .c=3

Then for variables where the defaults are ok, you enclose the macro in braces

struct something standard = { LIBRARY_DEFAULTS };

For a variable where the defaults need tweaking, add some additional initializers

struct something tweaked = { LIBRARY_DEFAULTS, .a=100 };

Why does this work? Section 6.7.9 of the C specification discusses the use of designators in initialization lists and has this to say about specifying the same designator more than once:

19 The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject;151) all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.

and note 151 says this

151) Any initializer for the subobject which is overridden and so not used to initialize that subobject might not be evaluated at all.

Which is to say that the compiler is required to use the last designated initializer, and may implement this solution without any overhead.

like image 27
user3386109 Avatar answered Nov 06 '25 01:11

user3386109