Создание списка строковых аргументов макроса с вариативными параметрами и поздними расширениями

У меня следующая проблема - задано переменное количество аргументов макроса argX для создания списка строковых аргументов #argX

Пример:

LIST(A, B) -> "A", "B"
LIST(A, B, C) -> "A", "B", "C"

Я использую Boost, поэтому приведенный выше макрос не так уж сложно реализовать с помощью вспомогательных макросов для каждого количества аргументов и отправки LIST(...) в соответствующий LIST_n(arg1, ... argn).

Проблема начинается, когда входные данные для LIST сами являются макросами. В этом случае (если я использую ... и __VA_ARGS__) макросы расширяются до того, как они будут преобразованы в строку, что дает:

#define A 10
LIST(A, B) -> "10", "B"

Я хочу, чтобы это работало с макросами, определенными в заголовках Windows, и большинство значений там являются макросами (MB_OK, AF_INET, ...), поэтому все, что я получаю, это список строковых чисел.

Когда __VA_ARGS__ не используется, все работает нормально:

#define A 10
#define LIST_1(arg0) #arg0
LIST_1(A) -> "A"

Я пробовал несколько макросов, которые откладывают раскрытие __VA_ARGS__ на более позднее время (например, до LIST_1, в котором нет переменных), но ничего не сработало.

Возможно ли это реализовать с помощью препроцессора C?


person user2971693    schedule 01.01.2014    source источник


Ответы (1)


Извините, но теперь есть способ сделать это на msvc. Из-за классических ошибок в их препроцессоре (см. for-other-macros" rel="nofollow">здесь и здесь) они рассматривают __VA_ARGS__ как один аргумент. Чтобы разбить его на отдельные аргументы, необходимо применить еще одно сканирование, которое также расширит макросы. В препроцессоре C99 вы можете запретить расширение __VA_ARGS__, используя пустой заполнитель:

/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)

/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT(x, y) PRIMITIVE_CAT(x, y)

/* p is an empty placeholder used to inhibit the expansion of __VA_ARGS__ */
#define STRINGIZE_ALL(p, ...) FIRST(NARGS(__VA_ARGS__), PRIMITIVE_STRINGIZE_ALL(p ## __VA_ARGS__,~,~,~,~,~,~,~,~))
#define PRIMITIVE_STRINGIZE_ALL(x1, x2, x3, x4, x5, x6, x7, x8, ...)  #x1, #x2, #x3, #x4, #x5, #x6, #x7, #x8

/* Retrieve the first n arguments from __VA_ARGS__ */
#define FIRST(n, ...) CAT(FIRST_, n)(__VA_ARGS__,~,~,~,~,~,~,~,~)
#define FIRST_1(x1, ...) x1
#define FIRST_2(x1, x2, ...) x1, x2
#define FIRST_3(x1, x2, x3, ...) x1, x2, x3
#define FIRST_4(x1, x2, x3, x4, ...) x1, x2, x3, x4
#define FIRST_5(x1, x2, x3, x4, x5, ...) x1, x2, x3, x4, x5
#define FIRST_6(x1, x2, x3, x4, x5, x6, ...) x1, x2, x3, x4, x5, x6
#define FIRST_7(x1, x2, x3, x4, x5, x6, x7, ...) x1, x2, x3, x4, x5, x6, x7
#define FIRST_8(x1, x2, x3, x4, x5, x6, x7, x8, ...) x1, x2, x3, x4, x5, x6, x7, x8

#define A 10
STRINGIZE_ALL(, A, B)

Это будет работать до 8 аргументов в gcc и clang 3.4 или выше.

person Paul Fultz II    schedule 03.01.2014