Что означает тильда (~) в макросах?

На этом сайте код показывает вызовы макросов. используя тильду в скобках:

HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (~))
//                                          ^^^

Что это значит/делает? Я подозреваю, что это просто пустой спор, но я не уверен. Может быть, это специфично для C (99), например, __VA_ARGS__ специфично для C99 и существует в C++?


person Xeo    schedule 25.06.2011    source источник
comment
~ — это побитовое дополнение, как вы, наверное, знаете. Для меня это просто заполнитель. Не думаю, что это имеет какое-то особое значение.   -  person Assaf Lavie    schedule 25.06.2011


Ответы (3)


На вводной странице Boost.Preprocessor пример приведен в разделе A.4.1.1 Горизонтальное повторение.

#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

Объяснение приведено ниже:

Процесс генерации кода запускается вызовом BOOST_PP_REPEAT, макроса высшего порядка, который неоднократно вызывает макрос, названный вторым аргументом (TINY_size). Первый аргумент указывает количество повторных вызовов, а третий может быть любыми данными; он передается без изменений вызываемому макросу. В данном случае TINY_size не использует эти данные, поэтому выбор передачи ~ был произвольным. [5]

(выделено мной)

И есть примечание:

[5] ~ не совсем произвольный выбор. И @, и $ могли бы быть хорошим выбором, за исключением того, что технически они не являются частью базового набора символов, который должны поддерживать реализации C++. Такой идентификатор, как ignored, может быть подвергнут расширению макроса, что приведет к неожиданным результатам.

Таким образом, тильда — это просто заполнитель, потому что аргумент требуется, но в нем нет необходимости. Поскольку любой пользовательский идентификатор Wannabe может быть расширен, вам нужно использовать что-то еще.

Оказывается, что ~ в значительной степени не используется (двоичное отрицание не так часто используется) по сравнению, например, с + или -, поэтому вероятность путаницы невелика. Как только вы определились с этим, постоянное его использование придает тильде новое значение; например, использование operator<< и operator>> для потоковой передачи данных стало идиомой C++.

person Matthieu M.    schedule 25.06.2011
comment
Мой +1, это очень хорошая находка! Я не мог найти стандартную ссылку для этого и изо всех сил пытался понять это полностью. Ваш ответ прекрасно все объясняет. - person Alok Save; 25.06.2011
comment
Как именно расширение макроса может привести к неожиданным результатам, учитывая, что аргумент (и, следовательно, аргумент, расширенный макросом) не появляется в расширении TINY_size? - person Random832; 26.06.2011
comment
@ Random832: Расширение макроса всегда было для меня немного размытым, но я думаю, например, что #define unused a, b теперь TINY_size будет вызываться с 4 аргументами вместо 3, и, таким образом, код будет отклонен. - person Matthieu M.; 26.06.2011

~ ничего не делает. Почти любой другой контент внутри этих скобок будет работать так же.

Суть этого трюка в том, чтобы проверить, находится ли _TRIGGER_PARENTHESIS_ рядом с (~) в расширении _TRIGGER_PARENTHESIS_ __VA_ARGS__ (~). В любом случае HAS_COMMA(...) расширяет свои аргументы до 0 или 1.

person aschepler    schedule 25.06.2011
comment
Мне интересно, как это будет работать с аргументом, который сам является макросом.. _TRIGGER_PARENTHESIS_ MYMACRO (~). - person Xeo; 25.06.2011
comment
почему он использовал (~), а не (+) или что-то еще? - person Johannes Schaub - litb; 25.06.2011
comment
@Johannes: на странице «Введение в Boost.Preprocessor» есть рассуждение, которое я привел ниже. Цель состоит в том, чтобы использовать действительный, но редкий токен препроцессора. - person Matthieu M.; 25.06.2011

Проверяемые аргументы помещаются между макросом и его скобками, макрос срабатывает только в том случае, если аргументы пусты:

_TRIGGER_PARENTHESIS_ __VA_ARGS__ (~)

ПРИМЕЧАНИЕ. На самом деле в самой ссылке, которую вы разместили, указано это. Я проверю ссылку на это в стандарте.

person Alok Save    schedule 25.06.2011
comment
На заметку: Да, перечитав его несколько раз после публикации вопроса, он пришел ко мне... Это не первый раз, когда я задавал вопрос, и через несколько минут пришло решение... :( Также , ваше самое первое предложение как-то не имеет прямого отношения к вопросу. - person Xeo; 25.06.2011
comment
@Xeo: Извините, боюсь, я недостаточно хорошо понял контекст, чтобы опубликовать развернутый ответ, были недоработки относительно того, почему только ~, и я долго безуспешно разорял стандарт. Отлично, нашел @Matthieu M., но я могу спать спокойно! - person Alok Save; 25.06.2011
comment
Спасибо за ваши усилия в любом случае. :) - person Xeo; 25.06.2011