宏如何为传递给它的标记提供值,并将它们展开并粘贴在一起?

问题描述 投票:0回答:2

我目前的解决方案:

#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++ 重载的简单名称。

我有点想知道我是否可以用一些预定义的宏来代替参数(它们的定义中可能包含逗号),以在下降之前强制进行额外一轮的宏扩展,但我想不通现在有一个明确的计划。即使有效,也可能是非法的。

c++ c macros preprocessor
2个回答
1
投票

这是可能的,但是编写在循环体内使用外部状态的宏循环(在您的情况下状态是

X
Y
的值)很棘手,所以我将使用一个小库来那:
macro_sequence_for
(我是作者)。

我还稍微更改了模式语法,因为它更容易循环形式为

(a)(b)(b)
(与
a, b, c
相反)的序列。循环逗号分隔的列表需要样板宏,其数量限制了序列元素的最大数量。

在 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)
,然后第二个循环将元素连接成一个标识符。


0
投票

这似乎有效,达到适度的限制:

#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)
© www.soinside.com 2019 - 2024. All rights reserved.