Typenvariable Argumente, aber Boost.Any schlechter Stil?



  • Das geht so aber nicht, weil die std::function einen festen Call-Typ erwartet, daher kannst du kein Event-Spezifischen Typ als Parameter verwenden.



  • Schade. Also doch Pointer-Casting. Oder kann Boost.Function das?



  • Du kannst dir eine "Trampolin"-Funktion schreiben, die zwischen std::function (wozu Boost?) und deinem OnUserAuthed() liegt. Sie führt einen Downcast durch und gibt den Zeiger an OnUserAuthed() weiter.

    Dabei musst du natürlich Typsicherheit garantieren, sonst wird das mit dem Downcast nichts.



  • Wenn ich dich richtig verstanden habe, ist das Problem, dass du zur Compilezeit noch nicht weisst, welche Nachrichten kommen, und du in Zukunft ohne das das Hauptprogramm neu compiliert wird weitere Nachrichten hinzukommen?

    Falls ja, das exakt selbe Problem habe ich gelöst, indem ich die Nachrichten als XML-Text verschickt habe.

    Jeder kann sich als Nachrichten-Empfänger anhängen (via boost::signals). Wer was mit der Nachricht anfangen kann, bearbeitet sie, und schickt die Antwort ebenfalls per XML.



  • BadWolf schrieb:

    Wenn ich dich richtig verstanden habe, ist das Problem, dass du zur Compilezeit noch nicht weisst, welche Nachrichten kommen, und du in Zukunft ohne das das Hauptprogramm neu compiliert wird weitere Nachrichten hinzukommen?

    Falls ja, das exakt selbe Problem habe ich gelöst, indem ich die Nachrichten als XML-Text verschickt habe.

    Jeder kann sich als Nachrichten-Empfänger anhängen (via boost::signals). Wer was mit der Nachricht anfangen kann, bearbeitet sie, und schickt die Antwort ebenfalls per XML.

    Optimalerweise gibt es für die Nachrichten auch Dokumenttypdefinitionen. Außer bei so platten Sachen wie "Anzeigen der Nachricht" braucht der Empfänger ja eine Wissen darüber, was er zu erwarten hat.
    Also teilen sich Sender und Empfänger eine DTD zu dieser XML-Nachricht. Das Kernsystem muß davon überhaupt keine Ahnung haben, nicht die Geringte. Es sieht nur XML, ohne reinzugucken. Es können zwei Plugins trotzdem Nachrichten austauschen, sie sind sich eben zur Compilezeit über das Format einig.

    Na, mal hoffen, daß das nicht nach inner platform effect riecht.

    Headerfile statt DTD und Basisklassenzeiger statt XML und man hats als Sprachmittel. Ich verstehe Eure Motivation nicht. Odert redest Du gerade bloß davon, Nachrichten zu serialisieren, damit man sie übers Netz schicken kann?



  • Der einzige Unterschied besteht darin, dass es nicht zwingend abstürzt, wenn man sich doch nicht an die DTD hält, oder? Da wäre doch dynamic_cast auch nicht schlechter und würde keine Multi-Millionen-Zeilen XML-Bibliothek und einen DTD-Server benötigen.



  • Okay, dynamic_cast verlangt natürlich nach einem kompatiblen Compiler... Dann halt eine normale struct mit GUID-Message Ids und reinterpret_cast...



  • Decimad schrieb:

    Okay, dynamic_cast verlangt natürlich nach einem kompatiblen Compiler... Dann halt eine normale struct mit GUID-Message Ids und reinterpret_cast...

    static_cast



  • Okay, ich muss das jetzt ein für alle mal lernen. Macht es denn bei so einer Form von Downcast einen Unterschied, ob ich reinterpret_cast oder static_cast verwende? Kann die abgelittene Klasse irgendeine Form von Alignment verändern, dass reintepret_cast falsch ist? Okay, static_cast kann anspringen wenn es sieht, dass die Klasse gar nicht in Verbindung stehen. Aber kann es auch zur Laufzeit was verändern? Ich meine die Nachricht kann ja in keinem für diese Nachrichten spezifischen Adressraum sein, oder durch einen Cast dazwischen wandern.

    Danke schonmal für Aufklärung.

    Edit: Hrmmm, mehrfach-Vererbung vielleicht? Die konkrete Nachricht leitet von der Basisnachricht und etwas anderem ab, eventuell in anderer Reihenfolge, sodass die Basisnachricht nicht "vorne" im Speicher liegt?
    Edit2: Herrje, wie funktioniert in solch einem Fall eigentlich das delete? Okay, die Zerstörung geht dank virtuellem Destruktor ja problemlos, aber wie bekommt delete die Richtige Basisadresse raus? Gibt's da eine versteckte virtuelle Funktion, die die Basisadresse der konkreten Klasse ausspuckt?



  • reinterpret_cast garantiert nur, daß wenn man einen zeiger in einen beliebigen typen und wieder zurück castst, wieder das original erhält (platz muss natürlich genug da sein). über die gültigkeit des zwischenergebnisses habe ich keine ahnung, fürchte ich.



  • Decimad schrieb:

    Macht es denn bei so einer Form von Downcast einen Unterschied, ob ich reinterpret_cast oder static_cast verwende?

    Ich weiß jetzt nicht genau, was für einen Cast du meinst. Aber wenn du dich innerhalb einer Vererbungshierarchie bewegst, solltest du entweder dynamic_cast oder static_cast verwenden. Bei reinterpret_cast findet ein ggf notwendiges Adjustment nicht statt. Das knallt dir dann spätestens bei Mehrfachvererbung um die Ohren, siehe hier.

    #include <iostream>
    
    using namespace std;
    
    struct A {int i;};
    struct B {int j;};
    struct C : A, B {};
    
    int main()
    {
      C c;
      B& b = c;
      C& c2 = static_cast<C&>(b);
      C& c3 = reinterpret_cast<C&>(b);
      cout << &c << endl;
      cout << &b << endl;
      cout << &c2 << endl;
      cout << &c3 << endl;
    }
    

    Bei meinem Test sieht man, dass die Adressen &c und &b unterschiedlich sind. Der static_cast für c2 ist quasi die Umkehrung dieser impliziten Wandlung und stellt die richtige Adresse wieder her. Der reinterpret_cast führt hier dazu, dass c3 keine gültige Referenz auf ein C-Objekt ist.



  • Okay, danke Dir für die ausführliche Erklärung!
    Ich find Mehrfachvererbung sowieso blöd, das führt immer zu Streitereien! 🙂

    Hast Du auch eine Ahnung wie das mit dem Löschen bei Mehrfachvererbung läuft, wie ich im zweiten Edit fragte?



  • Decimad schrieb:

    Edit2: Herrje, wie funktioniert in solch einem Fall eigentlich das delete? Okay, die Zerstörung geht dank virtuellem Destruktor ja problemlos, aber wie bekommt delete die Richtige Basisadresse raus? Gibt's da eine versteckte virtuelle Funktion, die die Basisadresse der konkreten Klasse ausspuckt?

    Naja, dass es für die Implementierung kein Problem sein kann/darf, die "Basisadresse" zu ermitteln, sollte ja wohl klar sein.

    Der VTable-Zeiger eines z.B. Foo-Teilobjekts das Teil eines Bars ist zeigt (bei Mehrfachverrebung) ja nicht auf den Foo-VTable oder Bar-VTable, sondern auf einen speziellen Foo-in-Bar-VTable.
    Da der VTable nicht nur die Adressen von virtuellen Funktionen enthält, sondern auch einen Zeiger auf Informationen über das Layout des konkreten Objekts, kann man da einiges machen.
    Ist auch nötig, sonst könnte dynamic_cast nicht implementiert werden.

    Und um deine konkrete Frage auch noch zu beantworten: ja, es gibt so eine Funktion, und zwar dynamic_cast<void*>(p) (wobei das nicht unbedingt eine versteckte virtuelle Funktion ist, aber es werden sicher Informationen aus dem VTable oder darin verlinkten Datenstrukturen verwendet). Das liefert die Adresse des "most derived object" -- also die Anfangsadresse des konkreten Objekts.
    Das wird auch die Adresse sein die üblicherweise an operator delete übergeben wird. Bzw. ich meine sogar dass es immer die Adresse sein muss die an operator delete übergeben wird, ich glaube nicht dass der Standard hier genug Spielraum lässt es anders zu implementieren.


Anmelden zum Antworten