Как объединить все аргументы вариативного макроса в строку в кавычках?
Ниже приведено определение невариативного макроса, который объединяет 2 аргумента в строку в кавычках:
#define TO_STRING(x) #x
#define CONCAT_STRINGIFY(x,y) TO_STRING(x##y)
Вызов этого макроса следующим образом:
CONCAT_STRINGIFY(AAA,BBB)
... производит следующий вывод:
"AAABBB"
Как сделать макрос CONCAT_STRINGIFY(...) вариативным, чтобы он принимал произвольное количество аргументов?
P.S.
Решение может использовать только препроцессор C или C++ и никакие сторонние библиотеки
Кстати: я не передаю вывод препроцессора компилятору C/C++.
Ой, макро ад! Зачем вам это нужно, молитесь?
@Barmar: то, что вы связали, имеет определяемый пользователем разделитель. В моем вопросе такого разделителя нет. Это делает его проще.
Вот почему я сказал, что это похоже, а не совсем то же самое. Разве вы не можете просто использовать ту же технику без разделителя?
Обратите внимание, что для вывода c/c++ вы можете просто вызывать TO_STRING() для каждого аргумента. В c/c++ "abc" "def" допустимо и совпадает с "abcdef".
@Barmar: Нет, мне действительно не нравится это решение. Он имеет верхний предел для аргументов, вручную перечисляет макросы без переменных и является громоздким. Вскоре я буду использовать отдельные макросы CONCAT_STRINGIFY_n для другого количества аргументов, чем метод, показанный в другом посте. Я надеюсь, что кто-то найдет лучшее решение, воспользовавшись тем фактом, что мне не нужен «определяемый пользователем разделитель».
Вся причина, по которой это делается таким образом, заключается в том, что это единственный способ сделать это в препроцессоре. Это не язык общего назначения, в нем нет циклов.
@lorro: решение может использовать только препроцессор C/C++. Это не для предварительной обработки кода C. Я специально указал, что он должен использовать ТОЛЬКО препроцессор. Он не может использовать компилятор C. Без компилятора соседние строки в кавычках НЕ БУДУТ объединены.
Это рабочий хак: #define CONCAT_STRINGIFY(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) TO_STRING(A##B##C##D##E##F##G##H##I##J##K##L##M##N##O##P##Q##R##S##T##U##V##W##X##Y##Z) но он требует традиционного препроцессора /Zc:preprocessor- и генерирует предупреждение C4003. Я не могу отключить это предупреждение в MSVC с помощью #pragma warning( disable : 4003 ), и даже если бы я мог, все директивы #pragma ... остаются в выводе после предварительной обработки, что неприемлемо, поскольку я не передаю вывод препроцессора компилятору C.





