Есть ли способ использовать строковое преобразование препроцессора C++ для переменных аргументов макроса?

Я думаю, что ответ на этот вопрос нет, но было бы здорово, если бы был способ. Чтобы уточнить, предположим, у меня есть следующий макрос:

#define MY_VARIADIC_MACRO(X...) // Does some stuff here in the macro definition

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

#define MY_VARIADIC_MACRO(X...) some_variadic_function("some string", #X)

3 ответа

Решение

Хорошо, я не хотел отвечать здесь на свой вопрос, но я нашел приличное решение, которое в некоторой степени является комбинацией ответа Марка Уилкинса и примера, который я привел в этом вопросе.

Можно структурировать весь набор переменных набора, который затем включает в строку запятые с разделителями. Вот быстрый пример:

#define MY_VARIADIC_MACRO(X...) printf(#X)

Использование приведенного выше макроса показывает, что весь набор аргументов, передаваемых в макрос, становится строковым.

Затем вы можете определить функцию для токенизации этих аргументов, используя запятую с разделителями, тем самым получая набор токенизированных строк с помощью макроса variadic:

#define MY_VARIADIC_MACRO(X...) tokenize_my_arguments(#X)

Тогда на самом деле больше нет зависимости от того, что макрос variadic вызывает функцию variadic, и я могу красиво перебирать мой массив константных строк C, а не перебирать va_arg.

* Новый материал от редактировать следует *

Согласно комментарию Тима, вот подробности решения. Пожалуйста, прости любые ошибки, так как это было сделано на скорую руку, и мне пришлось портировать с того, над чем я работаю. Кроме того, он не предназначен для копирования / вставки, поскольку он выводит только строковые аргументы для демонстрации POC, но должен быть достаточным для демонстрации функциональности.

Хотя это решение требует некоторых вычислений во время выполнения, переменные макросы часто вызывают переменные функции и требуют итерации по va_args, поэтому итерация происходит при поиске токенов, хотя производительность, вероятно, жертвуется. Тем не менее, для удобства обслуживания, универсальности и простоты реализации, это, кажется, лучший вариант на данный момент:

#define VARIADIC_STRINGIFY(_ARGUMENTS_TO_STRINGIFY...) Variadic_Stringification_Without_Variadic_Function(#_ARGUMENTS_TO_STRINGIFY)

void Variadic_Stringification_Without_Variadic_Function (const char* _stringified_arguments)
{
    strcpy(converted_arguments, _stringified_arguments);

    for(char* token = strtok(converted_arguments, ","); token != 0x0; token = strtok(0x0, ","))
        std::cout << token << std::endl;
}

Вы можете использовать различные методы рекурсивных макросов для работы с переменными макросами. Например, вы можете определить NUM_ARGS макрос, который подсчитывает количество аргументов для вариационного макроса:

#define _NUM_ARGS(X100, X99, X98, X97, X96, X95, X94, X93, X92, X91, X90, X89, X88, X87, X86, X85, X84, X83, X82, X81, X80, X79, X78, X77, X76, X75, X74, X73, X72, X71, X70, X69, X68, X67, X66, X65, X64, X63, X62, X61, X60, X59, X58, X57, X56, X55, X54, X53, X52, X51, X50, X49, X48, X47, X46, X45, X44, X43, X42, X41, X40, X39, X38, X37, X36, X35, X34, X33, X32, X31, X30, X29, X28, X27, X26, X25, X24, X23, X22, X21, X20, X19, X18, X17, X16, X15, X14, X13, X12, X11, X10, X9, X8, X7, X6, X5, X4, X3, X2, X1, N, ...)   N

#define NUM_ARGS(...) _NUM_ARGS(__VA_ARGS__, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

Затем с этим вы можете написать FOREACH макрос, который расширяет другой макрос для каждого элемента списка:

#define EXPAND(X)       X
#define FIRSTARG(X, ...)    (X)
#define RESTARGS(X, ...)    (__VA_ARGS__)
#define FOREACH(MACRO, LIST)    FOREACH_(NUM_ARGS LIST, MACRO, LIST)
#define FOREACH_(N, M, LIST)    FOREACH__(N, M, LIST)
#define FOREACH__(N, M, LIST)   FOREACH_##N(M, LIST)
#define FOREACH_1(M, LIST)  M LIST
#define FOREACH_2(M, LIST)  EXPAND(M FIRSTARG LIST) FOREACH_1(M, RESTARGS LIST)
#define FOREACH_3(M, LIST)  EXPAND(M FIRSTARG LIST) FOREACH_2(M, RESTARGS LIST)
        :

Который, в свою очередь, позволит вам определить ваш макрос, который структурирует каждый из его аргументов:

#define STRINGIFY(X)    #X
#define MY_VARIADIC_MACRO(...)    FOREACH(STRINGIFY, (__VA_ARGS__))

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

#define MY_VARIADIC_MACRO(X) printf( "%s\n", #X )

Тогда используйте это как следующее. Заключите параметры в парены, чтобы они отображались в макросе как один параметр.

MY_VARIADIC_MACRO ((var1, var2, "string"));

Затем вы можете заставить макрос вызывать некоторую функцию, которая удаляет внешние символы или, возможно, анализирует данную строку.

Другие вопросы по тегам