首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C-预处理器递归宏

C-预处理器递归宏
EN

Stack Overflow用户
提问于 2012-11-29 05:47:08
回答 3查看 11K关注 0票数 10
代码语言:javascript
运行
复制
#define PP_ARG0_(arg0, ...) arg0
#define PP_REST_(arg0, ...) __VA_ARGS__
#define PP_ARG0(args) PP_ARG0_ args
#define PP_REST(args) PP_REST_ args

#define FUNCTION(name) void name();
#define FUNCTION_TABLE(...)                   \
    FUNCTION(PP_ARG0((__VA_ARGS__)))          \
    FUNCTION_TABLE(PP_REST((__VA_ARGS__)))    \

测试代码:

代码语言:javascript
运行
复制
FUNCTION_TABLE(f1, f2,f3,testA,testB,testC);

显然,由于递归扩展,它只会声明void f1();,其余的不会扩展:

代码语言:javascript
运行
复制
void f1(); FUNCTION_TABLE(f2,f3,testA,testB,testC);

在这种情况下,我可以使用哪种技巧来实现递归扩展?问题是,我需要支持许多参数(最高100),而且我绝对不能使用boost。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-11-30 08:50:48

如果有人想要做同样的事情,这里有答案。

代码语言:javascript
运行
复制
#define _PP_0(_1, ...) _1            // (a,b,c,d) => a
#define _PP_X(_1, ...) (__VA_ARGS__) // (a,b,c,d) => (b,c,d)

//for each a in __VA_ARGS__ do f(a,x) 
//where x is some parameter passed to PP_TRANSFORM
#define PP_TRANSFORM(f,x,...) \
    PP_JOIN(PP_TRANSFORM_,PP_NARG(__VA_ARGS__))(f,x,(__VA_ARGS__))

#define PP_TRANSFORM_0(...)
#define PP_TRANSFORM_1( f,x,a) f(_PP_0 a,x) PP_TRANSFORM_0( f,x,_PP_X a)
#define PP_TRANSFORM_2( f,x,a) f(_PP_0 a,x) PP_TRANSFORM_1( f,x,_PP_X a)
...
#define PP_TRANSFORM_51(f,x,a) f(_PP_0 a,x) PP_TRANSFORM_50( f,x,_PP_X a)
...
#define PP_TRANSFORM_99(f,x,a) f(_PP_0 a,x) PP_TRANSFORM_98(f,x,_PP_X a)
#define PP_TRANSFORM_100(f,x,a)f(_PP_0 a,x) PP_TRANSFORM_99(f,x,_PP_X a)

其中PP_NARG是计算参数数量的宏,PP_JOIN是连接标记的宏(即PP_JOIN(a,b) => ab)。如果您希望能够处理超过64个参数,则还需要修补该PP_NARG

现在,回到原来的问题。使用PP_TRANSFORM的解决方案是:

代码语言:javascript
运行
复制
#define FUNCTION(name, dummy) void name();
#define FUNCTION_TABLE(...) PP_TRANSFORM(FUNCTION,dummy,__VA_ARGS__)

如果想要生成c++实现函数,那么可以使用PP_TRANSFORM不透明的x参数

代码语言:javascript
运行
复制
#define FUNCTION_CPP(name, class) void class::name(){}
#define FUNCTION_TABLE_CPP(...) PP_TRANSFORM(FUNCTION_CPP,MyClass,__VA_ARGS__)

所有这些在GCC和MSVC预处理器上都能很好地工作;PP_TRANSFORM_NN没有使用__VA_ARGS__来避免为GCC和MSVC分别实现100个定义。

票数 4
EN

Stack Overflow用户

发布于 2013-06-08 06:59:24

最简单的解决方案是像这样使用序列迭代:

代码语言:javascript
运行
复制
#define CAT(x, y) PRIMITIVE_CAT(x, y)
#define PRIMITIVE_CAT(x, y) x ## y

#define FUNCTION(name) void name();
#define FUNCTION_TABLE(seq) CAT(FUNCTION_TABLE_1 seq, _END)
#define FUNCTION_TABLE_1(x) FUNCTION(x) FUNCTION_TABLE_2
#define FUNCTION_TABLE_2(x) FUNCTION(x) FUNCTION_TABLE_1
#define FUNCTION_TABLE_1_END
#define FUNCTION_TABLE_2_END

然后使用预处理器序列而不是变量参数调用FUNCTION_TABLE

代码语言:javascript
运行
复制
FUNCTION_TABLE((f1)(f2)(f3)(testA)(testB)(testC))

这不仅简单得多,而且比使用递归解决方案更快(即更快的编译)(就像你所展示的或者像这个here一样)。

票数 10
EN

Stack Overflow用户

发布于 2021-02-10 12:16:36

我真的很难理解这个序列迭代的概念,但是把above answer和答案here拼凑在一起--然后一点一点地通过它……-我相信我已经弄明白了,以及如何解释和理解它。

下面是一些友好的代码,我对它们进行了调整:

  • 重命名宏(我发现更明智地命名宏会帮助我更好地理解这个过程)
  • 在需要时提供了一个可选的空参数选项。__VA_OPT__ (如果对宏的顺序进行unwanted)
  • rearranging,则可以删除该部分,以便它们可以与我当前的编译器g++一起工作(从技术上讲,这段代码是c++,而不是c,但它基本上与交换用于此递归过程的可变宏的能力相同

请注意,参数指定为(() () ())格式,而不是( , , )格式,因为函数样式的宏要依赖它才能使用任何参数。有关详细的说明性演练,请参阅下面的代码。

代码语言:javascript
运行
复制
#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

代码语言:javascript
运行
复制
//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 << " ";

注意:如果你想使用带有X-宏概念的RECURSE,你必须做一些额外的事情。X-宏的问题是,为了在递归宏中使用,您可能会像这样定义它:

代码语言:javascript
运行
复制
#define MyThing (() (0) () (1) (2) (3) ())

当你像那样使用它的时候,要么通过。RECURSE()或任何其他宏,它都包含在一些额外的圆括号中:

代码语言:javascript
运行
复制
//It interprets this as RECURSE((() (0) () (1) (2) (3) ())), which is bad.
std::cout RECURSE(MyThing);

解决方案是使用像这样的宏,并让它自然地解析以删除括号。以下是为此修改RECURSE()的示例:

代码语言:javascript
运行
复制
#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时,不会将__VA_ARGS__包装在()中。

编辑:在尝试在实际项目中使用上述宏之后,我更新了它们。我还添加了一些可能会用到的其他相关宏(RECURSE_FIRST (仅浏览第一个条目)、RECURSE_LATTER (仅浏览第一个条目之后的后一个条目)及其_ESCAPE版本,以及RECURSE_SPLIT (浏览第一个条目,对其应用VARIADIC_FUNC_FIRST()宏,只浏览后一个条目,对这些条目应用VARIADIC_FUNC()宏,再次浏览第一个条目,对其应用VARIADIC_FUNC_END()宏,并将所有这些连接在一起...):

代码语言:javascript
运行
复制
//#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-宏:

代码语言:javascript
运行
复制
MyXMacro((a) (b) (57) (32))
MyXMacro((c) (d) (49) (32))

另请参阅:

(本文展示了一种比您通常看到的更简洁的方式:)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13614992

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档