Два решения.
1.
На основе обычного шаблона COUNT, который описан в ответе на другой вопрос (в комментариях) от Jarod42:
Во-первых, вы определяете макросы подсчета аргументов. Вы можете добавить произвольное количество аргументов (до предела компилятора) в COUNT_N и числа в определении COUNT. У него будут числа в порядке убывания после __VA_ARGS__, поэтому он возвращает количество аргументов:
#define COUNT_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define COUNT(...) COUNT_N(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
// Warning: COUNT() return 1 (as COUNT(A)) :-/
Тогда вам понадобятся обычные определения IDENTITY и APPLY:
#define IDENTITY(N) N
#define APPLY(macro, ...) IDENTITY(macro(__VA_ARGS__))
Наконец, вы добавляете диспетчер на основе количества аргументов. К сожалению, вам нужно создать кейс для каждого количества аргументов; если хотите, вы можете сгенерировать этот код (да, даже с препроцессором, но в отдельном прогоне):
#define CONCAT_STRINGIFY_DISPATCH(N) CONCAT_STRINGIFY ## N
#define TO_STRING(X) #X
#define CONCAT_STRINGIFY1(A) TO_STRING(A)
#define CONCAT_STRINGIFY2(A, B) TO_STRING(A ## B)
#define CONCAT_STRINGIFY3(A, B, C) TO_STRING(A ## B ## C)
#define CONCAT_STRINGIFY4(A, B, C, D) TO_STRING(A ## B ## C## D)
// ...
#define CONCAT_STRINGIFY(...) IDENTITY(APPLY(CONCAT_STRINGIFY_DISPATCH, COUNT(__VA_ARGS__)))(__VA_ARGS__)
CONCAT_STRINGIFY(AAA, BBB, CCC)
Возможно, немного более «хакерский», но решение для сортировки состоит в том, чтобы объединить множество аргументов и передать пустые аргументы по мере необходимости:
#define TO_STRING(X) #X
#define CONCAT_STRINGIFY_4(A, B, C, D, ...) TO_STRING(A ## B ## C## D)
#define CS(...) CONCAT_STRINGIFY_4(__VA_ARGS__,,,,)
Это работает до 4 аргументов, вы можете добавить больше по мере необходимости.
Во втором решении CS(AAA,BBB,CCC) расширяется до "AAA,BBB,CCC". Запятые не должны быть частью расширения, поэтому это не работает. Смотрите: godbolt.org/z/o1cMq4snM
Кстати: это тоже рабочий хак: #define CONCAT_STRINGIFY(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) TO_STRING(A##B##C##D##E##F##G##H##I##J##K##L##M##N##O##P##Q##R##S##T##U##V##W##X##Y##Z) но он генерирует предупреждение C4003. Я не могу отключить это предупреждение в MSVC с помощью #pragma warning( disable : 4003 ), и даже если бы я мог, все директивы #pragma ... остаются в выводе после предварительной обработки, что неприемлемо, поскольку я не передаю вывод препроцессора компилятору C.
@PavelStepanek Я думаю, это нестандартная штука для MSVC.
@PavelStepanek попробуйте включить /Zc:preprocessor, чтобы получить препроцессор, соответствующий стандарту, в MSVC.
Да, мой уродливый хак работает только с традиционным препроцессором /Zc:preprocessor- Именно поэтому я задал этот вопрос на StackOverflow, чтобы найти более общее решение.
@PavelStepanek Спасибо за голосование. Кроме того, чтобы уточнить, исправление camel-cdr работает для MSVC (удаляет запятые).
Вот общее решение. Он работает с 342 аргументами, но может быть экспоненциально расширен для работы с большим количеством аргументов путем добавления большего количества EVAL.
#define EVAL1(...) __VA_ARGS__
#define EVAL2(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__))))
#define EVAL3(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__))))
#define EVAL4(...) EVAL3(EVAL3(EVAL3(EVAL3(__VA_ARGS__))))
#define EVAL5(...) EVAL4(EVAL4(EVAL4(EVAL4(__VA_ARGS__))))
#define EMPTY()
#define TUPLE_AT_1(a,b,...) b
#define CHECK(...) TUPLE_AT_1(__VA_ARGS__)
#define CAT_PROBE(...) ,CAT_END,
#define CAT_IND() CAT_
#define CAT_(x,a,...) CHECK(CAT_PROBE a,CAT_NEXT)(x,a,__VA_ARGS__)
#define CAT_NEXT(x,a,...) CAT_IND EMPTY()()(x##a,__VA_ARGS__)
#define CAT_END(x,a,...) #x
#define CAT(...) EVAL5(CAT_(,__VA_ARGS__,()))
CAT(a,b,c,d,e,f,g,h,i,j,k) // "abcdefghijk"
Он использует макрорекурсию для объединения всех аргументов до тех пор, пока не будет достигнут искусственно добавленный аргумент "()". Я выбрал «()», потому что его нельзя вставить и его легко обнаружить.
Поскольку MSVC по умолчанию не реализует препроцессор, соответствующий стандарту, вам необходимо включить флаг /Zc:preprocessor, чтобы приведенный выше код работал в MSVC.
Редактировать:
Если вас не волнует общее решение, вот красивое и компактное решение с 16 аргументами:
#define CAT_(a,b,c,d,e,f,g,i,j,k,l,m,n,o,p,...) a##b##c##d##e##f##g##i##j##k##l##m##n##o##p
#define CAT(...) CAT_(__VA_ARGS__,,,,,,,,,,,,,,,,,)
#define STR(...) #__VA_ARGS__
#define STRe(...) STR(__VA_ARGS__)
STRe(CAT(1,2)) // "12"
STRe(CAT(1,2,3,4,5,6,7)) // "1234567"
Пожалуйста, добавьте в свой ответ отказ от ответственности, что ваши решения не работают с традиционным препроцессором MSVC (опция: /Zc:preprocessor-), и я приму ваш ответ. Смотрите: godbolt.org/z/5qexvb1Wj
Если вам известно более общее решение, которое работает на всех типах препроцессоров, включите его.
@PavelStepanek подойдет, но в вашем вопросе не указан msvc, и приведенное выше решение должно строго соответствовать стандарту C. Вы не можете учитывать все ошибочные реализации.
Вы правы, и мне придется реализовать универсальный конкатенатор для MSVC, используя #if !defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL ваш метод для нетрадиционного препроцессора и что-то вроде: #define CONCAT_STRINGIFY(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) TO_STRING(A##B##C##D##E##F##G##H##I##J##K##L##M##N##O##P##Q##R##S##T##U##V##W##X##Y##Z) для традиционного препроцессора. К сожалению, последнее вызывает предупреждение C4003, которое нельзя отключить с помощью #pragma warning( disable : 4003 ).
Это выглядит похоже: stackoverflow.com/questions/69968476/…