简体   繁体   中英

How to fill a pointer array without a function call and from a macro?

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]
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM