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;
}
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With