如何使用可变参数宏附加到数组?

How to append to an array using a variadic macro?

我正在尝试使用宏来消除一堆样板代码。

这是有效的方法。我可以替换:

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

用宏

#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; \
    }

像这样:

do_register_type(script);

完美!

但我也有一些像这样的:

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

现在上面的宏不起作用了。

如何向宏添加另一个参数以支持此功能?

我使用的是 C,而不是 C++,所以没有模板。

更新:此外,有时代码会使用名称的别名

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

在预处理器中遍历列表通常需要生成样板宏,但您可以通过对列表使用有点奇怪的语法来避免这种情况:

do_register_type(script, (area)(perimeter))

以下是循环此类列表的方法:

#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)) 将扩展为 {"foo", foo}, {"bar", bar},

然后你把这个宏添加到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; \
    }

你想要那个吗:

#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});

之前的代码在 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:~ $ 

我注意到您不希望 areaperimeter 的前缀“__”,与 new[ 相反=22=] 和 delete 但在某些其他情况下您可能需要它,因为它不可能自动扩展,我看到的唯一方法是明确给出代码添加