Combine two designated initializers via a macro - c

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.

Related

Use of macros in array definition in C

I am new to C and using it to program a Nordic nrf52 chip. I believe my problem is a general C one though rather than application.
I am setting up an array of structs using macros predefined in the chip SDK. Using those macros in the array initialisation works, but doing element by element does not.
So, the following works:
nrf_twi_mngr_transfer_t transfers_1[2] = { \
NRF_TWI_MNGR_WRITE(MSBARO5X_0_ADDR , &reg_addr[1], 1, NRF_TWI_MNGR_NO_STOP), \
NRF_TWI_MNGR_READ (MSBARO5X_0_ADDR , &p_buffer[0], sizeof(p_buffer), 0)
};
Where:
typedef struct {
uint8_t * p_data; ///< Pointer to the buffer holding the data.
uint8_t length; ///< Number of bytes to transfer.
uint8_t operation; ///< Device address combined with transfer direction.
uint8_t flags; ///< Transfer flags (see #ref NRF_TWI_MNGR_NO_STOP).
} nrf_twi_mngr_transfer_t;
NRF_TWI_WRITE and _READ are macros that use further macros, for example:
#define NRF_TWI_MNGR_WRITE(address, p_data, length, flags) \
NRF_TWI_MNGR_TRANSFER(NRF_TWI_MNGR_WRITE_OP(address), p_data, length, flags)
which uses
#define NRF_TWI_MNGR_WRITE_OP(address) (((address) << 1) | 0)
and
#define NRF_TWI_MNGR_TRANSFER(_operation, _p_data, _length, _flags) \
{ \
.p_data = (uint8_t *)(_p_data), \
.length = _length, \
.operation = _operation, \
.flags = _flags \
}
What I want to do is change individual items in this array, for example:
transfers_1[0] = NRF_TWI_MNGR_WRITE(MSBARO5X_0_ADDR , &reg_addr[1], 1, NRF_TWI_MNGR_NO_STOP);
However when I do that, I get the error "expected an expression".
MSBARO5X_0_ADDR is also defined in a define statement:
#define MSBARO5X_0_ADDR 0x76
If I replace this in any of the above code with a variable, I get the same "expected an expression" error. I suspect the two problems I have are due to the same lack of understanding on my part. SO forgive me for combining the two in a single post.
So the questions are:
-Why am I getting this error?
-Is it possible to change individual items in my array, and if so how?
-Is it possible to use a variable in place of the MSBARO5X_ADDR, and if so how?
Many thanks!
Ultimately, the macro expands into a brace enclosed initializer. Such a thing is not an expression, so it cannot be used as the right hand side of plain assignment (assignment and initialization are different things). It will work as part of a larger initializer, but not the way you try to use it unmodified.
But all is not lost. The syntax of the initializer implies c99 support. So we can use a trick. Structure objects can be assigned to eachother. So we need only obtain an object from somewhere. We can use a compound literal in order to create said object:
transfers_1[0] = (nrf_twi_mngr_transfer_t)NRF_TWI_MNGR_WRITE(/*Your arguments*/);
If you define the value of a structure the moment you declare it, the compiler will infer the type of the structure from the declaration. So this here will compile:
struct coordinates {
int x;
int y;
};
struct coordinates origin = { 10, 20 }; // This is OK
But if you assign a value to a previously declared variable, the compiler cannot infer its type. This code won't compile:
struct coordinates origin;
origin = { 10, 20 }; // ERROR! The type of the rvalue is unknown!
The type is unknown, because two structures are not equivalent in C just because they have the same members. E.g. this is legal in C:
struct coordinates {
int x;
int y;
};
struct dayOfYear {
int day;
int month;
};
Now what would { 5, 8 } be? The coordinates (5/8) or the 5th of August? It could be both. All that he compiler knows is that it is a struct of type { int, int }. Yet this does not define a type in C. The following is possible in some languages but it's not possible in C:
struct dayOfYear date = { 2, 3 };
struct coordinates cords = date; // ERROR!
Despite the fact that both structures are of type { int, int }, for the compiler struct dayOfYear and struct coordinates are two completely distinct and unrelated data types.
If you want to declare a hardcoded struct value, you need to tell the compiler what kind of struct that is:
struct coordinates origin;
origin = (struct coordinates){ 10, 20 }; // This is OK
Your NRF_TWI_MNGR_TRANSFER defines a hardcoded struct but only when you use that in a definition the compiler knows the type. If you try to use it as an assignment, you need to cast to the correct type.
transfers_1[0] = (nrf_twi_mngr_transfer_t)NRF_TWI_MNGR_WRITE(MSBARO5X_0_ADDR , &reg_addr[1], 1, NRF_TWI_MNGR_NO_STOP);
Which is not really a cast, even though it has the same syntax. In fact this is just telling the compiler how to interpret the following data.

