2015-08-25 4 views
7

Ich habe gerade herausgefunden, dass gcc das Ergebnis der Erweiterung eines funktionsähnlichen Makros als separates Token zu behandeln scheint. Hier ist ein einfaches Beispiel, das Verhalten von gcc zeigt:Erweiterung des funktionsähnlichen Makros erstellt ein separates Token

#define f() foo 
void f()_bar(void); 
void f()bar(void); 
void f()-bar(void); 

Wenn ich ausführen gcc -E -P test.c (läuft nur den Präprozessor), habe ich folgende Ausgabe:

void foo _bar(void); 
void foo bar(void); 
void foo-bar(void); 

Es scheint, wie in den ersten beiden Definitionen, gcc fügt Leerzeichen nach dem erweiterten Makro ein, um sicherzustellen, dass es sich um ein separates Token handelt. Ist das wirklich was hier passiert?

Ist dies von irgendeinem Standard vorgeschrieben (Ich konnte keine Dokumentation zu diesem Thema finden)?

Ich möchte _bar Teil des gleichen Tokens machen. Gibt es eine Möglichkeit, dies zu tun? Ich könnte den Token-Verkettungsoperator ## verwenden, aber es wird mehrere Ebenen von Makros erfordern (da im realen Code f() komplexer ist). Ich habe mich gefragt, ob es eine einfache (und wahrscheinlich besser lesbare) Lösung gibt.

+1

Ist Google down? Hast du nicht einmal versucht, den C [Standard] (http://port70.net/~nsz/c/c11/n1570.html#6.10.3) zu finden, der ** offensichtlich ** die maßgebliche Quelle ist, zu der man sich beraten kann. – Olaf

+1

@Olaf - Ich habe weder in C99 noch auf dem Link, auf den Sie mich hingewiesen haben, eine explizite Antwort auf meine Frage gefunden. – martinkunev

+4

Um fair zu sein, ist das Verhalten, wie in der Norm beschrieben, nicht ganz offensichtlich. Ich vermute, das Schlüsselwort ist, dass der Präprozessor das Makro durch * Token * ersetzt, was bedeutet, dass Whitespace eingefügt wird, wo es nötig ist, um dies tatsächlich zu tun. – nemetroid

Antwort

1

es scheint, wie in den ersten beiden Definitionen, gcc Einsätze Raum nach dem erweiterten Makro, um sicherzustellen, es ist ein separater Token ist. Ist das wirklich was hier passiert?

Ja.

Ist dies von jedem Standard vorgeschrieben (Ich konnte keine Dokumentation zu diesem Thema finden)?

Ja, obwohl eine Implementierung würde sogar mehr als ein Leerzeichen einfügen, um die Token zu trennen.

f()_bar

hier haben Sie 4 Token nach lexikalische Analyse (sie zu diesem Zeitpunkt tatsächlich Präprozessor-Token sind aber nennen wir sie Token): f, (, ) und _bar.

Die funktionsähnlichen Makro Ersatz semantischen (wie in C11 definiert, 6.10.3) hat das Token f 3, (, ) in eine neue foo zu ersetzen. Es ist nicht erlaubt, an anderen Tokens zu arbeiten und das letzte Token zu ändern. Dazu muss die Implementierung mindestens einen Whitespace einfügen, um das Token _bar zu erhalten. Andernfalls wäre das Ergebnis foo_bar, was ein einzelnes Token ist.

gcc Präprozessor etwas documents es hier:

Sobald die Eingabedatei in Token gebrochen wird, ändern sich die Token Grenzen nie, es sei denn, die ‚##‘ Vorverarbeitung Operator Token verwendet wird, zusammen zu fügen. Siehe Verkettung. Zum Beispiel

#define foo() bar 
foo()baz 
    ==> bar baz 
not 
    ==> barbaz 

Im anderen Fall, wie f()-bar gibt 5 Token: f, (, ), - und bar. (- ist ein Interpunktionstoken in C, während _ in _bar ist einfach ein Zeichen des Bezeichner Tokens). Die Implementierung muss kein Token-Trennzeichen (als Whitespace) einfügen, da nach dem Makroersatz -bar immer noch als zwei separate Tokens der C-Syntax betrachtet werden.

gcc Preprozessor (cpp) fügt hier keine Leerzeichen ein, weil es nicht notwendig ist. In cppdocumentation, auf Token-Abstand steht geschrieben (auf einer anderen Ausgabe):

Allerdings würden wir Raum Einfügung auf ein Minimum halten mögen, sowohl aus ästhetischen Gründen und weil es Probleme für Menschen verursacht, die immer noch versuchen, den Präprozessor für Dinge wie Fortran-Quelle und Makefiles zu missbrauchen.

ich ging nicht auf die Lösung für Ihr Problem in dieser Antwort, aber ich glaube, Sie haben Operator verwenden explizit angegeben Tokens verketten: den ## token einfügen Operator.

1

Die einzige Möglichkeit, die ich denken kann (wenn man nicht den Token Verkettungsoperator verwenden ##) wird mit der traditionellen (Vornorm) C Vorverarbeitung:

gcc -E -P -traditional-cpp test.c 

Ausgang:

void foo_bar(void); 
void foobar(void); 
void foo-bar(void); 

More info

Verwandte Themen