可变递归预处理器宏 - 可能吗?
我遇到了一些理论问题。 在我维护的一段代码中,有一组宏,例如
#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
我想做的是将它们替换为如下内容:
/* 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__))
...当然,这不是有效的预处理器代码。
忽略这种特殊情况可能应该使用函数而不是预处理器宏来解决,是否可以定义可变参数 MAX_OF_N() 宏?
为了清楚起见,最终结果应该是一个单独的宏,它采用任意数量的参数并计算其中最大的参数。 我有一种奇怪的感觉,这应该是可能的,但我不知道如何实现。
I've run into a little theoretical problem. In a piece of code I'm maintaining there's a set of macros like
#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
What I'd like to do is replace them with something like this:
/* 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__))
...which, of course, is not valid preprocessor code.
Ignoring that this particular case should probably be solved using a function rather than a preprocessor macro, is it possible to define a variadic MAX_OF_N() macro?
Just for clarity, the end result should be a single macro that takes an arbitrary number of parameters and evaluates to the largest of them. I've got an odd feeling that this should be possible, but I'm not seeing how.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
可以编写一个计算其调用参数数量的宏。 (我找不到第一次看到它的链接。)所以你可以编写 MAX_OF_N() ,它可以按照你的意愿工作,但你仍然需要所有编号的宏,直到达到一定的限制:
现在
MAX_OF_N(a,b,c,d,e)
将计算为max(a, max(b, max(c, max(d, e))))
。 (我已经在 gcc 4.2.1 上进行了测试。)请注意,基本情况 (
MAX_OF_2
) 在扩展中不要多次重复其参数(这就是为什么我将在本例中为 >max
)。 否则,您将在每个级别将扩展的长度加倍,因此您可以想象 64 个参数会发生什么:)It's possible to write a macro that evaluates to the number of arguments it's called with. (I couldn't find a link to the place where I first saw it.) So you could write MAX_OF_N() that would work as you'd like, but you'd still need all the numbered macros up until some limit:
Now
MAX_OF_N(a,b,c,d,e)
will evaluate tomax(a, max(b, max(c, max(d, e))))
. (I've tested on gcc 4.2.1.)Note that it's critical that the base case (
MAX_OF_2
) doesn't repeat its arguments more than once in the expansion (which is why I putmax
in this example). Otherwise, you'd be doubling the length of the expansion for every level, so you can imagine what will happen with 64 arguments :)您可能会认为这是作弊,因为它不是递归的,并且它不在预处理器中完成工作。 它使用 GCC 扩展。 而且它只适用于一种类型。 然而,它是一个可变参数 MAX_OF_N 宏:
哦,是的,并且由于初始值设定项列表中存在潜在的变量表达式,我认为与此等效的内容(使用其自己的函数来避免 std::max_element)不会在C89。 但我也不确定 C89 中是否有可变参数宏。
我认为这是绕过“只有一种类型”限制的东西。 不过,它变得有点毛茸茸的:
You might consider this cheating, since it is not recursive and it doesn't do the work in the preprocessor. And it uses a GCC extension. And it only works for one type. It is, however, a variadic MAX_OF_N macro:
Oh yes, and because of the potential variable expression in the initializer list, I don't think that an equivalent of this (using its own function to avoid std::max_element) would work in C89. But I'm not sure variadic macros are in C89 either.
Here's something that I think gets around the "only one type" restriction. It's getting a bit hairy, though:
不,因为预处理器只对文件进行一次“滑动”。 没有办法让它递归地定义宏。
我见过的唯一执行类似操作的代码不是可变参数,而是使用用户必须传递的默认值:
假设所有值都是非负的。
内联函数至少应该为 C++ 提供相同的功能。 正如您所说,最好留给具有类似于 printf() 的可变参数的函数。
No, because the preprocessor only takes one "swipe" at the file. There's no way to get it to recursively define macros.
The only code that I've seen do something like this was not variadic, but used default values the user had to pass:
assuming all values were non-negative.
Inline functions should give you the same for C++ at least. As you state, it's probably better left to a function with variable arguments similar to
printf()
.我认为,即使您可以递归地扩展宏,您的方法在效率方面也会存在一个小问题...当扩展宏时,如果
MAX_OF_[N-1]
为更大,那么你必须从头开始重新评估它。这是一个愚蠢而愚蠢的答案,可能没有人会喜欢 xD
文件“source.c”
文件“Makefile”
文件“make_macros.py”
然后你将定义那些漂亮的宏:
而最好的事情是......有效^_^
I think that, even if you could expand macros recursively, there would be one little problem with your approach in terms of efficiency... when the macros are expanded, if the
MAX_OF_[N-1]
is greater, then you have to evaluate it again from scratch.Here is a silly and stupid answer that probably no one will like xD
file "source.c"
file "Makefile"
file "make_macros.py"
then you'll have those pretty macros defined:
and the best thing about it is that... it works ^_^
如果您打算使用 C++ 走这条路,请查看模板元编程< /a>. 它并不漂亮,它可能无法解决您的确切问题,但它可以处理递归。
If you're going down this road in C++, take a look at template metaprogramming. It's not pretty, and it may not solve your exact problem, but it will handle recursion.
首先,宏不会递归扩展。 尽管如此,通过为每个递归级别创建一个宏,然后推导递归级别,宏可以具有可重入性。 然而,所有这些重复和推论递归都是由 Boost.Preprocessor 库。 因此,您可以使用高阶折叠宏来计算最大值:
现在,这将理解 0-256 之间的字面数字。 它不适用于 C++ 变量或表达式,因为 C 预处理器不理解 C++。 它只是纯文本替换。 但是 C++ 提供了一种称为“函数”的功能,该功能适用于 C++ 表达式,您可以使用它来计算最大值。
现在,上面的代码确实需要 C++11 编译器。 如果您使用 C++03,则可以创建函数的多个重载以模拟可变参数。 此外,我们可以使用预处理器为我们生成这些重复的代码(这就是它的用途)。 所以在C++03中,你可以这样写:
First, macros don't expand recusrsively. Although, macros can have reentrance by creating a macro for each recursion level and then deducing the recursion level. However, all this repetition and deducing recursion, is taken care of by the Boost.Preprocessor library. You can therefore use the higher order fold macro to calculate the max:
Now, this will understand literal numbers between 0-256. It wont work on C++ variables or expression, because the C preprocessor doesn't understand C++. Its just pure text replacement. But C++ provides a feature called a "function" that will work on C++ expressions, and you can use it to calculate the max value.
Now, the code above does require a C++11 compiler. If you are using C++03, you can create multiple overloads of the function in order to simulate variadic parameters. Furthermore, we can use the preprocessor to generate this repetitive code for us(thats what it is there for). So in C++03, you can write this:
这里有一个很好的递归示例,
“hack”是在预处理器中有一个中间步骤,使其认为在给定步骤中,define 不会被任何其他内容替换。
There's a nice recursion example here
The "hack" is to have a mid-step in the preprocessor to make it think that the define is not replaced by anything else at a given step.