Static structure initialization with a pointer to array of pointers member

I'm trying to initialize a structure member that needs to point to an array of pointers. The idea is to declare the array of structures statically to avoid initialization overhead since all the data is fixed and known at compile time.
Unfortunately, I cannot get the code to compile under Visual Studio 2015. The code listed below produces the following error: C2099: initializer is not a constant. Which seems odd because list is only initialized with a fixed sized list of string literals.
#define DATA_LIST { \
L"some", \
L"example", \
L"data", \
L"in a list", \
NULL \
}
#define INFO_LIST { \
L"another", \
L"list", \
L"with", \
L"some", \
L"info", \
NULL \
}
typedef struct data {
unsigned int flag;
const wchar_t **list;
} dataset, *pdataset;
static dataset somedata[] = {
{ .flag = 2,
.list = (const wchar_t *[])DATA_LIST // C2099
},
{ .flag = 4,
.list = (const wchar_t *[])INFO_LIST // C2099
}
};
I've also tried to use a pointer to a flexible array (const wchar_t *list[];). Not the ideal solution because somedata will no longer be able to be declared as an array of structures. Next to that, it will also produce a warning (C4200: nonstandard extension used: zero-sized array in struct/union).
typedef struct data {
unsigned int flag;
const wchar_t *list[]; // C4200 (somedata can no longer be an array of structures)
} dataset, *pdataset;
static dataset somedata = {
.flag = 2,
.list = DATA_LIST
};
Another idea was to define list as a pointer to a fixed size array of pointers. But this requires the dataset structure to be defined with a list member that is large enough to hold the largest list. Also not ideal when there are lots of small lists and one single large list.
typedef struct data {
unsigned int flag;
const wchar_t *list[sizeof (wchar_t *[])INFO_LIST / sizeof *(wchar_t *[])INFO_LIST];
} dataset, *pdataset;
static dataset somedata[] = {
{ .flag = 2,
.list = DATA_LIST
},
{ .flag = 4,
.list = INFO_LIST
}
};
Maybe I'm overseeing something or is there some language extension feature available that can provide an elegant solution? Any suggestions are welcome.
Note: even though the visual-c++ tag is added, the code is compiled as C code.
Another interesting thing to add which might be related, is that when somedata is declared as non-static (thus without the static keyword), the compiler will produce some warnings but is able to compile the code. By declaring somedata as non-static, the constraint is removed that forces the data used for initializing somedata to be known at compile time.
As indicated by the compilation warnings, it seems that the compiler temporarily stores the address of the list of string literals in an automatic variable before initializing the list member with it. This remains speculation, though. Maybe someone experienced can shed some light on what is actually happening here.
typedef struct data {
unsigned int flag;
const wchar_t **list;
} dataset, *pdataset;
// C4221: nonstandard extension used: 'list': cannot be initialized using
// address of automatic variable '$S1'
// C4204: nonstandard extension used: non-constant aggregate initializer
dataset somedata = {
.flag = 2,
.list = (const wchar_t *[])DATA_LIST // note: see declaration of '$S1'
};
Last but not least, when using a temporary variable initialized with the address of the list of string literals to initialize the list member, the code finally compiles fine without any warnings or errors.
static const wchar_t *temp[] = DATA_LIST;
static dataset somedata = {
.flag = 2,
.list = temp
};
But when declaring temp as a pointer to a pointer and typecasting the list of string literals, the code can no longer be compiled as the expression that initializes list becomes marked as an active error: expression must have a constant value
static const wchar_t **temp = (const wchar_t *[])DATA_LIST;
static dataset somedata = {
.flag = 2,
.list = temp // marked as active error
};
If I then decide to make somedata non-static again, the expression is no longer marked as an active error. But when trying to compile the code, the following error comes back up again: C2099: initializer is not a constant
I'm wondering if Visual Studio 2017 behaves the same way and if there is an alternative method available to organize and process the data by similar means.
MSVC has poor compliance to the C Standard. As a workaround you can use named objects instead of compound literals:
static const wchar_t *x_data_list[] = DATA_LIST;
static const wchar_t *x_info_list[] = INFO_LIST;
static dataset somedata[] = {
{ .flag = 2,
.list = x_data_list
},
{ .flag = 4,
.list = x_info_list
}
};
I'm not sure whether you intentionally made your lists non-const, but if you are not planning to write to x_data_list at runtime then you can make it const and give the .list member the type const wchar_t * const *.

