2016-04-08 2 views
2

У нас есть следующий макрос препроцессора. Его использовали, чтобы помочь с Doxygen документацией, потому что Doxygen имеет проблемы с C++ и некоторые шаблоны определений типов:Как сделать препроцессорный макрос жадным?

#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.

+0

Я подозреваю, что это может вызвать проблемы, потому что левый аргумент X ставится в положение в декларации спецификатора. В декларации стиля C и C++ спецификаторы декларации не принимают необязательные круглые скобки. Однако деклараторы: 'int x;' и 'int (x);' - одно и то же. '(int) x;' выглядит как литое выражение. – Kaz

+0

@Kaz Не могли бы вы жить с * всегда * ставить круглые скобки вокруг аргумента, который мог бы содержать параметры шаблона, * и *, только поддерживающие компиляторы, реализующие переменные макросы C99? Если это так, есть хак, который будет работать. – zwol

4

Ты не так:

#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; 
+0

Это уродливо. Могу ли я всегда рассчитывать на не-жадный матч? Если это так, я собираюсь изменить «X» и «Y». Кажется, что этот сложный момент: то, что четко определено в зависимости от реализации. Мне нужно, чтобы он работал на компиляторах почти для всех платформ и восходил к 1990-м годам. Это особый вид ада. – jww

+0

Препроцессор разбивает списки аргументов макросов на запятые, которые не заключены в круглые скобки. (Он подсчитывает токены в скобках, чтобы они уравновешивались). Квадратные скобки, скобки или угловые скобки не распознаются; они не защищают запятую. Это имеет мало общего с жадностью. – Kaz

+0

О переносимости, я использовал только синтаксис препроцессора C90. – Kaz

Смежные вопросы