The nearest equivalent to templates in C (that I know of) is X-Macros. I think you can achieve this result, but it will require introducing another identifier for each struct (actually it doesn't -- scroll down to the "Edit"!) which we can sync with the array by declaring these identifiers in an enum.
To start with, we change the initializer elements to be in the form of macro calls. For the style I prefer, the name of this macro is not important, so I'll call it _
. All calls will need the same number of elements, so add an empty list where necessary. And the whole thing is wrapped in one big macro. This big macro will receive another macro as an argument which it calls for each element.
#define DATA(_) \
_(4, "FIRST_ELEMENT", {}) \
_(6, "SECOND_ELEMENT", {}) \
_(10, "FATHER ELEMENT", {15, 20}) \
_(15, "SON ELEMENT 1", {}) \
_(20, "SON ELEMENT 2", {}) \
_(0, NULL, {})
Now we can declare the array data by defining a usage macro that emit the arguments in the correct form for the array declaration.
#define CREATE_ARRAY(a, b, c) \
{a, b, c},
struct item items[] = {
DATA(CREATE_ARRAY)
}
So far we've just achieved the same result. But now it's in a more flexible form. The next step is adding the new IDs.
#define DATA(_) \
_(FIRST, 4, "FIRST_ELEMENT", {}) \
_(SECOND, 6, "SECOND_ELEMENT", {}) \
_(FATHER, 10, "FATHER ELEMENT", {15, 20}) \
_(SON1, 15, "SON ELEMENT 1", {}) \
_(SON2, 20, "SON ELEMENT 2", {}) \
_(END, 0, NULL, {})
And adjust the CREATE_ARRAY
macro to account for the new argument.
#define CREATE_ARRAY(a, b, c, d) \
{b, c, d},
struct item items[] = {
DATA(CREATE_ARRAY)
};
Now the fun part. We make another macro to generate the IDs as enum values.
#define CREATE_IDS(a, b, c, d) \
a,
enum identifiers {
DATA(CREATE_IDS)
};
Now the data can use these identifiers to index the array.
#define DATA(_) \
_(FIRST, 4, "FIRST_ELEMENT", {}) \
_(SECOND, 6, "SECOND_ELEMENT", {}) \
_(FATHER, 10, "FATHER ELEMENT", {SON1, SON2}) \
_(SON1, 15, "SON ELEMENT 1", {}) \
_(SON2, 20, "SON ELEMENT 2", {}) \
_(END, 0, NULL, {})
And, of course, remove the child_id
member from the struct, since our new identifiers are the desired array indices, directly.
Edit. Wait a moment. You have identifiers already. And they're already unique. So we don't need to introduce new ones. We can simply mangle them! __VA_ARGS__
is also needed to handle the possible embedded commas in the child list.
#define CREATE_ARRAY(a, b, ...) \
{a, b, __VA_ARGS__ },
#define ID_(x) ID ## x
#define CREATE_IDS(a, b, ...) \
ID_(a),
#define DATA(_) \
_(4, "FIRST_ELEMENT", {}) \
_(6, "SECOND_ELEMENT", {}) \
_(10, "FATHER ELEMENT", {ID15, ID20}) \
_(15, "SON ELEMENT 1", {}) \
_(20, "SON ELEMENT 2", {}) \
_(0, NULL, {})
enum identifiers {
DATA(CREATE_IDS)
};
struct item items[] = {
DATA(CREATE_ARRAY)
};
cpp -P
output (linebreaks added):
enum identifiers {
ID4, ID6, ID10, ID15, ID20, ID0,
};
struct item items[] = {
{4, "FIRST_ELEMENT", {} },
{6, "SECOND_ELEMENT", {} },
{10, "FATHER ELEMENT", {ID15, ID20} },
{15, "SON ELEMENT 1", {} },
{20, "SON ELEMENT 2", {} },
{0, NULL, {} },
};
For more about X-macros, see the answers to this question (one of which I wrote :P).