dynamic_cast testen



  • auchabgeleitet schrieb:

    rttiTester schrieb:

    Durch Änderungen im Code ist es öfter als einmal vorgekommen, dass mit dem static_cast auf die falsche Klasse konvertiert worden ist

    dynamic_cast ist hier der falsche Ansatz. Das kostet nur Performance (und nicht zu knapp!!)

    Das ist mal wieder so ein "premature optimization". Es kommt immer darauf an, ob das wirklich relevant ist. Sicher ist dynamic_cast nicht die schnellste Möglichkeit, aber vielleicht an der Stelle die übersichtlichste und robusteste Variante. Da fallen ein paar CPU-Zyklen dann wirklich nicht ins Gewicht.



  • Meistens will man static_cast für Downcasts. Und dynamic_cast wird dann höchstens zur Kontrolle im Debug-Modus verwendet. Du kannst das auch kombinieren, wie boost::polymorphic_downcast .

    Mit if und dynamic_cast verbirgst du nur Logikfehler. Du gehst davon aus, dass sich eine Klasse downcasten lässt, wie reagierst du bei falschen Typen? Du kannst nicht reagieren. Das einzig Richtige hier ist ein assert() o.Ä., sodass dieser Fall sicher nicht durch den Debugger kommt. Einfach eine if -Abfrage und nichts tun, bedeutet Logikfehler ignorieren und Applikation in korruptem Status weiterlaufen lassen.

    auchabgeleitet schrieb:

    Weil das offensichtlich langsam ist.
    Libraryübergreifend geht das nicht viel besser.

    Warum ist typeid langsamer als getType() ?

    auchabgeleitet schrieb:

    Es steht dir natürlich auch frei, das assert durch ein throw InvalidClassCastExecutorManagerException() zu ersetzen.

    🤡

    tntnet schrieb:

    Sicher ist dynamic_cast nicht die schnellste Möglichkeit, aber vielleicht an der Stelle die übersichtlichste und robusteste Variante. Da fallen ein paar CPU-Zyklen dann wirklich nicht ins Gewicht.

    Du weisst ja nicht, wie oft die Downcasts vorkommen, dynamic_cast kann schon ins Gewicht fallen. Vor allem wenn man bedenkt, dass der Overhead bei korrektem Code nicht nötig ist. Wie gesagt ist schon der Ansatz, dynamic_cast hier verwenden zu wollen, verkehrt -- Performance ist dabei nebensächlich.



  • tntnet schrieb:

    Sicher ist dynamic_cast nicht die schnellste Möglichkeit, aber vielleicht an der Stelle die übersichtlichste und robusteste Variante.

    Stimmt, ich so gewohnt, dass GetType() nur an performance-kritischen Stellen vorkommt, dass ich stillschweigend davon ausgegangen bin.

    Übersichtliche und robuste Alternativen sind virtuelle Funktionen im Objekt oder das Visitor-Pattern.

    Nexus schrieb:

    Warum ist typeid langsamer als getType() ?

    Der Test, ob A von B erbt ist ungefähr so implementiert:

    for (auto a_type = typeid(A); a_type.name() != typeid(B).name();) {
    //                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- das ist richtig lahm
      if (a_type.has_parent())
        a_type = a_type.parent();
      else
        return false;
    }
    return true;
    

    Jetzt vergleich das mal mit einem einfachen Funktionspointeraufruf.

    Ich gehe sogar so weit und habe ein Feld mit der ID. Dann ist das nicht mal ein Funktionsaufruf.



  • Korrigierte version:

    bool a_erbt_von_b(type_info a_type, type_info b_type) {
      if (a_type.name() == b_type.name();
        return true;
      for (type_info p : a_type.parents())
       if (a_erbt_von_b(p, b))
          return true;
      return false;
    }
    


  • ok, um es kurz zu sagen:
    klar wäre es mir auch lieber, wenn das design dementsprechend wäre, dass man ohne typinfos auskommt.
    ist aber nicht so.
    und ich kann mich auch nicht 100% darauf verlassen, dass ich immer die richtigen typen übergeben bekomme. auch nicht schön.
    ist aber so.
    ich brauche jetzt erstmal eine robuste methode, um sichere downcasts durchzuführen. und dafür ist dynamic_cast geeignet.

    also bitte keine bekehrungen im sinne von "dynamic_cast ist ungeeignet". ich werde es im ersten schritt verwenden müssen. mir ist es lieber, wenn die SW beim kunden etwas langsamer ist, als wenn es kracht.

    also daher nochmals meine ursprüngliche frage, auf die bis jetzt keine antwort kam: welche tests sind nötig, um zu sehen, ob dynamic_cast einwandfrei funktioniert? was wären "härtefälle"? reicht der test, den ich oben präsentiert habe?



  • Ich verstehe immer noch nicht, warum GetTyp() besser als typeid sein sollte. Ich wuerde naemlich den Typ in der vtable hinterlegen oder aehnlich.

    Weiterhin funktioniert die vorgestellte Loesung nur mit Blaettern in der Vererbungshierarchie. dynamic_cast beruecksichtigt auch Zwischenknoten. Deswegen wird wohl dynamic_cast auch langsamer sein als GetType/typeid, aber beide Ansaetze sind sowieso nicht aequivalent.



  • auchabgeleitet schrieb:

    Der Test, ob A von B erbt ist ungefähr so implementiert:

    warum sollte es das sein? dr COmpiler kann doch einfach einen Baum speichern der Pro Typ ienen Knoten hat und dann pro Instanz die ID des passenden Klassenknoten speichern. mit der id findest du dann den passenden Knoten im Baum und upcasten ist dann einfach nur die parents solange traversieren bis in Knoten die richtige ID hat. crosscast ist dann Breitensuche im Baum.


  • Mod

    knivil schrieb:

    Weiterhin funktioniert die vorgestellte Loesung nur mit Blaettern in der Vererbungshierarchie. dynamic_cast beruecksichtigt auch Zwischenknoten.

    und Mehrdeutigkeiten. Die Suche kann also bei einem Fund nicht sofort abgebrochen werden. Die Laufzeit eines cross-Casts hängt damit wesentlich von der Gesamtgröße der Vererbungshierarchie ab.
    Einfache Downcasts gehen nat. schneller. Was nicht hilft, wenn der Cast fehlschlägt (dann musste ja der Algorithmus für den Crosscast erst mal angewendet werden).



  • a_type.name() != typeid(B).name()
    

    Meine Güte, liest hier keiner Alexandrescu? Das funktioniert nicht! Es ist nicht garantiert dass zwei Klassen unterschiedliche " type_info "-Namen haben, und auch nicht dass der Name überhaupt gleich bleibt (in der anderen Implementierung oder auch Version)!



  • Deswegen nimmt man ja auch nicht den String sondern die Id. Stringvergleiche sind unnoetig.



  • Sone schrieb:

    a_type.name() != typeid(B).name()
    

    Meine Güte, liest hier keiner Alexandrescu?

    Viel zu viele Leute lesen Alexandrescu.

    Dir ist schon bewusst, dass das eine Beispielimplementation für dynamic_cast war? Die Compilerhersteller werden schon wsisen, ob sie kompatibel zu sich selbst sind.



  • Ich übersehe wohl was. Wozu braucht man Upcasts und Downcasts?
    Die korrekte Lösung für das hier:

    auchabgeleitet schrieb:

    Basis *b=ListeVonPolymorphenObjekten[x];
    if(b->GetTyp()==eTypA)
    {
     Abgeleitet *a=basis_cast<Abgeleitet*>(b); // bummm schon hier!
     a->EineFunktionVonAbgeleitet();
    }
    

    ist doch wohl sowas in der Art:

    struct Basis{
     virtual void rufDieRichtigeFunktionAuf(){
     }
    };
    struct Abgeleitet{
     void EineFunktionVonAbgeleitet();
     void rufDieRichtigeFunktionAuf(){
      EineFunktionVonAbgeleitet();
     }
    };
    
    ...
    
    Basis *b=ListeVonPolymorphenObjekten[x];
    b->rufDieRichtigeFunktionAuf();
    

    Jetzt, da RTTI angeschaltet ist sollte das doch funktionieren und der Compiler wird sich schon was Schlaues und Optimiertes für die Implementierung überlegen.

    Wenn man das nicht will, weil die abgeleiteten Klassen zu verschieden sind, als dass man für alles eine virtuelle Funktion anlegen will, dann sollte man vielleicht nicht alles von der Basis ableiten und in einen Container stopfen.



  • auchabgeleitet schrieb:

    for (auto a_type = typeid(A); a_type.name() != typeid(B).name();) {
    //                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- das ist richtig lahm
    

    Macht man ja auch nicht. std::type_info (und std::type_index ) ist direkt vergleichbar. Der Vergleich kann z.B. auch mit Integern implementiert werden.

    Ich würde hier nicht zu viel spekulieren, die Performance von RTTI hängt massgeblich von der Compiler-Implementierung ab. Ausserdem hat man meist andere Probleme, wenn man RTTI benötigt.


  • Mod

    nwp3 schrieb:

    Ich übersehe wohl was. Wozu braucht man Upcasts und Downcasts?
    Die korrekte Lösung für das hier:

    auchabgeleitet schrieb:

    Basis *b=ListeVonPolymorphenObjekten[x];
    if(b->GetTyp()==eTypA)
    {
     Abgeleitet *a=basis_cast<Abgeleitet*>(b); // bummm schon hier!
     a->EineFunktionVonAbgeleitet();
    }
    

    ist doch wohl sowas in der Art:

    struct Basis{
     virtual void rufDieRichtigeFunktionAuf(){
     }
    };
    struct Abgeleitet{
     void EineFunktionVonAbgeleitet();
     void rufDieRichtigeFunktionAuf(){
      EineFunktionVonAbgeleitet();
     }
    };
    
    ...
    
    Basis *b=ListeVonPolymorphenObjekten[x];
    b->rufDieRichtigeFunktionAuf();
    

    Jetzt, da RTTI angeschaltet ist sollte das doch funktionieren und der Compiler wird sich schon was Schlaues und Optimiertes für die Implementierung überlegen.

    Wenn man das nicht will, weil die abgeleiteten Klassen zu verschieden sind, als dass man für alles eine virtuelle Funktion anlegen will, dann sollte man vielleicht nicht alles von der Basis ableiten und in einen Container stopfen.

    Das geht sogar ohne RTTI, entspricht aber nicht dem gestellten Problem, als da wäre mit Äpfeln und Birnen umzugehen ohne über Obst zu sprechen.


Anmelden zum Antworten