Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic recursive preprocessor macros - is it possible?

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.

like image 601
Christoffer Avatar asked May 05 '09 12:05

Christoffer


People also ask

Is recursion possible in macros?

Macros can't be recursive.

What is recursive macro expansion in system software?

21.5. 1 Macro Expansion Is Recursive Notice that when sendmail recursively expands a macro, it does so one macro at a time, always expanding the leftmost macro first.

What does ## mean in macro?

The double-number-sign or token-pasting operator (##), which is sometimes called the merging or combining operator, is used in both object-like and function-like macros. It permits separate tokens to be joined into a single token, and therefore, can't be the first or last token in the macro definition.

What is __ Va_opt __?

The key to making this work is a new pre-processor feature in C++20, __VA_OPT__(x) , which expands to x when a variable-argument macro has more than zero arguments and to nothing otherwise.


2 Answers

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:

#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__) 

Now MAX_OF_N(a,b,c,d,e) will evaluate to max(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 put max 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 :)

like image 123
DS. Avatar answered Sep 21 '22 09:09

DS.


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:

#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); } 

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:

#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); } 
like image 26
Steve Jessop Avatar answered Sep 23 '22 09:09

Steve Jessop