Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/141.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 通过_VA_OPT递归宏___C++_Preprocessor_C++20 - Fatal编程技术网

C++ 通过_VA_OPT递归宏__

C++ 通过_VA_OPT递归宏__,c++,preprocessor,c++20,C++,Preprocessor,C++20,使用编写递归宏是否合法 GCC和Clang似乎不会递归地替换,但我不确定这是否是有意的(因为\uuuuu VA\uu OPT\uuuu支持是最近才出现的) C++规范(§19.3.1/3:\uu VA\u OPT\uu): 否则,替换由扩展的结果组成 内容作为当前函数(如宏)的替换列表 重新扫描和进一步更换之前 上面突出显示的部分是否意味着不可能进行递归 例如,要添加可变宏参数列表,请执行以下操作: #define RECURSE(mFIRST, ...) + mFIRST __VA_OPT_

使用
编写递归宏是否合法

GCC和Clang似乎不会递归地替换,但我不确定这是否是有意的(因为
\uuuuu VA\uu OPT\uuuu
支持是最近才出现的)

C++规范(§19.3.1/3:
\uu VA\u OPT\uu
):

否则,替换由扩展的结果组成 内容作为当前函数(如宏)的替换列表 重新扫描和进一步更换之前

上面突出显示的部分是否意味着不可能进行递归


例如,要添加可变宏参数列表,请执行以下操作:

#define RECURSE(mFIRST, ...) + mFIRST __VA_OPT__(RECURSE(__VA_ARGS__))

int main(int argc, char const* const argv[])
    {
        return 1 RECURSE(2, 3, 4);
        // Expected result: "return 1 + 2 + 3 + 4;"
    }
GCC和Clang都在其后预处理中生成
递归

// NOTE: Below is the output from g++ -E
int main(int argc, char const* const argv[])
 {
  return 1 + 2 RECURSE(3, 4);
 }


注意:如果这是可能的,更复杂的变量宏可以相当容易地编写,例如连接,因为您可以从
\uuuuuuuuu VA\u NO\u OPT\uuuuuu>创建自定义
\uuuuuuuuuu OPT\uuuuuuu>,从而为1和2+参数提供完全独立的代码。

答案是肯定的!在某种程度上,你已经可以在C++20中实现,并且
\uuuu VA\uu OPT\uuu
使一些事情变得越来越好

下面是您可以做的事情:为每个
宏定义一个
,它将一个宏应用于一组参数,并且比在
\uuuu VA\u OPT\uuu
之前必须做的可怕事情要好得多:

#define PARENS () // Note space before (), so object-like macro

#define EXPAND(arg) EXPAND1(EXPAND1(EXPAND1(EXPAND1(arg))))
#define EXPAND1(arg) EXPAND2(EXPAND2(EXPAND2(EXPAND2(arg))))
#define EXPAND2(arg) EXPAND3(EXPAND3(EXPAND3(EXPAND3(arg))))
#define EXPAND3(arg) EXPAND4(EXPAND4(EXPAND4(EXPAND4(arg))))
#define EXPAND4(arg) arg

#define FOR_EACH(macro, ...)                                    \
  __VA_OPT__(EXPAND(FOR_EACH_HELPER(macro, __VA_ARGS__)))
#define FOR_EACH_HELPER(macro, a1, ...)                         \
  macro(a1)                                                     \
  __VA_OPT__(FOR_EACH_AGAIN PARENS (macro, __VA_ARGS__))
#define FOR_EACH_AGAIN() FOR_EACH_HELPER

FOR_EACH(F, a, b, c, 1, 2, 3)   // => F(a) F(b) F(c) F(1) F(2) F(3)

这仅仅是因为它们总是无限递归吗?因为对于
\uu VA\u OPT\uuu
,这不再是真的。我想这是因为:19.3.4/2:重新扫描和进一步替换。“如果在扫描替换列表(不包括源文件的其余预处理标记)期间找到要替换的宏的名称,则该宏不会被替换”。不太可能被修改。C++非常努力地摆脱了每次修改使用宏的需要。由于各种原因,它不能,但递归不太可能被添加<代码>\uuuu VA\u OPT\uuuuuu
是一个小小的方便功能。递归宏是一个巨大的变化。@Nicolas:AFAICT
\uuuu VA\u OPT\uuuu
是c++2a特性,而不是c++17特性,因此标记回滚。抱歉,如果我的信息有误。我实际上使用了类似的东西,F通常是一个用户定义的字符串文字,所以宏(“dir1”、“dir2”、“file”)变成了“dir1”\ux、“dir2”\ux,“file”\ux。在创建包含路径中每个目录名的constexpr数组时非常有用(更清晰),特别是当UDL名称实际上比目前的64个项目长时,因此仍然感觉有点不太正常。@xaxazak上面的代码最多接受342个参数,这比64个参数好,并且可能足以满足任何合理的要求。如果不是的话,为EXPAND5多写一行代码,你肯定已经足够了。你的指数技术比我的好(我会修改它,谢谢),但不幸的是,任何任意的最大值都会让人感觉不舒服。还有一个与编译时间的折衷。如果您指定了100条路径,每个路径需要1000条宏替换,并且编译器正在跟踪这些路径进行调试,这可能会变得很明显。此外,这也为类似
\uuu VA\u SEP\uuu()
的东西提供了一个很好的例子,它的行为类似于
\uu VA\u ARGS\uuuu
,但使用
而不是逗号。这可能会消除对递归的许多需求。@xaxazak
\uuu VA\u SEP\uu
对于每个
来说都不如
一般。如果他们想促进这种宏,最好的办法就是直接将
FOR_EACH
(或
\u FOR_EACH_uu
或其他内容)构建到预处理器中。