Как сделать макрос препроцессора жадным?

У нас есть следующий макрос препроцессора. Он используется для помощи с документацией по 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 (и это не генерирует документацию).

Как сделать макрос препроцессора жадным?

3

Решение

Невозможно изменить способ, которым препроцессор анализирует аргументы макроса. Запятые, которые не заключены в скобки, всегда разделяют аргументы макроса.

Что ты может быть в состоянии сделать это

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.

3

Другие решения

Вам не понравится это:

#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> Бар;
4

По вопросам рекламы [email protected]