Inspired by some answers from this question, I've got the following C++ code that tries to emulate default function arguments with macros.
// do_func(int a, int b, int c = 0);
void do_func(int, int, int);
#define FUNC_2_ARGS(a, b) do_func(a, b, 0)
#define FUNC_3_ARGS(a, b, c) do_func(a, b, c)
#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define MACRO_CHOOSER(...) \
GET_4TH_ARG(__VA_ARGS__, FUNC_3_ARGS, \
FUNC_2_ARGS, not_func)
#define func(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
func(1, 2)
func(1, 2, 3)
The idea being that MACRO_CHOOSER(__VA_ARGS__)
should expand to either GET_4TH_ARG(1, 2, 3, FUNC_3_ARGS, FUNC_2_ARGS, not_func
which further expands to FUNC_2_ARGS
which now takes (1, 2)
and results into do_func(1, 2, 0)
. Similarly, if three arguments are passed, FUNC_3_ARGS
is chosen.
It works perfectly well with both gcc and clang when run like g++ -E x.cpp
:
# 0 "x.cpp"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "x.cpp"
void do_func(int, int, int);
# 13 "x.cpp"
do_func(1, 2, 0)
do_func(1, 2, 3)
However, Visual C++ 19.32's seems to apply GET_4TH_ARG
before expanding __VA_ARGS__
and always gets the following result when run with cl /P x.cpp
:
#line 1 "x.cpp"
void do_func(int, int, int);
// snip
not_func(1, 2)
not_func(1, 2, 3)
What behavior is correct here according to C++ or C standard? Is it even a legal C++? Or maybe it's legal C, but not C++?
Of course, there are better macros and BOOST_PP
, but I'm wondering what's the logic here.