I have a small recursive AST as follows:
enum ABC
{
A,
B,
C
};
typedef struct abc_t
{
enum ABC kind;
union {
struct a_
{
int x;
} a_;
struct b_
{
int y;
} b_;
struct c_
{
struct abc_t **array;
} c_;
} u;
} abc_t;
To simplify the constructions, I define fill macros:
#define ABC_SET_A(n) (abc_t) { .kind = A, .u.a_ = { n } }
#define ABC_SET_B(n) (abc_t) { .kind = B, .u.b_ = { n } }
const abc_t bar = ABC_SET_A(5);
const abc_t foo = ABC_SET_B(2);
However, I don't know how to fill the "c_" branch of my AST with a macro. I would like to arrive at this result:
const abc_t test = ABC_SET_C({ABC_SET_B(4), ABC_SET_A(3), ABC_SET_B(2)});
// or
const abc_t test = ABC_SET_C(ABC_SET_B(4), ABC_SET_A(3), ABC_SET_B(2)); // With variadics arguments
The goal would be to have an immutable array of abc_t
that would represent a set of pre-built structures, something like:
const abc_t pre_builds[] =
{
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C({ABC_SET_A(8), ABC_SET_B(4)}), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
...
};
I don't know how to handle a recursive structure for a such as macro, especially for an array in this case. How do I do it?
First fix your ABC_SET_A
and ABC_SET_B
. You don't initialize int
with braces like int var = { 1 };
, you initialize int
like int var = 1;
:
#define ABC_SET_A(n) (abc_t){ .kind = A, .u.a_ = n }
#define ABC_SET_B(n) (abc_t){ .kind = B, .u.b_ = n }
Doing your ABC_SET_C
is easy. As you have array of pointers in your structure, you can do this:
#define ABC_SET_C(...) (abc_t){ .kind = C, .u.c_ = (abc_t*[]) __VA_ARGS__ }
const abc_t pre_builds[] =
{
ABC_SET_A(10), // pre_builds[0]
// note - as this is array of pointers, I added & to get the pointer
ABC_SET_C({&ABC_SET_A(8), &ABC_SET_B(4)}), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
The {
}
braces are not special to preprocessor anyway and not parsed by it. I would prefer:
#define ABC_SET_C(...) (abc_t){ .kind = C, .u.c_ = (abc_t*[]) { __VA_ARGS__ } }
const abc_t pre_builds[] =
{
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C(&ABC_SET_A(8), &ABC_SET_B(4)), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
If you want to get rid of writing &
in front of every ABC_SET_*
, then you can overload the macro on number of arguments and insert them per each argument. I find it not worth the effort, but it may be implemented like this for up to 3 arguments:
#define ABC_SET_A(n) ((abc_t){ .kind = A, .u.a_ = n })
#define ABC_SET_B(n) ((abc_t){ .kind = B, .u.b_ = n })
#define ABC_SET_C_1(_1) &_1
#define ABC_SET_C_2(_1,_2) &_1, &_2
#define ABC_SET_C_3(_1,_2,_3) &_1, &_2, &_3
#define ABC_SET_C_N(_1,_2,_3,N,...) ABC_SET_C##N
#define ABC_SET_C(...) (abc_t){ .kind = C, .u.c_ = (abc_t*[]) { \
ABC_SET_C_N(__VA_ARGS__,_3,_2,_1)(__VA_ARGS__) \
} }
const abc_t pre_builds[] = {
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C(ABC_SET_A(8), ABC_SET_B(4)), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
Personally I don't like writing (abc_t)
a compound literal in macro variable initialization. They make somethings like abc_t *arr = (abc_t[]){ ABC_SET_A(8) }
impossible. I prefer leaving writing the &(abc_t)
if needed to the user, so that others will know what is a pointer and what is not a pointer, so they know when and how the memory is allocated, what is the storage duration of the memory, etc. It also makes the macro easy to work in C++ and C99. Like this:
#define ABC_SET_A(n) { .kind = A, .u.a_ = n }
#define ABC_SET_B(n) { .kind = B, .u.b_ = n }
#define ABC_SET_C(...) { .kind = C, .u.c_ = __VA_ARGS__ }
const abc_t pre_builds[] = {
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C((abc_t*[]){ &(abc_t)ABC_SET_A(8), &(abc_t)ABC_SET_B(4) }), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
// it also allows for something like the following naturally to work:
abc_t *make_me_visible_in_debugger[] = {
&(abc_t)ABC_SET_A(8),
&(abc_t)ABC_SET_B(4)
};
const abc_t pre_builds2[] = {
ABC_SET_A(10), // pre_builds[0]
ABC_SET_C(make_me_visible_in_debugger), // pre_builds[1]
ABC_SET_B(23) // pre_builds[2]
};
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments