c++ 宏如何为传递给它的标记提供值,并展开和粘贴这些值?

z31licg0  于 2023-05-19  发布在  其他
关注(0)|答案(2)|浏览(134)

我目前的解决方案:

#define TEMPLATE_X_YX_(X, Y, a, b, c) a ## X ## b ## Y ## X ## c
// other templates may also exist, with different parameter arrangements

#define GENERATOR(name, template, ...) \
        const auto name##1 = &TEMPLATE##template(A, 1, __VA_ARGS__); \
        const auto name##2 = &TEMPLATE##template(B, 2, __VA_ARGS__); \
        const auto name##3 = &TEMPLATE##template(C, 3, __VA_ARGS__);

GENERATOR(example, _X_YX_, example_, _mid_, _suffix)

// expected output:

        const auto example1 = &example_A_mid_1A_suffix;
        const auto example2 = &example_B_mid_2B_suffix;
        const auto example3 = &example_C_mid_3C_suffix;

但是我发现令人沮丧的是需要定义和引用TEMPLATE宏,并且它与我使用的实际模板定义不一致。我喜欢的可能是:

GENERATOR(example, example_, X, _mid_, Y, X, _suffix)

我尝试了一些嵌套实验,并试图强制将X和Y扩展为更深层次的宏的参数,但预处理器没有这样做。
这方面的一个应用是快速地将具有复杂名称的每个类型的C函数的大列表折叠成C++重载的简单名称。
我想知道我是否可以为替换参数做一些预定义的宏(可能在它们的定义中包含逗号),以在下降之前强制执行一轮额外的宏扩展,但我现在无法想出一个明确的计划。即使成功了,也可能是非法的。

guz6ccqo

guz6ccqo1#

这是可能的,但是编写在循环体内部使用外部状态的宏循环(在你的例子中,状态是XY的值)是很棘手的,所以我将使用一个小库:macro_sequence_for(我是作者)。
我还对模式语法做了一点修改,因为循环(a)(b)(b)(与a, b, c相反)形式的序列更容易。在逗号分隔的列表上循环需要样板宏,其数量限制了序列元素的最大数量。
在www.example.com上运行gcc.godbolt.org

GENERATOR(foo, (x_is_)(X)(_and_y_is_)(Y))
// Expands to:
// const auto foo1 = &x_is_A_and_y_is_1;
// const auto foo2 = &x_is_B_and_y_is_2;
// const auto foo3 = &x_is_C_and_y_is_3;

宏实现:

#include <macro_sequence_for.h>

#define GENERATOR(name, seq) \
    const auto SF_CAT(name,1) = &DETAIL_MAKE_INIT(A, 1, seq);\
    const auto SF_CAT(name,2) = &DETAIL_MAKE_INIT(B, 2, seq);\
    const auto SF_CAT(name,3) = &DETAIL_MAKE_INIT(C, 3, seq);

// Generates a single initializer.
#define DETAIL_MAKE_INIT(x, y, seq) \
    SF_FOR_EACH(SF_NULL, DETAIL_CAT_STEP, SF_STATE,, SF_FOR_EACH(DETAIL_BODY, SF_STATE, SF_NULL, (x, y), seq))

// Loop body for the inner loop.
// Turns the initial sequence into a sequence of parts to be concatenated.
#define DETAIL_BODY(n, d, e) DETAIL_ELEM(e, SF_CAT(DETAIL_PLACEHOLDER_, e) d)

// Placeholders.
#define DETAIL_PLACEHOLDER_X(x, y) ,x
#define DETAIL_PLACEHOLDER_Y(x, y) ,y

// Extra indirection to turn `,` into argument separator.
#define DETAIL_ELEM(...) DETAIL_ELEM_A(__VA_ARGS__)
// If given `var_name, , value`, returns `(value)`.
// Otherwise if given `text, ...`, returns `(text)`.
#define DETAIL_ELEM_A(e, f, ...) DETAIL_ELEM_B##__VA_OPT__(_VAR)(e, __VA_ARGS__)
#define DETAIL_ELEM_B(e, ...) (e)
#define DETAIL_ELEM_B_VAR(e, ...) (__VA_ARGS__)

// A step function for the outer loop. Concatenates (appends) the element to the state.
#define DETAIL_CAT_STEP(n, d, e) SF_CAT(d, e)

这里有两个循环,第一个循环的输出作为第二个循环的输入序列。
第一个循环扩展输入中的变量,因此(x_is_)(X)(_and_y_is_)(Y)变成(x_is_)(A)(_and_y_is_)(B),第二个循环将元素连接成一个标识符。

tktrz96b

tktrz96b2#

这似乎是有效的,直到一个适度的限制:

#define JOIN_(x, y) x ## y
#define JOIN(x, y) JOIN_(x, y)
#define PREFIX_X(X, Y, end) JOIN(X, end)
#define PREFIX_Y(X, Y, end) JOIN(Y, end)
#define PREFIX(X, Y, end) end

#define UNPACK0(X, Y, t, ...) t
#define UNPACK1(X, Y, t, p, ...) JOIN(t, PREFIX##p(X, Y, UNPACK0(X, Y, __VA_ARGS__)))
#define UNPACK2(X, Y, t, p, ...) JOIN(t, PREFIX##p(X, Y, UNPACK1(X, Y, __VA_ARGS__)))
// ... lots more of the same ...
#define UNPACK9(X, Y, t, p, ...) JOIN(t, PREFIX##p(X, Y, UNPACK8(X, Y, __VA_ARGS__)))

#define UNPACK(X, Y, ...) UNPACK9(X, Y, __VA_ARGS__,,,,,,,,,,,,,,,,,,,,,,)
#define ARG_X ,_X,
#define ARG_Y ,_Y,

#define GENERATOR(name, ...) \
        const auto name##1 = &UNPACK(A, 1, __VA_ARGS__); \
        const auto name##2 = &UNPACK(B, 2, __VA_ARGS__); \
        const auto name##3 = &UNPACK(C, 3, __VA_ARGS__);

GENERATOR(example,  prefix_ ARG_X _mid_ ARG_Y ARG_X _suffix)

相关问题