Gibt es etwas Besseres als eine Metafactory, um die Konstruktorinjektion in abgeleitete Klassen in CRTP zu umgehen?

In demCRTP, Ich möchte den Konstruktor sauber in die abgeleitete Klasse einfügen - ohne Verwendung von Makros und ohne sie auszuschreiben. Es scheint unmöglich zu sein, deshalb habe ich einige Problemumgehungen gefunden.

Erstens gibt es einen BasiswertEreignisklasse (QEvent) das sollte einen eindeutigen Integer-Typ-Tag für jede abgeleitete Klasse haben (Siehe Begründung). Sie erhalten es durch Aufrufen einer Registrierungsfunktion. Es ist einfach genug, einen CRTP-Wrapper zu erstellen, der dies vor Ihnen verbirgt:

template <typename Derived> class EventWrapper : public QEvent {
public:
    EventWrapper() : QEvent(staticType()) {}
    static QEvent::Type staticType() {
        static QEvent::Type type = static_cast<QEvent::Type>(registerEventType());
        return type;
    }
};
class MyEvent1 : public EventWrapper<MyEvent1> {}; // easy-peasy
class MyEvent2 : public EventWrapper<MyEvent2> {};

Beachten Sie, dassMyEvent1::staticType() != MyEvent2::staticType(): registerEventType() Gibt bei jedem Aufruf eindeutige Typen zurück.

Jetzt möchte ich, dass die Ereignisklasse einige Daten enthält:

template <typename Derived> class StringEvent : public EventWrapper<D> {
    std::string m_str;
public:
    explicit StringEvent(const std::string & str) : m_str(str) {}
    std::string value() const { return m_str; }
};

Aber hier stoßen wir auf ein Problem: Wir müssen den Konstruktor in manuell definierenjeder der abgeleiteten Klassen. Der springende Punkt hierbei ist, dass das Erstellen solcher Klassen einfach sein sollte, da es viele verschiedene stringtragende Ereignistypen geben kann. Aber es ist alles andere als einfach:

class MyEvent3 : public StringEvent<MyEvent3> {
    public: MyEvent3(std::string s) : StringEvent(s) {}
};

Dies wird offensichtlich sehr schnell alt, selbst mit der Weiterleitung von C ++ 11-Konstruktoren:

class MyEvent3 : public StringEvent<MyEvent3> { using StringEvent::StringEvent; };

Was wir wollen, ist eine Möglichkeit, diesen Konstruktor in die abgeleitete Klasse zu injizieren oder dies zu vermeiden, während die Benutzerfreundlichkeit erhalten bleibt. Sicher, Sie können es in einem Präprozessor-Makro verbergen, aber ich hasse diese Makros, sie sind ein Wartungsschmerz, da sie neue Namen für sehr einfache Konzepte einführen.

Wir können natürlich einen Dummy-Typ verwenden. Beachten Sie, dass keine Definition des Dummy-Typs erforderlich ist. Es ist nur ein Name, der als Typargument verwendet wird.

// Pre-C++11
class DummyEvent3;
typedef StringEvent<DummyEvent3> MyEvent3;
// C++11
class DummyEvent3;
using MyEvent3 = StringEvent<DummyEvent3>;

Eine andere Lösung wäre die Verwendung einesint Template-Argument und benutze einen Enum-Wert, aber dies bringt das Problem der Eindeutigkeit zurück, das durch die Verwendung von gelöst wurderegisterEventType() an erster Stelle. Es würde keinen Spaß machen, zu garantieren, dass ein großes Programm korrekt ist. Und Sie müssten immer noch die Aufzählung formulieren.

Also habe ich mir eine Metaprogramm-Klasse ausgedacht, die ich als Metafactory bezeichnen werde und die gebrauchsfertig istStringEvent Klassen für uns unter Beibehaltung einer Typdefinition:

// the metafactory for string events
template <typename Derived> class StringEventMF {
public:
    class Event : public EventWrapper<Derived> {
        std::string m_str;
    public:
        explicit Event(const std::string & val) : m_str(val) {}
        std::string value() const { return m_str; }
    };
};

oder einfach

template <typename Derived> class StringEventMF {
public:
    typedef StringEvent<Derived> Event;
};

Dies wird verwendet wie:

class Update : public StringEventMF<Update> {};
class Clear : public StringEventMF<Clear> {};

void test() {
   Update::Event * ev = new Update::Event("foo");
   ...
}

Die Klassen, die Sie verwenden, sindUpdate::Event, Clear::Event. DasUpdate undClear sind Metafactories: Sie generieren für uns die gewünschte Eventklasse. Die Ableitung von der Metafactory umgeht die Ableitung vom konkreten Klassentyp. Der Metafactory-Typ gibt den eindeutigen Typdiskriminator an, der zum Erstellen eindeutiger konkreter Klassentypen erforderlich ist.

Die Fragen sind:

Gibt es eine "sauberere" oder "wünschenswertere" Möglichkeit, dies zu tun? Im Idealfall ist der folgende nicht funktionierende Pseudocode meine ideale Methode - ohne Wiederholung:

class UpdateEvent : public StringEvent <magic>;

Der Name der abgeleiteten Klasse wird nur einmal angezeigt und der Name des BasiskonzeptsStringEvent erscheint auch nur einmal. Das CRTP verlangt, dass der Klassenname zweimal vorkommt - bisher denke ich, dass es akzeptabel ist, aber meine Metaprogrammierung-Fu ist in Fetzen. Auch hier möchte ich eine Lösung ohne Präprozessor, andernfalls ist dies ein Kinderspiel.

Ist der Namemetafactory meine ursprüngliche Erfindung (ha ha), oder fehlt nur mein Google-Fu? Dieses metafaktorische Muster scheint ziemlich flexibel zu sein. Es ist einfach, Metafactories durch mehrfache Ableitung zu erstellen. Sagen Sie, Sie wollten eineUpdate::Event von einer Fabrik hergestellt, undUpdate::Foo von einem anderen gemacht.

Diese Frage ist motiviert vondiese Antwort. Hinweis: In echtem Code würde ich verwendenQString, aber ich versuche es so allgemein wie möglich zu halten.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage