14 Stimmen

Kann ich an ein Präprozessormakro anhängen?

Gibt es in Standard-C - oder mit GNU-Erweiterungen - eine Möglichkeit, etwas an eine Makrodefinition anzuhängen? z.B. mit einem Makro, das wie folgt definiert ist
#define List foo bar
kann ich anhängen bas so dass es List dehnt sich aus, als ob ich es definiert hätte
#define List foo bar bas ?

Ich hatte gehofft, dass ich so etwas tun könnte:

#define List    foo bar bas

#define List_   Expand(List)
#undef List
#define List    Expand(List_) quux

aber ich kann nicht herausfinden, wie ich die Expand() Makro, damit es das tut, was ich will.

Motivation: Ich spiele mit diskriminierten/markierten Gewerkschaften in diesem Sinne:

struct quux_foo { int x; };
struct quux_bar { char *s; };
struct quux_bas { void *p; };

enum quux_type {quux_foo, quux_bar, quux_bas};

struct quux {
    enum quux_type type;
    union {
        struct quux_foo foo;
        struct quux_bar bar;
        struct quux_bas bas;
    } t;
};

Ich denke, dies ist ein guter Platz für den X-macro. Wenn ich ein Makro definiere
#define quux_table X(foo) X(bar) X(bas)
die Aufzählung und die Struktur können so definiert werden, ohne dass sie aus dem Gleichgewicht geraten:

#define X(t) quux_ ## t,
enum quux_type {quux_table};
#undef X

#define X(t) struct quux_ ## t t;
struct quux {
    enum quux_type type;
    union {quux_table} t;
};
#undef X

Natürlich ist die quux_* Strukturen können aus dem Gleichgewicht geraten, deshalb würde ich gerne etwas Ähnliches machen, nur auf legale Weise:

struct quux_foo { int x; };
#define quux_table quux_table X(foo)

struct quux_bar { char *s; };
#define quux_table quux_table X(bar)

struct quux_bas { void *p; };
#define quux_table quux_table X(bas)

(Nun, was ich wirklich wollen in der Lage sein, etwas zu tun wie
member_struct(quux, foo) { int x; };
aber ich weiß sehr wohl, dass Makros nicht innerhalb von Makros (neu) definiert werden können).

Jedenfalls ist das mein motivierendes Beispiel. Gibt es eine Möglichkeit, dies zu erreichen?

Boost.Preprocessor Beispiele sind gut, wenn Sie mir zeigen können, wie die X-Makro-Technik mit dieser Bibliothek funktioniert.

14voto

rtpax Punkte 1617

Es gibt einen Weg!

Mit dem neuen _Pragma-Schlüsselwort kann dies in gcc erreicht werden (allerdings nicht mit msvc)

Wenn Sie ein Makro in seine eigene Definition einfügen, verzögert es seine Expansion, bis das Makro zum ersten Mal expandiert wird. Auf diese Weise können Sie die vorherige Erweiterung zu einem Teil der eigenen Definition machen. Da das Makro jedoch während seiner Expansion ausgelöst wird, kann es nur einmal verwendet werden.

Hier ist ein Beispielcode, um ihn in Aktion zu sehen

#define pushfoo _Pragma("push_macro(\"foo\")") //for convenience
#define popfoo _Pragma("pop_macro(\"foo\")")

#define foo 1

pushfoo                           //push the old value
#undef foo                        //so you don't get a warning on the next line
#define foo popfoo foo , 2        //append to the previous value of foo

pushfoo
#undef foo
#define foo popfoo foo , 3

pushfoo
#undef foo
#define foo popfoo foo , 4

foo //this whole list will expand to something like popfoo foo popfoo foo popfoo foo , 4
    //which will in turn expand to 1 , 2 , 3 , 4

foo //the second time this will expand to just 1

Diese Option sollte die automatische Codegenerierung etwas vereinfachen, leider aber nur unter gcc (vielleicht clang, habe ich nicht getestet)

Um ehrlich zu sein, gibt es keinen Grund, den ich finden kann, warum dies funktionieren muss, es ist höchstwahrscheinlich undefiniertes Verhalten, das zufällig funktioniert. Ich vermute, dass der Grund dafür ist, dass das aktuelle Makro, das expandiert wird, nach dem Löschen von foo nicht mehr mit dem Namen foo verknüpft ist, wodurch das Symbol foo zu erweitern, aber das ist nur meine Vermutung

Bearbeiten:

Nach dem Testen mit clang, dies nicht funktioniert auch mit Clang.

Ich weiß nicht, warum ich dachte, dass Clang nicht funktioniert, vielleicht funktionierte es auf einem anderen Rechner nicht. Ich habe es auf jeden Fall mit dem angegebenen Code zum Laufen gebracht

7voto

James McNellis Punkte 337231

Im Grunde genommen, nein.

Makros werden träge ausgewertet. Wenn Sie #define List_ Expand(List) ist die Ersetzungsliste die Folge von vier Token Expand , ( , List y ) . Es gibt keine Möglichkeit, ein Makro zu einer Ersetzungsliste zu erweitern.

Alle Makro-Ersetzungen finden statt, wenn ein Makro aufgerufen wird.

Ich würde empfehlen, die Boost.Preprocessor-Bibliothek für die automatische Codegenerierung zu verwenden. Es ist ein bisschen Arbeit, aber Sie können einige Ziele erreichen ziemlich beeindruckende Dinge es zu benutzen. Es sollte vollständig kompatibel mit C sein.

2voto

EnabrenTane Punkte 7370

Ich bin mir nicht sicher, ob das hilft, aber Sie können vari arg Makros machen. Mr. Conrad vom x264-Projekt liebt den Missbrauch von Präprozessoren. Wenn sie sich so anhören, als könnten sie helfen, können Sie mehr darüber erfahren Hier

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X