How to append to an array using a variadic macro?

Viewed 81

I'm trying to eliminate a bunch of boilerplate code by using macros.

Here's what works. I can replace:

int do_register_script(struct context *L)
{
    method_type const _instance_methods[] = {
        {"new", __new},
        {"delete", __delete}
        {NULL, NULL}
    };
    register_type(L, script, _instance_methods, 0);
    return 1;
}

with a macro

#define do_register_type(name) \
    int do_register_ ## name(struct context *L) \
    { \
        method_type const _instance_methods[] = { \
            {"new", __new}, \
            {"delete", __delete}, \
            {NULL, NULL} \
        }; \
        register_type(L, name, _instance_methods, 0); \
        return 1; \
    }

like so:

do_register_type(script);

which is perfect!

But I also have some that look like this:

int do_register_rectangle(struct context *L)
{
    method_type const _instance_methods[] = {
        {"new", __new},
        {"delete", __delete},
        {"area", area},
        {"perimeter", perimeter}
        {NULL, NULL}
    };
    register_type(L, rectangle, _instance_methods, 0);
    return 1;
}

And now the above macro doesn't work.

How can I add another parameter to the macro to support this?

I'm using C, not C++, so no templates.

UPDATE: Also sometimes the code this is going in uses aliases for the names

        {"area", area},
        {"Area", area},
        {"perimeter", perimeter}
        {"Perimeter", perimeter}
2 Answers

Looping over lists in preprocessor normally requires generating boilerplate macros, but you can avoid that by using a somewhat weird syntax for lists:

do_register_type(script, (area)(perimeter))

Here's how you loop over such lists:

#define REG_LOOP(seq) REG_END(REG_LOOP_A seq)

#define REG_END(...) REG_END_(__VA_ARGS__)
#define REG_END_(...) __VA_ARGS__##_END

#define REG_LOOP_A(func) REG_LOOP_BODY(func) REG_LOOP_B
#define REG_LOOP_B(func) REG_LOOP_BODY(func) REG_LOOP_A
#define REG_LOOP_A_END
#define REG_LOOP_B_END

#define REG_LOOP_BODY(func) {#func, func},

REG_LOOP((foo)(bar)) will expand to {"foo", foo}, {"bar", bar},.

Then you add this macro to do_register_type:

#define do_register_type(name, seq) \
    int do_register_ ## name(struct context *L) \
    { \
        method_type const _instance_methods[] = { \
            {"new", __new}, \
            {"delete", __delete}, \
            REG_LOOP(seq) \
            {NULL, NULL} \
        }; \
        register_type(L, name, _instance_methods, 0); \
        return 1; \
    }

do you want that :

#define do_register_type(name, ...) \
    int do_register_ ## name(struct context *L) \
    { \
        method_type const _instance_methods[] = { \
            {"new", __new}, \
            {"delete", __delete}, \
            __VA_ARGS__ __VA_OPT__(,) \
            {NULL, NULL} \
        }; \
        register_type(L, name, _instance_methods, 0); \
        return 1; \
    }

do_register_type(script);

do_register_type(script, {"area", area}, {"perimeter", perimeter});

previous code being in m.c:

pi@raspberrypi:~ $ gcc -E m.c
# 1 "m.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "m.c"
# 14 "m.c"
int do_register_script(struct context *L) { method_type const _instance_methods[] = { {"new", __new}, {"delete", __delete}, {NULL, NULL} }; register_type(L, script, _instance_methods, 0); return 1; };

int do_register_script(struct context *L) { method_type const _instance_methods[] = { {"new", __new}, {"delete", __delete}, {"area", area}, {"perimeter", perimeter} , {NULL, NULL} }; register_type(L, script, _instance_methods, 0); return 1; };
pi@raspberrypi:~ $ 

I noticed you do not want the prefix '__' for area and perimeter, contrarily to new and delete but may be you want it for some other cases, because of that it is not possible to expand automatically and the only way I see is to give explicitly the code to add

Related