Init a struct in C

I have a question related to struct initialization in C.
I have a struct:
struct TestStruct
{
u8 status;
u8 flag1;
u8 flag2;
};
I want a generic function/macro to initialize this struct and set the value of one parameter, eg status =1, easy way is:
TestStruct t = {};
t.status = 1;
However, by doing this I have set the value of status twice, first to 0 in init function and second set it to 1 (optimization does not help?).
(Please dont tell me t = {1,0,0} Im looking for a generic way)
I am thinking about a macro in init function, something like:
#define INIT_TESTSTRUCT (param, value) \
{ .status=0, .flag1=0, .flag2=0, .param=value }
TestStruct t = INIT_TESTSTRUCT(status, 0);
However, the compiler gives error "initialized field overwritten", because I have set the value of status twice.
Please help to point out how to alter the macro to achieve what I want, many thanks.
#define INIT_TESTSTRUCT(param, value) \
{ .param=(value) }
TestStruct t = INIT_TESTSTRUCT(status, 0);
should do it. The variable is then added in .data segment - as it is an initialized one - and all fields which are not mentionned explicitly are set to 0 by the compiler (instead of the linker or the loader).
You have a space in the wrong place:
#define INIT_TESTSTRUCT(param, value) \
{ .status=0, .flag1=0, .flag2=0, .param=(value) }
should do it.
The ( of the macro definition must come immediately after the macro name. Otherwise the parser takes this as a macro with no arguments and expands it to (param, value) ...etc... and in your case this then obviously is a syntax error.
Also note that it is usually a good idea to put () around parameters in the replacment text, to avoid syntax confusion.
Well, firstly
TestStruct t = {};
is illegal in C. If it compiled in your case, it must be a non-standard compiler extension. You can achieve the same effect by
TestStruct t = { 0 };
which is legal in C. It sets all fields in the entire struct to zeros.
Secondly, even with designated initializers C language follows the traditional "all-or-nothing" approach to initialization: if you initialize just one field of the aggregate, all other fields are implicitly initialized to zero.
Which means that in your case all you have to do is
TestStruct t = { .status = 1 };
to initialize the entire struct. status field will be set to 1, while all other fields will be set to zero.
So, your macro can be implemented as
#define INIT_TESTSTRUCT(param, value) { .param = value }
TestStruct t = INIT_TESTSTRUCT(status, 1);
There's no need to explicitly set all other fields to zero - it will happen by itself.

Strange structure definition

In code example of Intel DPDK i have found this strange syntactical construction. Can anybody explain me what does it mean?
static const struct rte_eth_conf port_conf = {
.rxmode = {
.split_hdr_size = 0,
.header_split = 0,
.hw_ip_checksum = 0,
.hw_vlan_filter = 0,
.jumbo_frame = 0,
.hw_strip_crc = 0,
},
.txmode = {
}
};
It's a C99 syntax known as a designated initializer.
In earlier C standards, the elements of a struct initializer had to appear in the same order as in the struct definition. With designated initializers, that restriction is lifted. Naturally the struct members have to be named to indicate which member is being initialized.
Designated initializers can also be used with arrays and allow you to initialize specific elements of an array. For example:
int a[6] = { [4] = 29, [2] = 15 };
If you have
struct X
{
type_a var_a;
type_b var_b;
type_c var_c;
type_d var_d;
};
you can initialize an object like this:
struct X x = {value_a, value_b, value_c, value_d};
But this means you need to know the order of variables in X as well as having an initial value for all of it. Alternatively, you can initialize like this:
struct X x = {
.var_a = value_a,
.var_b = value_b,
.var_c = value_c,
.var_d = value_d
};
This way, you can initialize member variables in any order, or even skip some.
This is specially useful in a library where you have some variables needed to be initialized by the user, while other variables are more internal and could even be changed with different versions of your library. Using this kind of initialization, the user doesn't need to know about those extra variables.
This is C99 feature called designated initializers. It lets you specify the names of the fields to which you set values, rather than specifying the values in the order the corresponding fields appear in the declaration. Additionally, this syntax lets you initialize members of unions other than the first one - something that was impossible before C99.

Statically initialized struct self-reference

I have a simple structure that is defined like so:
typedef struct {
int index;
double* arrayToRead;
} my_struct;
I want to initialize an array of structures so they become something like this:
double decimals[2] = {1.0, 2.0};
my_struct[2] = {
{0, &decimals[0]},
{1, &decimals[1]}
};
I have to initialize this array of structs statically.
Can I initialize it statically while referring to a previously defined member, like so:
my_struct[2] = {
{0, &decimals[index]},
{1, &decimals[index]}
};
Where "index" refers to the value for index defined to the left of "decimals"?
If you have a modern C compiler (aka C99) you can use P99 for code unrolling. Something like
#define INIT_ELEMENT(NAME, X, I) [I] = { \
.index = I, \
.arraytoread = &decimals[I] \
}
#define INIT_ELEMENTS(N) P99_FOR(, N, P00_SEQ, INIT_ELEMENT, P99_DUPL(N,))
and then
my_struct A[] = { INIT_ELEMENTS(2) };
should expand to an initializer of your liking.
(Here the first macro defines the code that is to be repeated, using only I from its arguments, the index of the invocation. In the second macro P00_SEQ means that this is separated by comma.)
No.*
If you're desperate, you could always devise a macro for this purpose:
#define DEFINE_ELEMENT(i) {(i), &decimals[(i)]}
my_struct m[2] = {
DEFINE_ELEMENT(0),
DEFINE_ELEMENT(1)
};
#undef DEFINE_ELEMENT
* Or "yes", as #Jens points out in his answer, for C99.
What's that in the sky? It's a bird! No, it's a plane! No, it's Code-Generation Man!
Or--with tongue removed from cheek--I don't know that there is a built in feature that supports this, but you can always use an external tool to write the initialization code for you.
Basically you write a script in some convenient language that output a c code fragment defining the structure and declaring the variable with the long-hand intialization, then use the c-preprocessor's #include feature to drag that generated file into you code.
You'll want to make the build manager aware of this need. In make it would look something like
cfile.o: cfile.c mystruct.c
my_struct.c:
awk -f generate_my_struct.awk > my_struct.c
i think that you need to give a name for your new array
my_struct my_array[2] = {
{0, &decimals[my_array[0].index]},
{1, &decimals[my_array[1].index]}
};

Resources