After understanding that GCC supports Compound Literals, where an anonymous structure can be filled using a {...} initaliser.
Then consider that gcc accepts (with limitations) variable length structures if the last element is variable length item.
I would like to be able to use macros to fill out lots of tables where most of the data stays the same from compile time and only a few fields change.
My structure are complicated, so here is a simpler working example to start with as a demonstration of the how it is to be used.
#include <stdio.h>
typedef unsigned short int uint16_t;
typedef unsigned long size_t;
#define CONSTANT -20
// The data we are storing, we don't need to fill all fields every time
typedef struct dt {
uint16_t a;
const int b;
} data_t;
// An incomplete structure definiton that matches the general shape
typedef struct ct {
size_t size;
data_t data;
char name[];
} complex_t;
// A typedef to make the code look cleaner
typedef complex_t * complex_t_ptr;
// A macro to generate instances of objects
#define CREATE(X, Y) (complex_t_ptr)&((struct { \
size_t size; \
data_t data; \
char name[sizeof(X)]; \
} ) { \
.size = sizeof(X), \
.data = { .a = Y, .b = CONSTANT }, \
.name = X \
})
// Create an array number of structure instance and put pointers those objects into an array
// Note each object may be a different size.
complex_t_ptr data_table[] = {
CREATE("DATA1", 1),
CREATE("DATA2_LONGER", 2),
CREATE("D3S", 3),
};
static size_t DATA_TABLE_LEN = sizeof(data_table) / sizeof(typeof(0[data_table]));
int main(int argc, char **argv)
{
for(uint16_t idx=0; idx<DATA_TABLE_LEN; idx++)
{
complex_t_ptr p = data_table[idx];
printf("%15s = (%3u, %3d) and is %3lu long\n", p->name, p->data.a, p->data.b, p->size);
}
return 0;
}
$ gcc test_macro.c -o test_macro
$ ./test_macro
DATA1 = ( 1, -20) and is 6 long
DATA2_LONGER = ( 2, -20) and is 13 long
D3S = ( 3, -20) and is 4 long
So far so good...
Now, what if we want to create a more complicated object?
//... skipping the rest as hopefully you have the idea by now
// A more complicated data structure
typedef struct dt2 {
struct {
unsigned char class[10];
unsigned long start_address;
} xtra;
uint16_t a;
const int b;
} data2_t;
// A macro to generate instances of objects
#define CREATE2(X, Y, XTRA) (complex2_t_ptr)&((struct { \
size_t size; \
data2_t data; \
char name[sizeof(X)]; \
} ) { \
.size = sizeof(X), \
.data = { .xtra = XTRA, .a = Y, .b = CONSTANT }, \
.name = X \
})
// Again create the table
complex2_t_ptr bigger_data_table[] = {
CREATE2("DATA1", 1, {"IO_TBL", 0x123456L}),
CREATE2("DATA2_LONGER", 2, {"BASE_TBL", 0xABC123L}),
CREATE2("D3S", 3, {"MAIN_TBL", 0x555666L << 2}),
};
//...
But there is a probem. This does not compile as the compiler (preprocessor) gets confused by the commas between the structure members. The comma in the passed structure members is seen by the macro and it thinks there are extra parameters.
GCC says you can put brackets round terms where you want to keep the commas, like this
MACRO((keep, the, commas))
e.g. In this case, that would be
CREATE_EXTRA("DATA1", 1, ({"IO_TBL", 0x123456L}) )
But that would not work with a structure as we'd get
.xtra = ({"IO_TBL", 0x123456L})
Which is not a valid initaliser.
The other option would be
CREATE_EXTRA("DATA1", 1, {("IO_TBL", 0x123456L)} )
Which results in
.xtra = {("IO_TBL", 0x123456L)}
Which is also not valid
And if we put the braces inside the macro
.xtra = {EXTRA}
...
CREATE_EXTRA("DATA1", 1, ("IO_TBL", 0x123456L) )
We get the same
Obviously some might say "just pass the elements of XTRA one at a time". Remember this is a simple, very cut down, example and in practice doing that would lose information and make the code much harder to understand, it would be harder to maintain but easer to read if the structures were just copied out longhand.
So the question is, "how to pass compound literal structures to macros as initalisers without getting tripped up by the commas between fields".
NOTE I am stuck with C11 on GCC4.8.x, so C++ or any more recent GCC is not possible.
So there is a way, though I can't find it meantioned on the GCC pages for Macros.
I found what I needed in this article: Comma omission and comma deletion
The following works.
typedef struct _array_data {
size_t size;
char * data;
}array_data_t;
#define ARRAY_DATA(ARRAY...) (char *) \
&(array_data_t) { \
sizeof((char []){ARRAY}), \
(char []){ARRAY} \
}
char * my_array = ARRAY_DATA(1,2,3,4);
size_t sent = send_packet(my_array);
if (len != my_array->size) ERROR("Not all data sent");
There are some interesting aspects to this.
1: Unlike the example in the gcc manual, the brackets are omitted round the {ARRAY}. In the document, the example uses (cast)({structure})
rather than (cast){structure}
. In fact it looks like the brackets are never needed and just confuse the compiler in some cases (like when you take the address).
2: The use of the cast (char [])
rather than (char *)
as one would have thought to be correct.
3: Of course it makes sense but you have to put a cast round the sizeof part too, as otherwise how would it know the size of the individual literals.
For completeness, the macro in the example above expands to:
char * my_array = (char *)&(array_data_t) { \
sizeof((char []){1,2,3,4}),
(char []){1,2,3,4};
}
Any my_array is a pointer to a structure that looks like this.
* my_array = {
size_t size = 4,
char data[4] = {1,2,3,4}
}