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

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

我有信息存储在宏列表中,如下所示:

#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两次 - 或者一种解决方法 - 使用我现有的列表?

c macros c-preprocessor c11
1个回答
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;

不幸的是,我对这有效的原因并不十分了解;我只是尝试了一些在这种情况下有助于帮助的技巧。但我可以解释一些。

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

当宏扩展到通常是类似函数的宏调用的标记时,有时我们已经通过了它们将被扩展的阶段。

我们可以通过将宏的扩展延迟到标志不会引起任何问题的程度来绕过第一个问题,方法是在评估时通过添加第二个宏来创建宏调用。这就是DELAYED_CALL所做的。但是这样做,我们遇到了第二个问题,所以我们必须添加一些调用EVAL来重新扫描宏(类似函数的宏的参数总是被扫描,所以将一系列标记传递给一个函数只是回声其参数的宏将导致重新扫描)。

有时我们需要一些重新扫描才能使一切正常。 EVAL2(X)只是EVAL(EVAL(X))的简写。有时需要更多的逃避。

以下代码使发生的事情更加清晰。注意MYLIST 2版本如何需要少一个EVAL;这是因为AA呼叫MYLIST,而MYLIST 2是设置了违规标志的人。

#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。这篇文章的作者比我更了解这一点。

© www.soinside.com 2019 - 2024. All rights reserved.