我真的很难理解这个序列迭代概念,但是将上面的答案和这里的答案拼凑在一起- 并且一点一点地完成它...... - 我相信我已经弄清楚了,以及如何解释和理解它.
这是一些友好的代码,我已经对其进行了调整:
- 重命名宏(我发现更合理地命名宏有助于我更好地理解这个过程)
- 如果需要通过提供可选的空参数选项。
__VA_OPT__
(如果不需要,可以删除该部分)
- 重新排列宏的顺序,以便它们与我当前的编译器 g++ 一起使用(从技术上讲,这段代码是 c++,而不是 c,但这里基本上是一样的。)
- 提供换出用于此递归过程的可变参数宏的能力
请注意,必须以格式(() () ())
而不是指定参数( , , )
,因为函数样式的宏依赖于它来处理任何参数。请参阅下面的代码以获取深入的解释性演练。
#include <iostream>
#define CONCAT(a, ...) a ## __VA_ARGS__
#define CONCAT_FUNC(a, ...) CONCAT(a, __VA_ARGS__)
//Whatever is inside each () will be prepended and appended with what's here.
#define MyVariadicMacro(...) << __VA_ARGS__ + 7 << " "
#define MyVariadicMacro2(...) << __VA_ARGS__ << " "
#define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
#define RESOLVE_B(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_A
#define RESOLVE_A_END
#define RESOLVE_B_END
#define RECURSE(...) CONCAT_FUNC(RESOLVE_A __VA_ARGS__, _END)
int main()
{
//Choose your own variadic macro, to provide what to prepend and append to each variadic argument!
#define VARIADIC_FUNC MyVariadicMacro
//Note: Empty ()'s are in here just to provide an example that they can be ignored via. __VA_OPT__().
std::cout RECURSE(() (0) () (1) (2) (3) ());
//Swapping out with variadic macro is being utilized for RECURSE.
#undef VARIADIC_FUNC
#define VARIADIC_FUNC MyVariadicMacro2
std::cout RECURSE(() (0) () (1) (2) (3) ());
#undef VARIADIC_FUNC
return 0;
}
Output: 7 8 9 10 0 1 2 3
//Starting with:
std::cout RECURSE(() (0) () (1) (2) (3) ());
//Apply: #define RECURSE(...) CONCAT_FUNC(RESOLVE_A __VA_ARGS__, _END)
std::cout CONCAT_FUNC(RESOLVE_A() (0) () (1) (2) (3) (), _END);
//Apply: #define CONCAT_FUNC(a, ...) CONCAT(a, __VA_ARGS__)
std::cout CONCAT(RESOLVE_A() (0) () (1) (2) (3) (), _END);
//Apply: #define CONCAT(a, ...) a ## __VA_ARGS__
std::cout RESOLVE_A() (0) () (1) (2) (3) () ## _END;
//Apply: #define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
//Note: Since the () is empty, the __VA_OPT__() part is simply skipped.
std::cout RESOLVE_B(0) () (1) (2) (3) () ## _END;
//Apply: #define RESOLVE_B(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_A
std::cout VARIADIC_FUNC(0) RESOLVE_A() (1) (2) (3) () ## _END;
//Apply: #define MyVariadicMacro(...) << __VA_ARGS__ + 7 << " "
//Apply: #define VARIADIC_FUNC MyVariadicMacro
std::cout << 0 + 7 << " " RESOLVE_A() (1) (2) (3) () ## _END;
//Apply: #define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
//Note: Since the () is empty, the __VA_OPT__() part is simply skipped.
std::cout << 0 + 7 << " " RESOLVE_B(1) (2) (3) () ## _END;
//And so on... ending up with:
//Note: Ending with empty () or non-empty() doesn't matter; either way, we will end up with a RESOLVE_?_END.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " " RESOLVE_A() ## _END;
//Apply: #define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
//Note: Since the () is empty, the __VA_OPT__() part is simply skipped.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " " RESOLVE_B ## _END;
//Apply: ## simply concatenates.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " " RESOLVE_B_END;
//Apply: #define RESOLVE_B_END
//Note: In this particular case, we happened to end up with RESOLVE_B_END; in other cases, we will end with
//RESOLVE_A_END.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " ";
//Etc.
std::cout << 7 << " " << 8 << " " << 9 << " " << 10 << " ";
注意:如果你想使用RECURSE
X-macro 概念,你必须做一些额外的事情。X-macro 的问题是你会像这样定义它以便在递归宏中使用:
#define MyThing (() (0) () (1) (2) (3) ())
当你去使用它时,无论是通过。RECURSE()
或任何其他宏,它包含在一些额外的括号中:
//It interprets this as RECURSE((() (0) () (1) (2) (3) ())), which is bad.
std::cout RECURSE(MyThing);
解决方案是使用这样的宏,并让它自然解决删除括号。这是一个为此修改的示例RECURSE()
:
#define ESCAPE_PAREN(...) __VA_ARGS__
//Old RECURSE():
#define RECURSE(...) CONCAT_FUNC(RESOLVE_A __VA_ARGS__, _END)
//New RECURSE():
#define RECURSE(...) CONCAT_FUNC(RESOLVE_A ESCAPE_PAREN __VA_ARGS__, _END)
//Alternatively, just use this instead of RECURSE() (seems to work better):
#define RECURSE_ESCAPE(...) ESCAPE_PAREN __VA_ARGS__
这里需要注意的重要一点是,ESCAPE_PAREN
使用时不会将 in 换__VA_ARGS__
行()
。
编辑:我尝试在实际项目中使用它们后更新了上述宏。我还添加了一些其他可能有用的相关宏(RECURSE_FIRST
(仅通过第一个条目),RECURSE_LATTER
(仅在第一个条目之后通过后面的条目)及其_ESCAPE
版本,以及RECURSE_SPLIT
(通过第一个条目,应用VARIADIC_FUNC_FIRST()
宏在那,只通过后面的条目,VARIADIC_FUNC()
在这些条目上应用宏,再次通过第一个条目,在上面应用VARIADIC_FUNC_END()
宏,然后将所有这些连接在一起......)):
//#define ESCAPE_PAREN(...) __VA_ARGS__
#define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__) RESOLVE_B)
#define RESOLVE_B(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__) RESOLVE_A)
#define RECURSE(...) RESOLVE_A __VA_ARGS__
//#define RECURSE_ESCAPE(...) RECURSE(ESCAPE_PAREN __VA_ARGS__)
#define RESOLVE_FIRST(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__) DISCARD_A)
#define RESOLVE_LATTER(...) RESOLVE_B
#define DISCARD_A(...) __VA_OPT__(DISCARD_B)
#define DISCARD_B(...) __VA_OPT__(DISCARD_A)
#define RECURSE_FIRST(...) RESOLVE_FIRST __VA_ARGS__ ()
#define RECURSE_LATTER(...) RESOLVE_LATTER __VA_ARGS__ ()
#define RESOLVE_SPLIT_FIRST(...) __VA_OPT__(VARIADIC_FUNC_FIRST(__VA_ARGS__) DISCARD_A)
#define RESOLVE_SPLIT_END(...) __VA_OPT__(VARIADIC_FUNC_END(__VA_ARGS__) DISCARD_A)
#define RECURSE_SPLIT(...) RESOLVE_SPLIT_FIRST __VA_ARGS__ () RESOLVE_LATTER __VA_ARGS__ () RESOLVE_SPLIT_END __VA_ARGS__ ()
在我的宏中尝试使用逗号和分号时,我发现这些麻烦/抑制要少得多......它们当然就像上面一样工作,您可以使用以下形式的 X-宏:
MyXMacro((a) (b) (57) (32))
MyXMacro((c) (d) (49) (32))
也可以看看: