如何在同一“调用堆栈”中两次使用宏列表?

时间:2019-03-03 21:28:53

标签: c macros c-preprocessor c11

我将信息存储在这样的宏列表中:

#define MYLIST(XX) \
  XX(1, hello)     \
  XX(2, world)     \
  ...

现在,我想在同一“调用堆栈”中两次使用此宏。这是一个愚蠢的例子:

#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +
int foo = MYLIST(AA) 0;

但是,自第二次MYLIST does not expand起,此操作无效:

int foo = 1 + (MYLIST(BB) 0) + 2 + (MYLIST(BB) 0) + 0;

是否可以使用现有列表在同一“调用堆栈”中两次使用MYLIST或一种解决方法?

1 个答案:

答案 0 :(得分:3)

以下代码将起作用:

#define EVAL(...) __VA_ARGS__
#define EVAL2(...) EVAL(__VA_ARGS__)
#define EMPTY()
#define DELAYED_CALL(F, ...) F EMPTY()(__VA_ARGS__)

#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +

#define MYLIST(XX) \
    DELAYED_CALL(XX, 1, hello) \
    DELAYED_CALL(XX, 2, world)

int foo = EVAL2(MYLIST(AA)) 0;

输出:int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;

不幸的是,我对为什么起作用没有深刻的了解;我只是尝试了一些技巧,在这种情况下往往会有所帮助。但是我可以解释一下。

有时,宏被标记为“不再进一步扩展”。该标志通常在您开始扩展它时设置,并在扩展完成后取消设置。这往往会阻止递归。

当宏扩展为通常是类似于函数的宏调用的令牌时,有时我们已经通过了将其扩展的阶段。

我们可以通过在评估宏时将第二个宏添加到 create 宏调用中,将宏的扩展延迟到标志不会引起任何问题的程度来绕过第一个问题。 DELAYED_CALL就是这样做的。但是这样做时,我们遇到了第二个问题,因此我们必须向EVAL添加一些调用才能重新扫描该宏(始终会扫描类似函数的宏的参数,因此传递一系列只是回传其参数的类似函数的宏的令牌将导致重新扫描。

有时候,我们需要进行几次重新扫描才能使所有功能正常运行。 EVAL2(X)只是EVAL(EVAL(X))的简写。有时需要更多评估。

下面的代码使事情更加清晰。请注意,MYLIST2版本如何需要少一个EVAL;这是因为AA调用了MYLIST,而MYLIST2是设置了违规标志的那个。

#define EVAL(...) __VA_ARGS__
#define EVAL2(...) EVAL(__VA_ARGS__)
#define EVAL3(...) EVAL2(__VA_ARGS__)
#define EMPTY()
#define DELAYED_CALL(F, ...) F EMPTY()(__VA_ARGS__)

#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +

#define MYLIST(XX) \
    DELAYED_CALL(XX, 1, hello) \
    DELAYED_CALL(XX, 2, world)

#define MYLIST2(XX) \
    XX(1, hello) \
    XX(2, world)

% MYLIST
int foo = MYLIST(AA) 0;
int foo = EVAL(MYLIST(AA)) 0;
int foo = EVAL2(MYLIST(AA)) 0;

% MYLIST2
int foo = MYLIST2(AA) 0;
int foo = EVAL(MYLIST2(AA)) 0;
int foo = EVAL2(MYLIST2(AA)) 0;

其输出将是:

% MYLIST
int foo = AA (1, hello) AA (2, world) 0;
int foo = 1 + (BB (1, hello) BB (2, world) 0) + 2 + (BB (1, hello) BB (2, world) 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;

% MYLIST2
int foo = 1 + (BB (1, hello) BB (2, world) 0) + 2 + (BB (1, hello) BB (2, world) 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;

(%符号没有什么特别的。我只希望注释出现在输出中,并且在预处理期间将删除C样式的注释。)

Further reading。这篇文章的作者比我更了解这一点。