Zu verwende Fkt.ptr. für Klasse initialisieren - Saubere Implementierung?



  • Hi,

    Ich habe eine Klasse, welche verschiedene Funktionszeiger verwenden muss (handelt sich um OpenGL-Extensions). Die Zeiger können natürlich statische Attribute sein. Man könnte aber auch überlegen, eine Art Extension-Manager-Klasse zu bauen, damit der Zugriff zentral gekapselt wird. Dann wiederum muss ich mir aber Gedanken machen, wo ich den instanziiere.

    Meine Frage ist also, wie ich diesen Sachverhalt so sauber wie halt möglich lösen kann.

    Bisher mache ich es so und habe das Gefühl, das ist nicht so schön:

    // Header
    class Mesh
    {
    private:
    static bool pointerLoaded_;
    
    static functionPointer function1;
    static functionPointer function2;
    
    Mesh();
    };
    
    // Cpp
    bool Mesh::pointerLoaded_ = false;
    functionPointer Mesh::function1 = 0;
    functionPointer Mesh::function2 = 0;
    
    Mesh::Mesh()
    {
    if(!pointerLoaded)
    {
    function1 = getFunctionPointer("function1");
    function2 = getFunctionPointer("function2");
    }
    };
    

    Ist jetzt nur pseudo, hoffe, das reicht. Geht ja um's Design.

    In manchen Programmiersprachen gibt es statische Ctors und genau so was wäre hier eigentlich die Idee. Wie würdet ihr die Problematik lösen?

    Viele Grüße



  • Welches Problem?



  • Meine Frage ist also, wie ich diesen Sachverhalt so sauber wie halt möglich lösen kann.

    Daher auch im Topic "- Saubere Implementierung".

    Oder ist das so ok?


  • Administrator

    Für mich bleibt die Frage von Zeus bestehen: Wo ist das Problem?
    Und zwar die Aufgabenbeschreibung ist für mich nicht genügend. Ich verstehe gar nicht was du erreichen möchtest. Was ist das Ziel? Was ist die Problemstellung? Oder muss ich zuerst meinen ersten Kaffee haben? Ich verstehe die Aufgabenstellung jedenfalls nicht. 😉

    Grüssli



  • Also was schonmal nicht sauber ist, ist deine Einrückung:

    // Header
    class Mesh
    {
        private:
            static bool pointerLoaded_;
    
            static functionPointer function1;
            static functionPointer function2;
    
            Mesh();
    };
    
    // Cpp
    bool Mesh::pointerLoaded_ = false;
    functionPointer Mesh::function1 = 0;
    functionPointer Mesh::function2 = 0;
    
    Mesh::Mesh()
    {
        if(!pointerLoaded)
        {
            function1 = getFunctionPointer("function1");
            function2 = getFunctionPointer("function2");
        }
    };
    

    Übrigens ist deine Klasse Mesh komplett abgeschirmt von jedem Zugriff von außen, da alles komplett private ist, sogar der Konstruktor, was zufolge hat, dass du nicht mal ein Objekt erstellen kannst.



  • Ok, dann kann ich gerne noch mehr Infos liefern. Aber manchmal findet man auch einfach im OP die nötigen Infos.

    FreakY<3Cpp:

    Ist jetzt nur pseudo, hoffe, das reicht.

    Grundsätzlich gibt es bisher kein Problem, aber statische Variablen genieße ich immer mit Vorsicht und ich frage mich einfach, ob man das aus irgendwelchen Gründen anders designen sollte, weil das erfahrungsgemäß irgendwelche Vorzüge hat.

    Also nochmal hoffentlich klarer ausgedrückt:
    Ich habe Funktionszeiger, die ich innerhalb meines Programms einmal initialisieren muss. Diese werden zunächst nur von dieser Klasse verwendet. Mit meinem Workaround von "if(!pointerLoaded)" stelle ich jetzt sicher, dass nur einmal geladen wird.
    Frage: Kann ich das geschickter anstellen? In anderen Programmiersprachen gibt es wie gesagt statische Konstruktoren und genau das würde ich hier wollen, aber das gibt es ja nicht.
    Weitere Frage: Würdet ihr Funktionszeiger aus irgendeinem Grund globaler als als statische Attribute in einer Klasse verwalten, auch wenn noch nicht klar fest steht, dass jemand anderes diese Zeiger verwendet? Beispielsweise könnte man sich eine Klasse zur Verwaltung von Funktionszeigern vorstellen. Ich habe bisher keine direkte Idee, ob das einen Vorteil geben könnte, aber deswegen frage ich. Vielleicht sollte man Funktionszeiger generell nicht statisch in einer Klasse speichern aus irgendwelchen Gründen.

    Danke schon Mal. 🙂


  • Administrator

    Mir fehlen zwar immer noch ein paar Informationen, zum Beispiel womit die eigentlich initialisiert werden müssen? Wieso Funktionszeiger, welche nur einmal intialisiert werden? Leuchtet mir irgendwie gerade nicht so ein.

    Jedenfalls wenn die Funktionszeiger nur von der Klasse selber verwendet werden und statisch sein sollen, dann nimm die aus dem Header raus und setze sie in einem annonymen Namensraum in die Übersetzungseinheit. Dies hat den Vorteil, dass wenn du die Zeiger veränderst, du nicht alle Klassen neu kompilieren musst, welche von diesem Header abhängig sind.
    Und wieso verwendest du diese Gültigkeitsprüfung? Es gibt verschiedene Techniken, um statische Variablen zu initialisieren, bevor man sie verwendet, ohne dass man ständig auf die Gültigkeit prüfen muss. Zum Beispiel wenn du es über lokal statische Variablen in Funktionen machst:

    static FunctionPtr getFunctionBlaBlaBla()
    {
      static FunctionPtr const functionPtr = getFunctionPointer("BlaBlaBla");
      // functionPtr wird beim ersten Aufruf der Funktion initialisiert.
      // Bei allen nächsten Aufrufen, wird nur noch das return ausgeführt.
      return functionPtr;
    }
    

    Wenn ich mich recht erinnere, kannst du bei Variablen in einer Übersetzungseinheit davon ausgehen, dass sie initialisiert sind, sobald du eine Funktion aus dieser Übersetzungseinheit aufrufst. Daher:

    // Mesh.cpp
    
    #include "Mesh.hpp"
    
    // ...
    
    namespace
    {
      FunctionPtr myFunction01 = getFunctionPointer("function01");
      FunctionPtr myFunction02 = getFunctionPointer("function02");
      FunctionPtr myFunction03 = getFunctionPointer("function03");
      // usw.
    }
    
    // ...
    
    Mesh::Mesh()
    {
      // Hier ist myFunction01, myFunction02, myFunction03 usw.
      // gültig.
    }
    

    Grüssli



  • Genau, nach solchen Ideen habe ich gesucht, weil ich mir schon dachte, dass ich diese "verschiedenen Techniken" gerade einfach übersehe. 🙂

    Nur kurz zu den Fragen

    Mir fehlen zwar immer noch ein paar Informationen, zum Beispiel womit die eigentlich initialisiert werden müssen?

    Das sind halt OpenGL-Extensions, die vorher nicht so in Quellcodedateien vorliegen. Ich hole mir die letztlich über wglGetProcAddress. Das liefert mir Funktionszeiger zurück. Ist aber halt alles OGL-Stuff, was hier ja eigentlich nicht reingehört, daher hab ich's nicht dazu geschrieben.

    Wieso Funktionszeiger, welche nur einmal intialisiert werden? Leuchtet mir irgendwie gerade nicht so ein.

    Man erhält diese Zeiger halt auf OpenGL-Funktionen. Wenn die Extension unterstützt wird, werden sie halt befüllt und ich kann sie ab dann verwenden. Da sich hier nichts ändert, reicht es zu Programmbeginn die zu laden.

    Die zweite Variante würde die Methoden sogar global verfügbar machen. Problem ist hier, dass ich vorher prüfen muss, ob die Extension verfügbar ist, d.h. vor das eigentliche Laden tritt die Abfrage. Zudem würde ich gerne mit einer Variable darstellen, ob die Extensions akzeptiert werden oder nicht.

    Aber das hatte ich nicht gesagt bisher und das hilft mir jetzt schon Mal sehr weiter!


  • Administrator

    Eisflamme schrieb:

    ... OpenGL Kram ...

    Hmmm, ok. Ich kenne mich immer noch nur sehr rudimentär in OpenGL aus. Meistens verwende ich Frameworks, welche OpenGL kapseln. 😉

    Eisflamme schrieb:

    Die zweite Variante würde die Methoden sogar global verfügbar machen.

    Nein, komm nicht auf die dumme Idee, sowas in einen Header zu schreiben. Ich habe bewusst oben noch den Kommentar Mesh.cpp hingeschrieben. Sowas sollte nur in einer Übersetzungseinheit sein. Übersetzungseinheit ist eben ein *.cpp File. Ich seh schon die Erbsenzähler kommen, welche diese Definition korrigieren wollen (:D), daher:
    Grundsätzlich sind es alle Files, welche zu einem Objectfile übersetzt werden. Ich hoffe, dass stellt sie zufrieden 🙂

    Eisflamme schrieb:

    Problem ist hier, dass ich vorher prüfen muss, ob die Extension verfügbar ist, d.h. vor das eigentliche Laden tritt die Abfrage. Zudem würde ich gerne mit einer Variable darstellen, ob die Extensions akzeptiert werden oder nicht.

    Gut, dass kannst du dann über ein Objekt machen und dem Ausnutzen des Konstruktors:

    // Mesh.cpp
    
    namespace
    {
      struct FunctionPtrLoader
      {
        int flags; // oder was auch immer ...
    
        FunctionPtr myFunction01;
        FunctionPtr myFunction02;
        FunctionPtr myFunction03;
    
        FunctionPtrLoader()
        {
          // Prüfe was du zu prüfen hast, setze die Flags und lade die Funktionen...
          myFunction01 = getFunctionPointer("function01");
          myFunction02 = getFunctionPointer("function02");
          myFunction03 = getFunctionPointer("function03");
        }
      }
      theFunctionPtrLoaderObject; // <- über dieses Objekt, greifst du dann zu.
    }
    

    Für Exceptions ist die Sache natürlich nicht so gut, da man sie fast nicht fangen kann. Wenn hier irgendwelche Fehler auftreten können, welche behandelt werden müssen.
    Für diese Fälle wäre die Funktion mit statischer lokaler Variable besser:

    // Irgendwo brauchen wir:
    struct FunctionPtrLoader
    {
      // Die Member und so.
      // z.b.
      FunctionPtr ptr;
    
      FunctionPtrLoader(char const* funcName)
      {
        // Lade das Zeug, wie oben halt.
        // Bei einem Fehler Exception werfen.
      }
    }
    
    // ...
    FunctionPtr getFunctionBlaBlaBla()
    {
      static FunctionPtrLoader const loader("blablabla");
      return loader.ptr;
    }
    

    So kann beim ersten Aufruf der Funktion eine Exception auftreten. Allerdings frage ich mich da, ob du nicht eher anderswo gleich überprüfen solltest, ob diese Extensions existieren. Also Überprüfung und Initialisierung trennen.

    Grüssli



  • Hm, stimmt, man kann die Funktionen noch gruppieren. Die Struktur passt dann aber eh gut, in der kann ich dann ja auch ein boolean anbieten, über welches man abfragen kann, ob die Funktionen verfügbar sind.

    Jetzt ist die Prüfung, ob die Extension verfügbar ist, natürlich aber auch für mehrere solcher "Gruppen" praktisch.

    Hm, und was mache ich, wenn ich die Funktionen von mehreren Übersetzungseinheiten aus aufrufen möchte? (ich weiß, ich bewege mich von dem Grundgedanken etwas weg) Dann bräuchte ich ja so was wie nen globalen Funktionsverwalter. Eine statiche Klasse wär etwas ekelhaft, ein Singleton wär auch nicht das Goldene vom Pferd...



  • Also was schonmal nicht sauber ist, ist deine Einrückung:

    Och, public/protected/private können ruhig auf der selben Höhe sein wie die umfassenden Klammern der Klasse. Zb VC++ versucht sogar einem diese Einrückung aufzuzwingen 😉


Anmelden zum Antworten