Простой вопрос, на который я не нашел ответа в сети. В макросах с переменными аргументами, как найти количество аргументов? Я согласен с препроцессором ускорения, если у него есть решение.
Если это имеет значение, я пытаюсь преобразовать переменное количество аргументов макроса, чтобы увеличить последовательность препроцессора, список или массив для дальнейшей обработки.
c++
c
c-preprocessor
variadic-macros
Anycorn
источник
источник
__typeof__
чтобы заставить его работать хотя бы на некоторых компиляторахОтветы:
Это фактически зависит от компилятора и не поддерживается никакими стандартами.
Однако здесь у вас есть реализация макроса , которая считает:
источник
#define EXPAND(x) x
#define PP_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) N
#define PP_NARG(...) EXPAND(PP_ARG_N(__VA_ARGS__, 9,8,7,6,5,4,3,2,1,0))
``PP_NARG()
не возвращает 0. РешенияGET_ARG_COUNT()
&Y_TUPLE_SIZE()
работают.PP_NARG()
не возвращает 0» ... не обязательно проблема. Можно сказать , чтоPP_NARG()
должен возвращать 1 по той же причинеPP_NARG(,)
должна возвращать 2. Детектирование 0 действительно может быть удобно в некоторых случаях, но решения , кажется, быть или менее общим (требуя , чтобы первый маркер будет pasteable, которые могут или не могут быть в порядке в зависимости от того, для чего вы его используете) или конкретной реализации (например, требуется трюк gnu с удалением запятой и вставкой).Обычно я использую этот макрос, чтобы найти несколько параметров:
Полный пример:
Это полностью действующий код C99. Однако у него есть один недостаток - вы не можете вызвать макрос
SUM()
без параметров, но у GCC есть решение - см. Здесь .Итак, в случае GCC вам нужно определить такие макросы:
и он будет работать даже с пустым списком параметров
источник
sizeof(int) != sizeof(void *)
?{__VA_ARGS__}
наint[]
, это простоint[]
, независимо от фактического содержания__VA_ARGS__
##
VS2017 не требуется, поскольку пустое значение__VA_ARGS__
автоматически удаляет любую предшествующую запятую.Если вы используете C ++ 11 и вам нужно значение как константа времени компиляции C ++, очень элегантным решением будет следующее:
Обратите внимание: подсчет происходит полностью во время компиляции, и значение может использоваться всякий раз, когда требуется целое число времени компиляции, например, как параметр шаблона для std :: array.
источник
sizeof((int[]){__VA_ARGS__})/sizeof(int)
предложенного выше, он работает, даже когда нельзя привести все аргументыint
.#define NUM_ARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value
Для удобства здесь есть реализация, которая работает для аргументов от 0 до 70 и работает в Visual Studio, GCC и Clang . Я считаю, что он будет работать в Visual Studio 2010 и более поздних версиях, но тестировал его только в VS2013.
источник
__VA_ARGS__
» (которая в C ++ технически является (почти универсальным, де-факто стандартом) расширением компилятора до C ++ 20). Большинство (? Все) компиляторы позволяют нулевую длину, но дроссель на задней запятой , если список является пустым (и перегрузками в##
качестве проты__VA_OPT__
, чтобы удалить запятую в данном случае); Версия MSVC о продлении просто не подавиться запятой (но будет задыхаться на перегруженный##
). Сравните MSVCunused, __VA_ARGS__
с не-MSVC0, ## __VA_ARGS__
; ни то, ни другое не вернее, проблема в том, что они разные.Есть несколько решений C ++ 11 для определения количества аргументов во время компиляции, но я удивлен, увидев, что никто не предложил ничего более простого, чем:
Это также не требует включения
<tuple>
заголовка.источник
VA_COUNT(&,^,%)
. Кроме того, если вы считаете через функцию, я не вижу смысла делать макрос.это работает с 0 аргументами с gcc / llvm. [ссылки тупые]
Visual Studio, похоже, игнорирует оператор ##, используемый для использования пустого аргумента. Вы, вероятно, можете обойти это с помощью чего-то вроде
источник
##__VA_ARGS__
есть запятую перед__VA_ARGS__
пустым, это расширение GCC. Это нестандартное поведение.С расширением msvc:
Работает для 0 - 32 аргументов. Этот предел можно легко увеличить.
РЕДАКТИРОВАТЬ: упрощенная версия (работает в VS2015 14.0.25431.01 Update 3 и gcc 7.4.0) до 100 аргументов для копирования и вставки:
источник
Y_TUPLE_SIZE("Hello")
делает его совершенно невозможным. Я согласен с @osirisgothra.Я предполагаю, что каждый аргумент VA_ARGS будет разделен запятыми. Если это так, я думаю, что это должен работать как довольно чистый способ сделать это.
У меня работал над godbolt для clang 4 и GCC 5.1. Это будет вычисляться во время компиляции, но не будет оцениваться препроцессором. Поэтому, если вы пытаетесь сделать что-то вроде FOR_EACH , это не сработает.
источник
NUMARGS(hello, world = 2, ohmy42, !@#$%^&*()-+=)
!!! Каждая строка аргумент не может иметь какие - то другие символы , как ,','
хотяint count = NUMARGS( foo(1, 2) );
производит 2, а не 1. godbolt.org/z/kpBuOmздесь простой способ подсчитать 0 или более аргументов VA_ARGS , мой пример предполагает максимум 5 переменных, но вы можете добавить больше, если хотите.
источник
VA_ARGS_NUM
использовании макроса: if I have#define TEST
(т.е. пустоTEST
) иVA_ARGS_NUM(TEST)
не возвращает 0 (ноль) при использовании в#if
:(Вы можете натягивать и подсчитывать токены:
источник
Препроцессор Boost фактически имеет это в Boost 1.49, поскольку
BOOST_PP_VARIADIC_SIZE(...)
. Работает до 64-го размера.Под капотом это в основном то же самое, что и ответ Корнеля Киселевича .
источник
__VA_OPT__
или расширений компилятора для##__VA_ARGS__
удаления предыдущей запятой, например: godbolt.org/z/X7OvnKЯ нашел ответы здесь все еще неполными.
Самая близкая переносимая реализация, которую я нашел отсюда, это: Препроцессор C ++ __VA_ARGS__ количество аргументов
Но он не работает с нулевыми аргументами в GCC без хотя бы
-std=gnu++11
параметра командной строки.Поэтому я решил объединить это решение с этим: https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
https://godbolt.org/z/3idaKd
c++11
,msvc 2015
,gcc 4.7.1
,clang 3.0
источник