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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在同一个“调用堆栈”中两次使用宏列表?相关的知识,希望对你有一定的参考价值。

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

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

答案

以下代码将起作用:

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

以上是关于如何在同一个“调用堆栈”中两次使用宏列表?的主要内容,如果未能解决你的问题,请参考以下文章

如何在一个 submitHandler 中两次更新反应状态

如何在 Django 中两次在查询中使用过滤器

React:如何在页面中两次渲染组件?

如何防止在node.js中两次调用相同URL的获取

如何在 C# 中两次读取 Http 响应流?

如何计算 Git 中两次提交之间更改的行数?