У нас есть следующий макрос препроцессора. Он используется для помощи с документацией по Doxygen, потому что Doxygen имеет проблемы с C ++ и некоторыми шаблонами typedefs:
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(x, y) class y : public x {};
#else
# define DOCUMENTED_TYPEDEF(x, y) typedef x y;
#endif
Это прекрасно работает, когда X
не является шаблоном или имеет только один параметр шаблона. Однако если X
шаблон с несколькими параметрами:
DOCUMENTED_TYPEDEF(Foo<R,S>,Bar);
Тогда это приводит к ошибкам компиляции, потому что строка разбивается на Foo<R
а также S>,Bar
(и это не генерирует документацию).
Как сделать макрос препроцессора жадным?
Невозможно изменить способ, которым препроцессор анализирует аргументы макроса. Запятые, которые не заключены в скобки, всегда разделяют аргументы макроса.
Что ты может быть в состоянии сделать это
DOCUMENTED_TYPEDEF((Foo<R,S>), Bar);
но, конечно, это работает только в том случае, если в раскрытии макроса допустимы внутренние скобки. Я не помню по макушке, вызовет ли это проблемы в контекстах, которые вы показываете.
Если все в порядке, требуя C99 variadic макросов, вы можете использовать их, чтобы избавиться от лишних скобок:
#define STRIP_PARENS(...) __VA_ARGS__
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(x, y) class y : public STRIP_PARENS x {};
#else
# define DOCUMENTED_TYPEDEF(x, y) typedef STRIP_PARENS x y;
#endif
DOCUMENTED_TYPEDEF((Foo<R,S>), Bar);
но теперь ты всегда должны поставить лишнюю пару скобок вокруг первого аргумента DOCUMENTED_TYPEDEF.
Вам не понравится это:
#define COMMA ,
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(x, y) class y : public x {};
#else
# define DOCUMENTED_TYPEDEF(x, y) typedef x y;
#endif
DOCUMENTED_TYPEDEF(Foo<R COMMA S>,Bar)
Тестовое задание:
$ gcc -E запятая-macro.c # 1 "comma-macro.c" # 1 "<встроенный>"# 1"<командная строка>"# 1" comma-macro.c "# 9" comma-macro.c "typedef Foo<R, S> Бар;
Списки аргументов макроса анализируются на предмет наличия скобок и запятых перед любой заменой. затем COMMA
заменяется в x
аргумент и x
подставляется в тело макроса В это время спор аргументов сделан; не имеет значения, что COMMA
был заменен на запятую с символом пунктуатора. Тем не менее, эта запятая будут отдельные аргументы, которые возникают в любых вызовах макросов, сгенерированных этим макросом, поэтому, если они должны быть защищены, вам нужно что-то более сумасшедшее.
Вы можете скрыть COMMA
скажем, за функциональным макросом PAIR
:
#define COMMA ,
#define PAIR(A, B) A COMMA B
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(x, y) class y : public x {};
#else
# define DOCUMENTED_TYPEDEF(x, y) typedef x y;
#endif
DOCUMENTED_TYPEDEF(PAIR(Foo<R, S>), Bar)
На первый взгляд это более привлекательно, но, возможно, есть и недостатки. Это более запутано. Читатель задается вопросом, есть ли семантика позади PAIR
? В то время как COMMA
выглядит слишком тупо, чтобы иметь семантику, и его цель, вероятно, сразу же станет очевидной для любого, кто имеет боевые шрамы от боя с препроцессором.
Около PAIR
мы можем скрыть это и получить синтаксис, как в ответе Звола. Но тогда нам нужно несколько вариантов DOCUMENTED_TYPEDEF
,
Также, кстати, отбросим бесполезные COMMA
который не нужен в правой части макроса:
#define PAIR(A, B) A, B
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF_2(x2, y) class y : public PAIR x2 {};
#else
# define DOCUMENTED_TYPEDEF_2(x2, y) typedef PAIR x2 y;
#endif
DOCUMENTED_TYPEDEF_2((<R, S>), Bar)
$ gcc -std = c90 -E -Wall -педантичная запятая-macro.c # 1 "comma-macro.c" # 1 "<встроенный>"# 1"<командная строка>"# 1" comma-macro.c "# 11" comma-macro.c "typedef <R, S> Бар;
Похоже, что это может быть выполнимо с variadic макросами стиля C99 Однако это может нарушать требование переносимости, обсуждаемое в комментариях, не говоря уже о том, что это C ++. Ради будущих посетителей:
#define PNEUMATIC_COMMA_GUN(A, ...) A, ## __VA_ARGS__
#if defined(DOXYGEN_PROCESSING)
# define DOCUMENTED_TYPEDEF(xv, y) class y : public PNEUMATIC_COMMA_GUN xv {};
#else
# define DOCUMENTED_TYPEDEF(xv, y) typedef PNEUMATIC_COMMA_GUN xv y;
#endif
DOCUMENTED_TYPEDEF((<R, S, T, L, N, E>), Bar)
$ gcc -std = c99 -E -Wall -педантичная запятая-macro.c # 1 "comma-macro.c" # 1 "<встроенный>"# 1"<командная строка>"# 1" comma-macro.c "# 9" comma-macro.c "typedef <R, S, T, L, N, E> Бар;