可变的recursion预处理器macros – 有可能吗?
我遇到了一点理论上的问题。 在一段代码中,我维护着一组macros
#define MAX_OF_2(a, b) (a) > (b) ? (a) : (b) #define MAX_OF_3(a, b, c) MAX_OF_2(MAX_OF_2(a, b), c) #define MAX_OF_4(a, b, c, d) MAX_OF_2(MAX_OF_3(a, b, c), d) ...etc up to MAX_OF_8
我想要做的就是把它们replace成这样的东西:
/* Base case #1, single input */ #define MAX_OF_N(x) (x) /* Base case #2, two inputs */ #define MAX_OF_N(x, y) (x) > (y) ? (x) : (y) /* Recursive definition, arbitrary number of inputs */ #define MAX_OF_N(x, ...) MAX_OF_N(x, MAX_OF_N(__VA_ARGS__))
这当然不是有效的预处理器代码。
忽略这个特殊情况应该使用函数而不是预处理macros来解决 ,是否可以定义一个可变参数MAX_OF_N()macros?
为了清楚起见,最终的结果应该是一个macros,它接受任意数量的参数并评估其中最大的参数。 我有一个奇怪的感觉,这应该是可能的,但我不知道如何。
不,因为预处理器只在文件上进行一次“滑动”操作。 没有办法让它recursion地定义macros。
我见过的唯一代码就是这样的, 不是可变参数,而是使用用户必须传递的默认值:
x = MAX_OF_8 (a, b, -1, -1, -1, -1, -1, -1)
假设所有的值都是非负的。
内联函数至less应该给你相同的C ++。 正如你所说的,最好留给一个可变参数与printf()
类似的printf()
。
可以编写一个macros来评估它所调用的参数的数量。 (我找不到连接到我第一次看到它的地方。)所以,你可以写MAX_OF_N(),它可以随心所欲地工作,但是你仍然需要所有编号的macros,直到一些限制:
#define MAX_OF_1(a) (a) #define MAX_OF_2(a,b) max(a, b) #define MAX_OF_3(a,...) MAX_OF_2(a,MAX_OF_2(__VA_ARGS__)) #define MAX_OF_4(a,...) MAX_OF_2(a,MAX_OF_3(__VA_ARGS__)) #define MAX_OF_5(a,...) MAX_OF_2(a,MAX_OF_4(__VA_ARGS__)) ... #define MAX_OF_64(a,...) MAX_OF_2(a,MAX_OF_63(__VA_ARGS__)) // NUM_ARGS(...) evaluates to the literal number of the passed-in arguments. #define _NUM_ARGS2(X,X64,X63,X62,X61,X60,X59,X58,X57,X56,X55,X54,X53,X52,X51,X50,X49,X48,X47,X46,X45,X44,X43,X42,X41,X40,X39,X38,X37,X36,X35,X34,X33,X32,X31,X30,X29,X28,X27,X26,X25,X24,X23,X22,X21,X20,X19,X18,X17,X16,X15,X14,X13,X12,X11,X10,X9,X8,X7,X6,X5,X4,X3,X2,X1,N,...) N #define NUM_ARGS(...) _NUM_ARGS2(0, __VA_ARGS__ ,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) #define _MAX_OF_N3(N, ...) MAX_OF_ ## N(__VA_ARGS__) #define _MAX_OF_N2(N, ...) _MAX_OF_N3(N, __VA_ARGS__) #define MAX_OF_N(...) _MAX_OF_N2(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
现在MAX_OF_N(a,b,c,d,e)
将评估为max(a, max(b, max(c, max(d, e))))
。 (我已经testing了gcc 4.2.1。)
请注意,基本情况( MAX_OF_2
)在扩展中不会MAX_OF_2
重复其参数是非常重要的(这就是为什么我在此示例中将max
设置为max
)的原因。 否则,你会扩大每个级别的扩展长度,所以你可以想象会发生什么64个参数:)
您可能会考虑这种作弊行为,因为它不是recursion的,并且不会在预处理器中执行。 它使用GCC扩展。 它只适用于一种types。 但是,它是一个可变的MAX_OF_Nmacros:
#include <iostream> #include <algorithm> #define MAX_OF_N(...) ({\ int ra[] = { __VA_ARGS__ }; \ *std::max_element(&ra[0], &ra[sizeof(ra)/sizeof(int)]); \ }) int main() { int i = 12; std::cout << MAX_OF_N(1,3,i,6); }
哦,是的,由于初始化列表中潜在的variablesexpression式,我不认为这是相当于(使用自己的函数来避免std :: max_element)将在C89工作。 但我不确定可变macros是否在C89中。
我认为这是“唯一的一种”限制。 虽然有点多毛:
#include <iostream> #include <algorithm> #define MAX_OF_N(x, ...) ({\ typeof(x) ra[] = { (x), __VA_ARGS__ }; \ *std::max_element(&ra[0], &ra[sizeof(ra)/sizeof(ra[0])]); \ }) int main() { int i = 12; std::cout << MAX_OF_N(i+1,1,3,6,i); }
我认为,即使您可以recursion地扩展macros,在效率方面也会有一个小小的问题…当macros扩展时,如果MAX_OF_[N-1]
更大,那么您必须评估它再次从头开始。
这是一个愚蠢愚蠢的答案,可能没有人会喜欢xD
文件“source.c”
#include "my_macros.h" ...
文件“Makefile”
myprogram: source.c my_macros.h gcc source.c -o myprogram my_macros.h: make_macros.py python make_macros.py > my_macros.h
文件“make_macros.py”
def split(l): n = len(l) return l[:n/2], l[n/2:] def gen_param_seq(n): return [chr(i + ord("A")) for i in range(n)] def make_max(a, b): if len(a) == 1: parta = "("+a[0]+")" else: parta = make_max(*split(a)) if len(b) == 1: partb = "("+b[0]+")" else: partb = make_max(*split(b)) return "("+parta +">"+partb+"?"+parta+":"+partb+")" for i in range(2, 9): p = gen_param_seq(i) print "#define MAX_"+str(i)+"("+", ".join(p)+") "+make_max(*split(p))
那么你将会定义这些漂亮的macros:
#define MAX_2(A, B) ((A)>(B)?(A):(B)) #define MAX_3(A, B, C) ((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C))) #define MAX_4(A, B, C, D) (((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D))) #define MAX_5(A, B, C, D, E) (((A)>(B)?(A):(B))>((C)>((D)>(E)?(D):(E))?(C):((D)>(E)?(D):(E)))?((A)>(B)?(A):(B)):((C)>((D)>(E)?(D):(E))?(C):((D)>(E)?(D):(E)))) #define MAX_6(A, B, C, D, E, F) (((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C)))>((D)>((E)>(F)?(E):(F))?(D):((E)>(F)?(E):(F)))?((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C))):((D)>((E)>(F)?(E):(F))?(D):((E)>(F)?(E):(F)))) #define MAX_7(A, B, C, D, E, F, G) (((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C)))>(((D)>(E)?(D):(E))>((F)>(G)?(F):(G))?((D)>(E)?(D):(E)):((F)>(G)?(F):(G)))?((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C))):(((D)>(E)?(D):(E))>((F)>(G)?(F):(G))?((D)>(E)?(D):(E)):((F)>(G)?(F):(G)))) #define MAX_8(A, B, C, D, E, F, G, H) ((((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D)))>(((E)>(F)?(E):(F))>((G)>(H)?(G):(H))?((E)>(F)?(E):(F)):((G)>(H)?(G):(H)))?(((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D))):(((E)>(F)?(E):(F))>((G)>(H)?(G):(H))?((E)>(F)?(E):(F)):((G)>(H)?(G):(H))))
而最好的事情是…它能起作用
如果你在C ++中走这条路,那么看看模板元编程 。 这不是很好,它可能无法解决你确切的问题,但它会处理recursion。
首先,macros不会扩张。 虽然macros可以通过为每个recursion级别创build一个macros然后推导recursion级别来重新授予权限。 但是,所有这些重复和推断recursion都由Boost.Preprocessor库负责。 因此,您可以使用高阶折叠macros来计算最大值:
#define MAX_EACH(s, x, y) BOOST_PP_IF(BOOST_PP_GREATER_EQUAL(x, y), x, y) #define MAX(...) BOOST_PP_SEQ_FOLD_LEFT(MAX_EACH, 0, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) MAX(3, 6, 8) //Outputs 8 MAX(4, 5, 9, 2) //Outputs 9
现在,这将理解0-256之间的字面数。 它不会在C ++variables或expression式上工作,因为C预处理器不理解C ++。 它只是纯文本replace。 但是C ++提供了一个称为“函数”的function,可以用于C ++expression式,并且可以用它来计算最大值。
template<class T> T max(T x, T y) { return x > y ? x : y; } template<class X, class... T> auto max(X x, T ... args) -> decltype(max(x, max(args...))) { return max(x, max(args...)); }
现在,上面的代码需要一个C ++ 11编译器。 如果您正在使用C ++ 03,则可以创build函数的多个重载以模拟可变参数。 此外,我们可以使用预处理器为我们生成这个重复的代码(也就是说它是什么)。 所以在C ++ 03中,你可以这样写:
template<class T> T max(T x, T y) { return x > y ? x : y; } #define MAX_FUNCTION(z, n, data) \ template<class T> \ T max(T x, BOOST_PP_ENUM_PARAMS(n, T x)) \ { \ return max(x, max(BOOST_PP_ENUM_PARAMS(n, x)));\ } BOOST_PP_REPEAT_FROM_TO(2, 64, MAX_FUNCTION, ~)