Ты не так:
#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 comma-macro.c
# 1 "comma-macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "comma-macro.c"
# 9 "comma-macro.c"
typedef Foo<R , S> Bar;
Macro списки аргументов обрабатываются для скобок и запятых, прежде чем какой-либо подмена происходит. Затем 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
, мы сможем скрыть его и получим синтаксис, как в ответе Zwol.Но тогда нам нужны несколько вариантов 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 -pedantic comma-macro.c
# 1 "comma-macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "comma-macro.c"
# 11 "comma-macro.c"
typedef <R, S> Bar;
Это выглядит как это может быть проделано с 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 -pedantic comma-macro.c
# 1 "comma-macro.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "comma-macro.c"
# 9 "comma-macro.c"
typedef <R, S, T, L, N, E> Bar;
Я подозреваю, что это может вызвать проблемы, потому что левый аргумент X ставится в положение в декларации спецификатора. В декларации стиля C и C++ спецификаторы декларации не принимают необязательные круглые скобки. Однако деклараторы: 'int x;' и 'int (x);' - одно и то же. '(int) x;' выглядит как литое выражение. – Kaz
@Kaz Не могли бы вы жить с * всегда * ставить круглые скобки вокруг аргумента, который мог бы содержать параметры шаблона, * и *, только поддерживающие компиляторы, реализующие переменные макросы C99? Если это так, есть хак, который будет работать. – zwol