38

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++?

a3f
  • 8,517
  • 1
  • 41
  • 46
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • 1
    ~ is the bitwise complement, as you probably know. Looks like just a placeholder to me. I don't think it has any special meaning. – Assaf Lavie Jun 25 '11 at 15:52

3 Answers3

32

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.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • My +1, That is a very good find! I couldn't find any standard reference for it & was struggling to understand it thoroughly. Your answer sorts it out nicely. – Alok Save Jun 25 '11 at 17:25
  • How exactly could macro expansion lead to unexpected results, considering that the argument (and therefore, the macro-expanded argument) doesn't appear in the expansion of TINY_size? – Random832 Jun 25 '11 at 21:43
  • 1
    @Random832: Macro expansion has always been a bit blurry to me, but I think for example of `#define unused a, b`, now `TINY_size` would get called with 4 arguments, instead of 3, and thus the code would get rejected. – Matthieu M. Jun 26 '11 at 09:39
4

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.

aschepler
  • 70,891
  • 9
  • 107
  • 161
3

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.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • On your note: Yeah, after rereading it a few times after posting the question it came to me... This isn't the first time that I asked a question and some minutes later the solution came to mind... :( Also, your very first sentence somehow doesn't seem to relate to the question directly. – Xeo Jun 25 '11 at 15:57
  • @Xeo: Sorry, I am afraid I did not understand the context thoroughly enough to post an elaborated answer, there were loose ends as to why only `~` and I kept ravaging the standard for long with no success. Great, find by @Matthieu M. though, I can sleep peacefully! – Alok Save Jun 25 '11 at 17:21
  • Thanks for your effor anyways. :) – Xeo Jun 25 '11 at 17:29