C# like statischer Constructor



  • Hallo,

    ich habe mich über statische Konstruktoren in c++ informiert und etwas eingebaut, dass etwa so aussieht

    #define staticCtor(Class) my_static_ctor<Class> static##Class;
    
    	template<typename Class>
    	class my_static_ctor
    	{
    		public:
    			my_static_ctor() { Class::_init(); }
    	};
    

    und rufe den staticCtor ein mal in der cpp auf. Die Frage ist jetzt allerdings, da das Gebilde grundsätzlich IMMER aufgerufen wird, wenn das Programm startet, ob es nicht ein Möglichkeit gibt das so umzuschreiben, dass es nur bei Aufruf einer Methode oder erzeugung eines Objektes ein einziges Mal aufgerufen wird, wie es bei C# der Fall ist?



  • Es ist zwar etwas konfus, was Du schreibst (Immer beim Programmstart vs. nur einmal), aber ich tippe mal darauf, dass Du glaubst, ein Singleton haben zu wollen.
    Du kannst den Begriff ja mal mit einer Suchmaschine Deiner Wahl suchen.



  • Ähm ... Nein.

    Es darf ja ruhig mehrere Instanzen eines Objektes geben, jedoch sollen ein mal bei der ersten erzeugung des Objektes bestimmte Funktionen aufgerufen werden (eben ein statischer Konstruktor) ohne dann jedes mal in den entsprechenden Instanzkonstruktor reinzuschreiben

    mycalss
    {
       if its the first isnatnce
          block with functions that have to be executed
    
       ctor stuff
    }
    

    Da ich mit OGL arbeite und die Erweiterungen zentral verwalte, wollte ich dann für das Beispiel der Shader-Class folgendes Konstrukt haben

    class Shader
    {
        public:
           static_ctor()
           {
              //called only once during whole runtime
              for every pointer in pointers
                if(EXTENSIONPOINTER == 0)
                  EXTENSIONPOINTER = (binding of pointer)
              ...
           }
    
           ctor() 
           {
             //called once for each instance
           }
    
           private:
              static EXTENSIONPOINTER extension
              ...
    };
    

  • Mod

    Meinst du so etwas?

    #include <iostream>
    
    struct static_data
    {
      static_data() { std::cout << "constructing static data\n"; }
    };
    
    class foo
    {
      static static_data data;
    public:
      foo() { std::cout << "constructing foo\n"; }
    };
    
    static_data foo::data;
    
    int main()
    {
      for(int i = 0; i < 10; ++i)
        foo f;
    }
    


  • Ich glaube das hier

    template<class T> class StaticConstructor
    {
        bool m_StaticsInitialised = false;
    
    public:
        typedef void (*StaticCallback)(void);
    
        StaticConstructor(StaticCallback callback)
        {
            if (m_StaticsInitialised)
                return;
    
            callback();
    
            m_StaticsInitialised = true;
        }
    }
    
    template<class T> bool StaticConstructor<T>::m_StaticsInitialised;
    
    class Test : public StaticConstructor<Test>
    {
        static std::vector<char> letters_;
    
        static void _Test()
        {
            for (char c = 'a'; c <= 'z'; c++)
                letters_.push_back(c);
        }
    
    public:
        Test() : StaticConstructor<Test>(&_Test)
        {
            // non static stuff
        };
    };
    

    passt besser

    [Quelle] http://www.streamreader.org/stackoverflow/questions/1197106/static-constructors-in-c-need-to-initialize-private-static-objects


  • Mod

    Was soll da dran denn besser passen, außer dass du völlig unnötigen Overhead eingebaut hast, der dir jeden normalen Konstruktoraufruf nicht-trivial macht und eine zusätzliche Abfrage einbaut, die gar nicht nötig wäre?



  • Pria schrieb:

    Ich glaube das hier [...] passt besser

    😕

    Was soll das CRTP dort?
    Wieso ist m_StaticsInitialised nicht statisch?

    Die übliche und threadsichere Lösung ist:

    #include <iostream>
    
    struct static_data_ {
      static_data() { std::cout << "constructing static data\n"; }
    };
    static_data_& static_data() { static static_data_ sd; return sd; }
    
    class foo
    {
    public:
      void bar() { static_data().foobar(); } // static_data_ wird erst dann konstruiert, wenn gebraucht.
    };
    


  • SeppJ: Dein Vorschlag hat aber nicht den Effekt, dass der statische Ctor erst bei der ersten Erstellung eines Objekts der Klasse aufgerufen wird, sondern dass es bei Programmstart auf jeden Fall aufgerufen wird.

    _confused_: bei dir muss an allen möglichen Stellen static_data() aufgerufen werden, was eben genau nicht das ist was der OP möchte.

    Mein erster Gedanke ging in die Richtung von dem was Pria geschrieben hat, kann man allerdings noch verbessern:

    template <class T>
    struct StaticCtor
    {
    protected:
      StaticCtor() 
      {
        static auto b = []() -> bool {T::Init(); return true;}();
      }
    };
    
    #include <iostream>
    
    struct Foo : StaticCtor<Foo>
    {
      static void Init() { std::cout << "first use of Foo!\n"; }  
    };
    
    int main()
    {
      std::cout << 1 << '\n';
      Foo a;
      std::cout << 2 << '\n';
      Foo b;
      std::cout << 3 << '\n';
      Foo c;
      std::cout << 4 << '\n';
    }
    


  • Die Vererbung ist unnötig.

    Ich würde es trennen - dann wird der Code auch gleich viel viel schöner.



  • Shade Of Mine schrieb:

    Die Vererbung ist unnötig.

    Hast recht - neue Version:

    template <void (*fun)()>
    struct StaticCtor
    {
      StaticCtor() 
      {
        static auto b = []() -> bool {fun(); return true;}();
      }
    };
    
    #include <iostream>
    
    struct Foo
    {
      static void Init() { std::cout << "first use of Foo!\n"; }  
      StaticCtor<Init> sc;
    };
    
    int main()
    {
      std::cout << 1;
      Foo a;
      std::cout << 2;
      Foo b;
      std::cout << 3;
    }
    

    -> ich kann alles mögliche als staischen ctor angeben, auch freie Funktionen...


  • Mod

    pumuckl schrieb:

    SeppJ: Dein Vorschlag hat aber nicht den Effekt, dass der statische Ctor erst bei der ersten Erstellung eines Objekts der Klasse aufgerufen wird, sondern dass es bei Programmstart auf jeden Fall aufgerufen wird.

    Als ich nach dem Stichwort gegoogelt habe, stand in der MSDN:

    Static constructors have the following properties:

    [ ... ]
    A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced.

    [ ... ]

    The user has no control on when the static constructor is executed in the program.

    Hervorhebung durch mich.



  • Okay, ich hatte mich eher auf die Fragestellung des OP konzentriert, nicht darauf, ob der letzte Teil korrekt war:

    Pria schrieb:

    Die Frage ist jetzt allerdings, [...] ob es nicht ein Möglichkeit gibt das so umzuschreiben, dass es nur bei Aufruf einer Methode oder erzeugung eines Objektes ein einziges Mal aufgerufen wird, wie es bei C# der Fall ist?

    Dass es bei C# nicht der Fall ist hast du jetzt aufgezeigt. Jetzt darf Pria sich aussuchen, ob es das "wie bei C#" haben möchte oder so, wie er dir Frage formuliert hat 😉



  • Hallo nochmal,

    das verursacht bei mir immer einen Fehler wenn ich schreibe

    template<typename T>
    	struct static_ctor
    	{
    		public:
    			static_ctor() 
    			{ 
    				static auto b = []() -> bool {T::_init(); return true;}(); 
    			}
    	};
    
    error C2159: Mehr als eine Speicherklasse angegeben
    


  • Das ist C++11, beim GCC z.B. musst du da mit -std=c++11 kompilieren. Aber so ganz nachvollziehen kann ich die Lösung nicht, ich mache das immer so:

    #include <iostream>
    
    struct foo
    {
      static foo& get_instance()
      {
        static foo f;
        return f;
      }
    
    private:
      foo()
      {
        std::cout << "Initialized\n";
      }
      ~foo()
      {
        std::cout << "Destructor\n";
      }
    };
    
    struct bar
    {
      bar()
      {
        static foo& dummy = foo::get_instance();
        std::cout << "Used..\n";
      }
    };
    
    int main()
    {
      std::cout << "!!!\n";
      bar b1, b2, b3, b4, b5;
    }
    


  • OK! Das wird wohl der Fehler gewesen sein. Nachdem ich alles wieder auf
    die "Ursprungslösung" gesetzt habe, klappt es nun.

    Aber so ganz nachvollziehen kann ich die Lösung nicht, ich mache das immer so

    Nun ja, die Lösung funktioniert und ich kann sie für mein Projekt einsetzen, dass ist doch die Hauptsache :xmas1:



  • Pria schrieb:

    Nun ja, die Lösung funktioniert und ich kann sie für mein Projekt einsetzen, dass ist doch die Hauptsache :xmas1:

    Und meine funktioniert nicht? :xmas2:



  • Habe ich nicht gesagt


Anmelden zum Antworten