Makromagie zum Kombinieren von Anzeigen und Implementierungen

    Eines der unangenehmen Probleme ist, dass Sie den Code an mehreren Stellen bearbeiten müssen, wenn Sie selbst einfache Änderungen vornehmen. Wenn beispielsweise ein Datenfeld zu einer Klasse hinzugefügt wird, muss es zur Klassendeklaration hinzugefügt werden, die in den Konstruktoren initialisiert wurde, und wenn Kopier- oder Vergleichsoperatoren neu definiert werden, müssen auch diese neu definiert werden. Dies nimmt Zeit in Anspruch und führt zu Fehlern, wenn Sie einen der Orte vergessen (es ist besonders unangenehm, die Initialisierung zu vergessen, solche Fehler können jahrelang auftreten und unerwartete, schwer reproduzierbare Probleme verursachen).

    Wenn eine Klasse eine aktive Änderung von Feldern beinhaltet, wird normalerweise ein Makro geschrieben, das die Implementierung von Aktionen mit dem Feld (Initialisierung, Kopieren, Serialisierung, Reflektion) auf sich nimmt.
    Daher sollte die Variable nur an zwei Stellen registriert werden - in der Klasse deklariert und implementiert (oder für die spätere Verwendung in der Implementierung registriert).

    Es stellt sich heraus wie:
    class TData
    {
    public:
    	int Number;
    	float Factor;
    	BEGIN_FIELDS
    		FIELD(Number, 0)
    		FIELD(Factor, 1.0f)
    	END_FIELDS
    };
    

    Ich zitiere nicht die Implementierung von Makros, zum Beispiel die Registrierung von Zeigern auf Datenfelder, deren Namen und Anfangswerte für die spätere Verwendung.

    Ein weiteres einfacheres Beispiel. Es ist notwendig, die Aufzählung zu reflektieren, um beispielsweise die Variante der Aufzählung mit einer Zeichenfolge ihres Namens abzugleichen. Normalerweise geschieht dies ungefähr so:
    enum TModelType
    {
    	Car,
    	Weapon,
    	Human
    };
    #define REFLECT_MODEL_TYPE(mac_value)	Register(mac_value, #mac_value);
    void TModelTypeReflection::RegisterTypes()
    {
    	REFLECT_MODEL_TYPE(Car)
    	REFLECT_MODEL_TYPE(Weapon)
    	REFLECT_MODEL_TYPE(Human)
    }
    

    Die TModelTypeReflection-Deklaration und die Registerimplementierung werden die Vorstellungskraft des Lesers anregen .

    Ich war lange mit diesem Zustand zufrieden. Aber kürzlich dachte ich, was könnte besser gemacht werden, nachdem ich es mit einer einzigen Ankündigung geschafft hatte. Sie können dies mit denselben Makros tun.

    Für das letzte Beispiel sieht es so aus:
    #define DECLARE_MODEL_TYPE(mac_value, mac_next)		\
    mac_value,                                              \
    mac_next                                                \
    Register(mac_value, #mac_value);
    #define END_MODEL_TYPE					\
    };	void TModelTypeReflection::RegisterTypes()	{
    enum TModelType
    {
    	DECLARE_MODEL_TYPE(Car,
    	DECLARE_MODEL_TYPE(Weapon,
    	DECLARE_MODEL_TYPE(Human,
    	END_MODEL_TYPE)))
    }
    

    Makros DECLARE_MODEL_TYPE entfalten zuerst in den Elementen aufgeführt ist , dem Code der END_MODEL_TYPE nahe der Transfereinheit und legen Sie die Header - Funktion in dem Funktionskörper Einsatz einen Anruf Register für die Elemente, aber in umgekehrter Reihenfolge, und schließlich die Klammer den Funktionsblock schließen (also ohne ein Semikolon )
    Ein ähnlicher Code kann für Klassenfelder geschrieben werden.

    Es bleibt nur zu sagen, über die Mängel:
    • Registrierung in umgekehrter Reihenfolge, aber wenn die Reihenfolge immer noch wichtig ist, können Sie dies in der Register- Implementierung berücksichtigen. Fügen Sie beispielsweise das nächste Feld oben in die Liste und nicht am Ende hinzu.
    • Probleme mit Systemen zur automatischen Generierung von Dokumentation wie DOxygen, sie werden nicht raten, Makros bereitzustellen;
    • die Notwendigkeit, beim Hinzufügen eines Feldes nach END_MODEL_TYPE eine weitere schließende Klammer hinzuzufügen . Unangenehmer Nachteil, weil Sie müssen den Code noch an zwei Stellen bearbeiten. Ein wenig froh, dass der Präprozessor die Halterung nicht vergessen lässt.

    Eine alternative Lösung zum Kombinieren einer Werbung mit einer Implementierung besteht in der Verwendung eines Codegenerators. Dieser Ansatz hat jedoch auch seine Nachteile.

    Jetzt auch beliebt: