Portably detect __VA_OPT__ support?

Eric Niebler picture Eric Niebler · Dec 31, 2017 · Viewed 7.1k times · Source

In C++20, the preprocessor supports __VA_OPT__ as a way to optionally expand tokens in a variadic macro if the number of arguments is greater than zero. (This obviates the need for the ##__VA_ARGS__ GCC extension, which is a non-portable and ugly hack.)

Clang SVN has implemented this feature, but they haven't added a feature test macro for it. Can any clever preprocessor hacker figure out a way to detect the presence or absence of __VA_OPT__ support without causing a hard error or a portability warning?

Answer

cpplearner picture cpplearner · Dec 31, 2017

Inspired by chris's answer.1

#define PP_THIRD_ARG(a,b,c,...) c
#define VA_OPT_SUPPORTED_I(...) PP_THIRD_ARG(__VA_OPT__(,),true,false,)
#define VA_OPT_SUPPORTED VA_OPT_SUPPORTED_I(?)

If __VA_OPT__ is supported, VA_OPT_SUPPORTED_I(?) expands to PP_THIRD_ARG(,,true,false,), so the third argument is true; otherwise, VA_OPT_SUPPORTED_I(?) expands to PP_THIRD_ARG(__VA_OPT__(,),true,false,), the third argument is false.


Edit: As Edward Diener's answer notes, GCC >= 8 issues a warning or error whenever it sees __VA_OPT__, if -pedantic mode is on and __VA_OPT__ is not enabled (e.g. in -std=c++17). This is GCC bug 98859. One might have to special-case GCC to avoid this diagnostic.

#if __cplusplus <= 201703 && defined __GNUC__ \
  && !defined __clang__ && !defined __EDG__ // These compilers pretend to be GCC
#  define VA_OPT_SUPPORTED false
#endif

1. As chris mentions, if __VA_OPT__(,) expands to ,, there will be 2 empty arguments, otherwise there will be 1 argument. So it's possible to test PP_NARG(__VA_OPT__(,)) == 2, where PP_NARG is a macro to count the number of arguments. To adapt to this test, the definition of PP_NARG can be simplified and inlined.