2016-04-08 18 views
2

Wir haben das folgende Präprozessor-Makro. Sein verwendet mit Doxygen Dokumentation zu helfen, weil Doxygen Probleme mit C++ hat und einige Vorlage typedefs:Wie macht man einen Preprozessor gierig?

#if defined(DOXYGEN_PROCESSING) 
# define DOCUMENTED_TYPEDEF(x, y) class y : public x {}; 
#else 
# define DOCUMENTED_TYPEDEF(x, y) typedef x y; 
#endif 

Es funktioniert gut, wenn X eine nicht-Vorlage ist oder nur ein Template-Parameter. Wenn jedoch X ist eine Vorlage mit mehreren Parametern:

DOCUMENTED_TYPEDEF(Foo<R,S>,Bar); 

Dann kommt es zu Kompilierung Fehler, da die Zeichenfolge in Foo<R aufgeteilt und S>,Bar (und es erzeugt keine Dokumentation).

Wie mache ich einen Preprozessor Makro gierig?

Antwort

3

Es gibt keine Möglichkeit zu ändern, wie der Präprozessor die Argumente zu einem Makro analysiert. Kommas, die nicht in Klammern stehen, trennen immer Makroargumente.

Was Sie kann der Lage sein, Werke zu tun ist

DOCUMENTED_TYPEDEF((Foo<R,S>), Bar); 

aber natürlich dies nur, wenn es in Ordnung ist für die inneren Klammern in der Expansion des Makros erscheinen. Ich erinnere mich nicht von ganzem Herzen, wenn das zu Problemen in den Kontexten führen würde, die Sie zeigen.

Wenn es OK ist, C99 variadische Makros zu benötigen, können Sie sie loswerden der zusätzlichen Klammern bekommen verwenden:

#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); 

aber jetzt Sie immer haben ein zusätzliches Paar von Klammern um das erste Argument zu setzen zu DOCUMENTED_TYPEDEF.

+0

Ich vermute, dass es zu Problemen führen, weil das linke Argument X in die Position einer Erklärung Spezifizierer gesetzt wird. In einer C- und C++ - Deklaration verwenden Deklarationsbezeichner keine optionalen Klammern. Deklaratoren tun jedoch: 'int x;' und 'int (x);' sind das gleiche. '(int) x;' sieht aus wie ein Cast-Ausdruck. – Kaz

+0

@Kaz Könnten Sie mit * always * leben, indem Sie Klammern um das Argument legen, das die Template-Parameter * und * enthält, wobei nur Compiler unterstützt werden, die C99 variadic-Makros implementieren? Wenn ja, gibt es einen Hack, der funktioniert. – zwol

4

Du wird nicht so:

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

Test:

 
$ 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; 

Makroargumentlisten für Klammern und Kommata analysiert werden, bevor eine Substitution stattfindet. Dann wird COMMA im x Argument ersetzt, und x wird in den Makrokörper substituiert. Zu dieser Zeit ist das Auseinanderbrechen der Argumente getan; Es ist nicht relevant, dass COMMA durch ein Komma-Punctuator-Token ersetzt wurde. Allerdings wird dieses Komma Argumente trennen, die in allen Makroaufrufen auftreten, die von diesem Makro generiert werden, also wenn diese geschützt werden müssen, brauchen Sie etwas verrückteres.

Sie können die COMMA hinter einem funktionsähnliche Makro verstecken, sagen 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) 

Auf den ersten Blick ist es attraktiver, aber es gibt wahrscheinlich Nachteile. Es ist mehr verschleiert. Der Leser fragt sich, gibt es eine Semantik hinter PAIR? Wohingegen COMMA für eine Semantik zu stumpf aussieht, und ihr Zweck ist wahrscheinlich sofort offensichtlich für jeden, der Kampfnarben von Kämpfen mit dem Präprozessor hat.

Über PAIR können wir es vielleicht verstecken und enden mit einer Syntax wie in Zwols Antwort.Aber dann brauchen wir mehrere Varianten der DOCUMENTED_TYPEDEF.

auch, nebenbei gesagt, lassen Sie uns die nutzlose COMMA fallen, die nicht auf der rechten Seite eines Makros benötigt wird:

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

Das sieht aus wie es mit C99 Stil variadische Makros machbar sein kann . Dies kann jedoch die in den Kommentaren besprochene Übertragbarkeitsanforderung verletzen, ganz zu schweigen davon, dass dies C++ ist. Aus Gründen der zukünftigen Besucher:

#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

Das ist hässlich. Kann ich immer mit einem nicht gierigen Match rechnen? Wenn dem so ist, denke ich daran, das "X" und das "Y" umzukehren. Der knifflige Punkt scheint zu sein: Was ist gut definiert gegenüber der Implementierung definiert. Ich brauche es für Compiler für fast alle Plattformen und gehe zurück in die 1990er Jahre. Es ist eine besondere Art von Hölle. – jww

+0

Der Präprozessor bricht Makroargumentlisten für Kommas, die nicht in Klammern eingeschlossen sind. (Es zählt runde Klammern, damit sie ausgeglichen sind). Eckige Klammern, Klammern oder Winkel werden nicht erkannt; Sie bieten keinen Schutz für das Komma. Es hat wenig mit Gier zu tun. – Kaz

+0

Über Portabilität, ich verwendete nur C90 Präprozessor-Syntax. – Kaz

Verwandte Themen