Statische Initialisierung in Template forcieren [GCC/LLVM/MSVC]



  • Hallo miteinander

    Ich experimentiere gerade an einer Art "Marker-Template" (nennen wir es hier vereinfacht nur marker ) herum. In etwa sieht das so aus:

    template<typename>
    struct marker
    {
        static const bool init;
    };
    
    template<typename T>
    const bool marker<T>::init = ([]() -> bool 
    {
        clog << "Super Sache.";
        return true;
    })();
    

    Die Idee ist nun, dass man für irgend eine Erwähnung von marker<T> im Code (konkrete Anwendung ist auch in Vererbungshierarchien) vor Programmstart den im Lambda aufgeführten Code ausführen kann. Weil es sich bei marker um ein Template handelt funktioniert das leider nicht einfach, da auf die Variable init in der Regel nicht zugegriffen wird.

    Nun bin ich auf der Suche nach einer möglichst portablen Lösung, um die Initialisierung zu forcieren. Unter GCC und LLVM habe ich z.B. folgende Möglichkeit gefunden:

    template<typename static_type, static_type>
    struct require_static_initialization
    {
    };
    
    // Angepasster Marker für GCC/LLVM:
    template<typename>
    struct marker
    {
        static const bool init;
        typedef require_static_initialization<decltype(init) *, &init> force_init;
    };
    

    Mit Visual C++ 2012 kompiliert es zwar problemlos, aber die Initialisierung findet nicht statt. Sieht jemand eine Möglichkeit, das trotzdem irgendwie zu ermöglichen?

    Falls jemand eine Idee haben sollte, lasst es mich bitte wissen 🙂

    Vielen Dank und schönes Wochenende noch!



  • Und wenn Du über ein Makro den Zugriff einfach erzwingst?

    #define MARK(T) static bool someDummyNonCollidingNameFor##T##Marker = marker<T>::init;
    

    Klappt halt nur, wenn die Art der Initialisierung erlaubt ist



  • Eisflamme schrieb:

    Und wenn Du über ein Makro den Zugriff einfach erzwingst?

    #define MARK(T) static bool someDummyNonCollidingNameFor##T##Marker = marker<T>::init;
    

    Klappt halt nur, wenn die Art der Initialisierung erlaubt ist

    Leider geht das nicht, da ich wirklich nur solche Dinge habe wie class foo : public marker<foo> {...}; und das muss dann schon reichen. Danke aber für die Idee trotzdem 🙂

    Ich habe inzwischen eine Variante gefunden, welche auch mit Visual C++ funktioniert mit privaten virtuellen Funktionen:

    template<typename>
    struct marker : public virtual marker<>
    {
        static const bool init;
    private:
        virtual void req() const
        {
            (void)init;
        }
    };
    
    template<typename T>
    const bool marker<T>::init = ([]() -> bool 
    {
        clog << "Super Sache.";
        return true;
    })();
    

    Dies in Kombination mit dem typedef für GCC/LLVM deckt die wichtigsten Compiler-Familien ab. Nicht schön, aber das muss es auch nicht sein 😉

    Danke und gute Nacht!



  • Was hältst du davon: http://ideone.com/aerqPY

    Biste wenigstens die virtuellen Funktionen los.

    Soweit ich das jetzt getestet habe, geht es so auch auf Clang und VS2013.

    Nachteil: Eine Warnung über die ungenutzte Expression (zumindest beim Clang)



  • Das nicht-Verwerfen von Variablen oder Code erzwingt man mMn. am besten mit __declspec o.ä. Compiler-Extensions. -> Ich würde mich in der Richtung mal umsehen. Ist vermutlich auch wie wesentlich "stabilere" Lösung -- so Rumgetrickse mit anderen Konstrukten kann mit einer neuen Compilerversion schnell nimmer funktionieren.



  • ohne virtual schrieb:

    Was hältst du davon: http://ideone.com/aerqPY

    Das funktioniert sehr gut, bedingt aber leider, dass man explizit eine Instanz der Klasse erzeugt, und die Initialisierung erfolgt darüber hinaus erst genau dann, wenn der Konstruktor aufgerufen wird. Mit der virtuellen Funktion ist das nicht nötig.

    @hustbaer: Ich habe nach einer __declspec-Erweiterung gesucht, aber dort gibts nur noch so unnützes Zeugs (Altlasten von Windows 3.11) oder Sachen, die von C++11 abgedeckt werden. Oder hattest du eine bestimmte Erweiterung im Kopf? 🙂

    MfG



  • und die Initialisierung erfolgt darüber hinaus erst genau dann, wenn der Konstruktor aufgerufen wird.

    Das stimmt interessanterweise nicht. Ich habe es Mal getestet und "Hello World"-Ausgabe vor die Konstruktion geschoben. Dennoch wird "init done" zuerst ausgegeben, sogar wenn man noch einen Scope zwischenschiebt.

    Wann die Initialisierung erfolgt, wäre aber die Frage, macht der Standard da irgendwelche Zusicherungen? Und dann ist die nächste Frage, ob das Tag wirklich für Klassen wichtig ist, die niemals instanziiert werden? Gibt es die überhaupt und welchen Sinn haben die?


  • Mod

    Eisflamme schrieb:

    und die Initialisierung erfolgt darüber hinaus erst genau dann, wenn der Konstruktor aufgerufen wird.

    Das stimmt interessanterweise nicht. Ich habe es Mal getestet und "Hello World"-Ausgabe vor die Konstruktion geschoben. Dennoch wird "init done" zuerst ausgegeben, sogar wenn man noch einen Scope zwischenschiebt.

    Wann die Initialisierung erfolgt, wäre aber die Frage, macht der Standard da irgendwelche Zusicherungen? Und dann ist die nächste Frage, ob das Tag wirklich für Klassen wichtig ist, die niemals instanziiert werden? Gibt es die überhaupt und welchen Sinn haben die?

    Die Initialisierung erfolgt so, als ob die Variable an der der Stelle an der sie instantiiert wird, als gewöhnliche Variable definiert worden wäre. Es gelten dann die üblichen Regeln für die Initialisierung von Variablen mit statischer Lebensdauer (wobei ich aus dem Kopf nicht sagen kann, auf welche ÜE die Initialisierung bezogen ist - wahrscheinlich unspezifiziert, die meisten Compiler initialisieren sowieso alles vor main und das reicht meist

    Die Stelle der Instantiierung des statischen Members ist die Stelle der Instantiierung der Templateklasse - vermittelt durch die zusätzlichen Konstrukte. Eine implizite Instantiierung einer statischen Memberveraiblen erfolgt, wenn sie in einem Kontext verwendet wird, in dem ihre Definition existieren muss. Die Anwendung des Adressoperators (in einem potentiell ausgewerteten Ausdruck) ist so ein Kontext.


Anmelden zum Antworten