Seen on this site, the code shows macro invocations using a tilde in parentheses:
HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (~))
// ^^^
What does it mean / do? I suspect it to just be an empty argument, but I'm not sure. Is it maybe specific to C(99) like the __VA_ARGS__
is specific to C99 and existent in C++?
On the introduction page of Boost.Preprocessor, an example is given in A.4.1.1 Horizontal Repetition
#define TINY_print(z, n, data) data
#define TINY_size(z, n, unused) \
template <BOOST_PP_ENUM_PARAMS(n, class T)> \
struct tiny_size< \
BOOST_PP_ENUM_PARAMS(n,T) \
BOOST_PP_COMMA_IF(n) \
BOOST_PP_ENUM( \
BOOST_PP_SUB(TINY_MAX_SIZE,n), TINY_print, none) \
> \
: mpl::int_<n> {};
BOOST_PP_REPEAT(TINY_MAX_SIZE, TINY_size, ~) // Oh! a tilde!
#undef TINY_size
#undef TINY_print
An explanation is provided below:
The code generation process is kicked off by calling
BOOST_PP_REPEAT
, a higher-order macro that repeatedly invokes the macro named by its second argument (TINY_size
). The first argument specifies the number of repeated invocations, and the third one can be any data; it is passed on unchanged to the macro being invoked. In this case,TINY_size
doesn't use that data, so the choice to pass~
was arbitrary. [5]
(emphasis mine)
And there is the note:
[5]
~
is not an entirely arbitrary choice. Both@
and$
might have been good choices, except that they are technically not part of the basic character set that C++ implementations are required to support. An identifier like ignored might be subject to macro expansion, leading to unexpected results.
The tilde, therefore, is simply a place holder because an argument is required, but none is necessary. Since any user-defined identifier wannabe could be expanded, you need to use something else.
It turns out that ~
is pretty much unused (binary negation is not that often called) in comparison to +
or -
for example, so there is little chance of confusion. Once you've settled on this, using it consistently gives it a new meaning to the tilde; like using operator<<
and operator>>
for streaming data has become a C++ idiom.
The ~
does nothing. Almost any other content inside those parentheses would work the same.
The lynchpin of this trick is to test whether _TRIGGER_PARENTHESIS_
is next to (~)
in the expansion of _TRIGGER_PARENTHESIS_ __VA_ARGS__ (~)
. Either way, HAS_COMMA(...)
expands its arguments to either 0
or 1
.
The arguments to be tested is placed between the macro and its parenthesis, the macro only triggers if the arguments are empty:
_TRIGGER_PARENTHESIS_ __VA_ARGS__ (~)
NOTE: Actually the very link you posted states it. I will check for a reference to this in the standard.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With