Designfrage: Singleton "installieren"



  • Danke für den ersten Ansatz. Habe das ganze jetzt so gemacht, allerdings mit intelligenten Zeigern.

    Einziges Problem ist, dass der Strategiewechsel immer noch zur Laufzeit passiert, und nicht schon zur Compilezeit gewechselt werden kann.

    Ich fordere ja jetzt, dass der Library-Nutzer eine Strategie implementiert und dann

    Singleton::getLogger().setStrategy(new MeineStrategy);
    

    aufruft. Wenn dieser Aufruf im Code steht, läuft das Programm ja schon. Das heißt, statisch initialisierte Objekte, die in ihren Konstruktoren was geloggt haben, haben noch die default-Strategie verwendet.

    Bei meiner alten Lösung war durch das implementieren der Singelton::getLogger Funktion ja schon zur Kompilierzeit klar, was für eine Strategie verwendet werden soll.

    Gibt's irgendeine Template-Magie nach dem Motto "aha, ich wurde implementiert, dann installier ich mich mal in der Klasse XYZ"?



  • PhilippM schrieb:

    Danke für den ersten Ansatz. Habe das ganze jetzt so gemacht, allerdings mit intelligenten Zeigern.

    Einziges Problem ist, dass der Strategiewechsel immer noch zur Laufzeit passiert, und nicht schon zur Compilezeit gewechselt werden kann.

    Ich fordere ja jetzt, dass der Library-Nutzer eine Strategie implementiert und dann

    Singleton::getLogger().setStrategy(new MeineStrategy);
    

    aufruft. Wenn dieser Aufruf im Code steht, läuft das Programm ja schon. Das heißt, statisch initialisierte Objekte, die in ihren Konstruktoren was geloggt haben, haben noch die default-Strategie verwendet.

    Bei meiner alten Lösung war durch das implementieren der Singelton::getLogger Funktion ja schon zur Kompilierzeit klar, was für eine Strategie verwendet werden soll.

    Gibt's irgendeine Template-Magie nach dem Motto "aha, ich wurde implementiert, dann installier ich mich mal in der Klasse XYZ"?

    Hy

    Ich versteh jetzt nicht ganz dein Problem, wenn du diese setStrategy-Funktion ganz am Anfang der Main aufrufst, dann wird diese doch auch für alle Konstruktoren festgelegt. Bzw. wenn du dies so in deinen Code vor den Funktionen aufrufst sollte dies funktionieren:

    namespace {
       Singleton::getLoger().setStrategy( new MeineStrategy );
    }
    

    Zumindest nehme ich diese Technik bei meinen Projekt die Möglichkeit zu haben das sie die Plugins die ich über Dlls lade sich selbst eintragen.

    Mfg marco



  • Marc-O schrieb:

    Ich versteh jetzt nicht ganz dein Problem, wenn du diese setStrategy-Funktion ganz am Anfang der Main aufrufst, dann wird diese doch auch für alle Konstruktoren festgelegt.

    Statische Objekte werden aber vor dem Eintritt in die main angelegt.

    Bzw. wenn du dies so in deinen Code vor den Funktionen aufrufst sollte dies funktionieren:

    namespace {
       Singleton::getLoger().setStrategy( new MeineStrategy );
    }
    

    Aber leider ist die Reihenfolge solcher statischen Initialisierungen über mehrere Compileunits nicht definiert. Das kann also irgendwann initialisiert werden. Dann habe ich wahrscheinlich manche Konstruktoren, die mit der default-strategy loggen, und ein paar, die mit MeineStrategy loggen. Klingt mir nicht nach einer guten Idee.

    Phil



  • Vielleicht so etwas?

    class ConsoleLogger{};
    
    template<typename T>
    class SingletonHolder : public boost::noncopyable
    {
    private:
    // ...
    
    public:
    	static T& getLogger() { static T logger; return logger; }
    
    };
    
    // Library Header Template
    typedef SingletonHolder<ConsoleLogger> Singleton;
    
    // somewhere in a file...
    class A
    {
       A() { Singleton::getLogger().writeLog("..."); }
    };
    
    A a;
    
    // ......................
    
    int main()
    {
    	Singleton::getLogger();
    }
    

    Wer keinen Logger schreibt, nutzt das Library Header Template "as is", wer einen eigenen Logger einhängt, ändert nur den Typedef

    :schland: :schland:



  • Das klingt nach einem richtig guten Plan, hat aber noch einen Schönheitsfehler:
    Wenn ich den default-Logger mit

    typedef SingletonHolder<ConsoleLogger> Singleton;
    

    installiere, der User dann aber seine eingene Implementierung baut und

    typedef SingletonHolder<MyFancyLogger> Singleton;
    

    macht, dann gibts einen conflicting typedef.



  • Herren der schwarzen Template-Magie, wo seid ihr? 🙄



  • typedef SingletonHolder<ConsoleLogger> MyConsoleLogger;
    
    typedef SingletonHolder<FancyLogger> MyFancyLogger;
    

    und alles wird gut 😮



  • Ich geb's auf und geh Schrauben sortieren ... 😞



  • PhilippM schrieb:

    Ich geb's auf und geh Schrauben sortieren ... 😞

    Das Heizöl hacken hast mir besser gefallen 😃


  • Administrator

    Wenn du eine Strategie zur Kompilezeit festlegen willst, dann mach das über Policies.

    Grüssli



  • Als dynamische Bibliothek wirst du das in der Form jedenfalls nicht hinkriegen - du willst die Strategie zur Compilezeit festlegen, und wenn die Bibliothek einmal kompiliert ist, ist die Compilezeit vorbei.

    Denkbar wäre, es zur Linkzeit zu verschieben, so dass der Benutzer dir eine andere Bibliothek hinlegen kann, aus der sich deine Klasse entsprechende Funktionen holen kann. Allerdings ist das ziemlich umständlich, insbesondere, wenn das Backend parametrisiert werden will - etwa ein Dateilogger, der einen Dateinamen braucht.



  • Dravere schrieb:

    Wenn du eine Strategie zur Kompilezeit festlegen willst, dann mach das über Policies.

    Grüssli

    Auch die sehen sher interessant aus, lösen aber nicht mein Problem:
    Auch hier muss ich ja eine standard-policy per typedef festlegen.
    Und wenn dann der Librarynutzer seine andere Policy geschrieben hat, kann er kein typedef mehr anlegen, dass diese Policy zum standard erklärt. Conflicting typedef. Ich glaub wir drehen uns gerade im Kreis!



  • Okay, neuer Vorschlag:
    Wenn wir Problem 1 nicht lösen können, dann wenigstens Problem 2:
    Gibt es eine Möglichkeit dem Linker beim Zusammenbauen der dynamsichen Library zu sagen "Das excutyble, das diese lib später benutzen wird, bietet garantiert die Funktion XYZ an", so dass ich den alten Mechanismus mit dem undefiniert lassen und erst in der Applikation definieren, in die dynamische library rüberretten kann?


  • Administrator

    PhilippM schrieb:

    Auch die sehen sher interessant aus, lösen aber nicht mein Problem:
    Auch hier muss ich ja eine standard-policy per typedef festlegen.
    Und wenn dann der Librarynutzer seine andere Policy geschrieben hat, kann er kein typedef mehr anlegen, dass diese Policy zum standard erklärt. Conflicting typedef. Ich glaub wir drehen uns gerade im Kreis!

    Moment, heisst das, dass der Logger bereits schon für die statischen und globalen Objekte deiner Bibliothek funktionieren soll, welche du als vorkompilierte DLL oder ähnliches mitliefern willst? Das kannst du kreuzweise vergessen.

    Aber was du machen kannst, ist eine Standardstrategie zu definieren, welche die Log-Einträge irgendwo abspeichert, bis der User eine Strategie definiert, mit welcher diese Einträge verwaltet werden können. Sobald er also setStrategy aufruft, werden alle bisherigen Einträge mit dieser Strategie behandelt.

    Grüssli



  • PhilippM schrieb:

    Okay, neuer Vorschlag:
    Wenn wir Problem 1 nicht lösen können, dann wenigstens Problem 2:
    Gibt es eine Möglichkeit dem Linker beim Zusammenbauen der dynamsichen Library zu sagen "Das excutyble, das diese lib später benutzen wird, bietet garantiert die Funktion XYZ an", so dass ich den alten Mechanismus mit dem undefiniert lassen und erst in der Applikation definieren, in die dynamische library rüberretten kann?

    Das geht, kann aber compilerabhängig Kunstgriffe erfordern. Wenn ich mich recht entsinne, muss bei Verwendung des gcc beispielsweise das Programm mit -Wl,-E kompiliert werden, damit die Executable eine dynamische Symboltabelle bekommt, die der Linker benutzen kann.

    Wie es bei anderen Compilern aussieht, kann ich dir aus dem Stand nicht sagen - ich hab sowas nie gebraucht.


Anmelden zum Antworten