First we define a macro for counting how many arguments were passed.
#define COUNTARGS(...) GET21ST(__VA_ARGS__,COUNTDOWN())
#define COUNTDOWN() 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define GET21ST(a20,a19,a18,a17,a16,a15,a14,a13,a12,a11,a10,a9,a8,a7,a6,a5,a4,a3,a2,a1,a,...) a
However the expansion of GET21ST
will happen before COUNTDOWN
, so we need to force the precompiler to evaluate these in a different order. Fortunately this answer provides us with a mechinism to do just that.
#define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__
#define VANISH()
#define OBSTRUCT(...) __VA_ARGS__ VANISH()
#define COUNTDOWN() 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define COUNTARGS(...) EVAL(OBSTRUCT(GET21ST)(__VA_ARGS__,COUNTDOWN()))
#define GET21ST(a20,a19,a18,a17,a16,a15,a14,a13,a12,a11,a10,a9,a8,a7,a6,a5,a4,a3,a2,a1,a,...) a
Now we are able to automatically select the correct macro from a list based on the number of arguments.
Todo this we will need a collection of macros to select from.
#define MAX_1(n) n
#define MAX_2(a,b) ((a)>(b)?(a):(b))
#define MAX_3(a,...) MAX_2(a,MAX_2(__VA_ARGS__))
#define MAX_4(a,...) MAX_2(a,MAX_3(__VA_ARGS__))
...
#define MAX_20(a,...) MAX_2(a,MAX_19(__VA_ARGS__))
We will also need some way of generating new macro calls.
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
From here we can define a generic MAX
macro that will call the appropriate specific macro.
#define MAX(...) CAT(MAX_,COUNTARGS(__VA_ARGS__))(__VA_ARGS__)
And testing on https://godbolt.org/ with the -E
flag.
This:
MAX(2,3,4,1)
Results in:
(2>(3>(4>1?4:1)?3:(4>1?4:1))?2:(3>(4>1?4:1)?3:(4>1?4:1)))
NOTE
For those using C++, this is a simpler and safer macro.
#include <vector>
#include <algorithm>
#define MAX(...) [](auto v)constexpr{ \
return *std::max_element(v.begin(), \
v.end()); \
}(std::vector<float>{__VA_ARGS__})
Or even better, just use algorithm
yourself.