dynamic_cast & virtual



  • volkard schrieb:

    edit: oh, ich rede über was, was es gar nicht gibt. wie heißen implizite casts korrekt?

    Ein Cast ist eine *explizite* Typumwandlung. Die macht man entweder mit einer "Cast-Expression", oder der "functional notation" oder mit einem
    der vier sogenannte Cast-Operatoren: dynamic_cast, static_cast, reinterpret_cast und const_cast.

    Dann gibt es noch sogenannte *implizite* Typumwandlungen oder auch Typkonverierungen. Dazu gehören z.B. so Dinge wie Standardkonvertierungen, Integrale Promotionen usw, aber auch die sogenannten "conversion operators" (Konvertierungsoperatoren). Das sind die Dinger die man mit der Syntax operator Type() in Klassen definieren kann und die neben Konstruktoren die mit einem Argument aufrufbar sind für *implizite* Typumwandlungen von Objekten verwendet werden.

    Casts muss der Mensch selbst hinschreiben. Implizite Typumwandlungen (Konvertierungen) macht der Compiler von alleine. Definitiv ein Unterschied.



  • HumeSikkins schrieb:

    Casts muss der Mensch selbst hinschreiben. Implizite Typumwandlungen (Konvertierungen) macht der Compiler von alleine. Definitiv ein Unterschied.

    ok, da habe ich ein wenig geschludert und "casten" viel zu häufig für "typumwandeln" verwendet.

    gemein wirds bei umwandelnden konstruktionen wie

    for(int i=0;i<256;++i){
       char c=i;
       cout<<c;
    }
    

    da würde ich sagen: durch den trick mit dem char habe ich i vor dem ausgeben in einen char gecastet. dem wesen (schönes wort) nach wahr, aber formal völliger quatsch. und irgendwann fängt man an, auch nen cast zu denken, wenn man bewußt eine implizite typumwandlung heraufbeschwört.

    muß wohl so ein ding sein, wie die if-schleife, die es ja auch nicht gibt, aber die ich immer wieder lese.



  • volkard schrieb:

    gemein wirds bei umwandelnden konstruktionen wie

    for(int i=0;i<256;++i){
       char c=i;
       cout<<c;
    }
    

    Ich sehe hier nichts gemeines, ich sehe hier nur eine Integer-Konvertierung ;)Und die ist in C++ bekanntlich implizit. Das du die Zeile die zu der Konvertierung führt explizit hingeschrieben hast, macht die Konvertierung nicht explizit und insbesondere ist hier auch kein cast anwesend (was man leicht daran erkennt, dass hier weder irgendwelche Klammern unmotiviert rumstehen und auch von diesen ewig langen neuen C++ Schlüsselwörter mit Unterstrich und eckigen Klammern dran ist weit und breit nichts zu sehen).

    Manchmal ist eine Definition einfach eine Definition.

    muß wohl so ein ding sein, wie die if-schleife, die es ja auch nicht gibt, aber die ich immer wieder lese.

    Imo sind Begriffsdefinitionen in einem technischen Umfeld wie einer Programmiersprache sehr wichtig. Es gibt da aber durchaus verschiedene Level.
    Wenn jemand in dem oben zitierten Kontext davon spricht, dass er i vor der Ausgabe nach "char castet", dann ist das imo noch durchaus ok (es sei denn es geht gerade um das Thema Cast vs. implizite Typumwandlung).
    Wenn aber jemand von einer if-Schleife spricht, dass geht gar nicht.
    Ich für meinen Teil schalte in dem Moment bereits ab.



  • najut, dann halt implizite typumwandlungen, mir wurden sie zusammen mit den andren casts unter dem großen oberbegriff "casts" als "implizit" beigebracht...

    @volkard jo ich kann kein c++,wird sich sicher ändern, aber atm keine ahnung wie ich einen datentyp mit einer größe von 1-100byte zur laufzeit in einen parameter quetschen kann 🙄.
    vielelicht sollte ich ja doch statt nem void* ein char* nehmen 🙄
    vielleicht sollte ich mich auch damit abfinden,dass ich 2 plugins unter keinen umständen erlauben darf mittels eigener datentypen über die standardschnittstelle für plugins in meinem projekt miteinander zu kommunizieren, ja das wär sicher die beste möglichkeit 🙄.

    casts sind übrigens für mich warnung genug, immerhin sind sie a) deutlich hervorgehoben und b) von mir so gewollt, ich sehe genau was ich mit meinem typ mache, deshalb gefallen mir implizite typumwandlungen nicht, weil man es dem code im zweifesfall nicht ansieht.

    dynamic_cast ist fast immer schrecklich

    solange es aber noch mindestens eine sinnvolle möglichkeit gibt sie anzuwenden(und zwar immer dann wenn man von einer baseclass einen downcast machen muss...) haben sie eine daseinsberechtigung.



  • otze schrieb:

    solange es aber noch mindestens eine sinnvolle möglichkeit gibt sie anzuwenden(und zwar immer dann wenn man von einer baseclass einen downcast machen muss...) haben sie eine daseinsberechtigung.

    Wirklich? Für simple Downcasts kann ich auch static_cast nehmen. Erst wenn es zu etwas komplizierteren Hierarchien kommt und Crosscasts benötigt werden, könnte bzw. müsste man dynamic_cast in Erwägung ziehen...



  • otze schrieb:

    @volkard jo ich kann kein c++,wird sich sicher ändern, aber atm keine ahnung wie ich einen datentyp mit einer größe von 1-100byte zur laufzeit in einen parameter quetschen kann 🙄.

    sowas wie string oder vector?

    vielelicht sollte ich ja doch statt nem void* ein char* nehmen 🙄

    eher nicht.
    void* ist nur "schlimm" aber nicht per dogma verboten. wenn man nach eingehender überlegung keinen guten ausweg findet, nimmt man eben mal void*. wenn man keine characters hat, sondern zum beispiel ne struct, dann ist ein void* angemessener als ein char*.

    machmal verwendet man aus performancegründen void* und baut ne kostenlose! typsichere schnittstelle drumherum.

    //ungetestet
    class TinyVoidPtrStackAsSingleLinedList{
       struct Node{
          void* data;
          Node* next;
          Node(void* _data,Node* _next):
             data(_data),next(_next){
          }
       };
       Node* anchor;
       TinyVoidPtrStackAsSingleLinedList(TinyVoidPtrStackAsSingleLinedList const&);//forbidden
       TinyVoidPtrStackAsSingleLinedList& operator=(TinyVoidPtrStackAsSingleLinedList const&);//forbidden
    public:
       TinyVoidPtrStackAsSingleLinedList(){
          anchor=0;
       }
       ~TinyVoidPtrStackAsSingleLinedList(){
          while(!isEmpty())
             pop();
       }
       bool isEmpty(){
          return anchor==0;
       }
       void* peek(){
          return anchor->data;
       }
       void pop(){
          Node* toDie=anchor;
          anchor=anchor->next;
          delete toDie;
       }
       void push(void* data){
          anchor=new Node(data,anchor);//SCNR
       }
    };
    
    //und ab hier keine kosten mehr. damit meine ich nicht 1000 takte oder 100 takte. 
    //damit meine ich 0 takte und 0 bytes. 
    template<class T>
    class TinyPtrStackAsSingleLinedList{
       TinyVoidPtrStackAsSingleLinedList m_tvpsasllData;
       bool isEmpty(){
          retrn m_tvpsasllData.isEmpty();
       }
       T* peek(){
          return static_cast<T*>(m_tvpsasllData.peek());
       }
       void pop(){
          return m_tvpsasllData.pop();
       }
       void push(T* data){
          return m_tvpsasllData.push(data)
       }
    };
    

    vielleicht sollte ich mich auch damit abfinden,dass ich 2 plugins unter keinen umständen erlauben darf mittels eigener datentypen über die standardschnittstelle für plugins in meinem projekt miteinander zu kommunizieren, ja das wär sicher die beste möglichkeit 🙄.

    wenn das geht, ohne sich ein bein zu brechen...
    evtl statt

    void send(char* data,int size);
    

    an sowas denken:

    void send(Packet* data);
    nebst
    struct /*class*/ Packet{
       void* data;
       int size;
    };
    

    das wiederum mutiert fast augenblicklich zu

    void send(Packet* data);
    nebst
    struct /*class*/ Packet{
       virtual ~Packkt(){
       }
    };
    

    je nach gusto sogar

    void send(auto_ptr<Packet> data);
    nebst
    struct /*class*/ Packet{
       virtual ~Packkt(){
       }
    };
    

    hier würde der empfänger sogar in der debug-version zur eigenen sicherheit einen dynamic_cast verwenden, aber nur, weil folgende lustige situation gegeben ist:
    - der sender weiß, daß er nur einen FooPacket* verschicken will
    - der empfänger weiß, daß er nur einen FooPacket* empfangen will
    - es wird ein Packet* auf die reise geschickt, weil die schnittstelle nix von FooPacket wußte.

    warum steht nicht gleich da

    void send(FooPacket* data);
    

    ?
    die beiden plugins, die mit FooPackets arbeiten, müßten halt gemeinsam die datei FooPacket.h kennen. und dann braucht man gar kein casten, keine basisklasse und vor allem kann an nicht aus versehen nen falschen typ versenden.
    jo, ich vermute, es geht, ohne sich ein bein zu brechen.

    dynamic_cast ist fast immer schrecklich

    solange es aber noch mindestens eine sinnvolle möglichkeit gibt sie anzuwenden(und zwar immer dann wenn man von einer baseclass einen downcast machen muss...) haben sie eine daseinsberechtigung.

    das haben wir früher auch anders lösen können.

    class Base{
       virtual Foo* toFooPtr(){return 0;}
       virtual Bar* toBarPtr(){return 0;}
       ...
    };
    class Foo{
       virtual Foo* toFooPtr(){return this;}
       ...
    };
    ...
    

    das war eigentlich auch ganz sicher, ohne weiteren rtti-overhead und sogar recht bequem. natürlich gibt es gründe, dynamic_cast zu haben, aber sichere downcasts aleine sind es nicht, die sind zu leicht so zu bekommen, daß man dafür ein schlüsselwort einführen würde.



  • @volkard soweit bin ich eigentlich auch schon gekommen,char* stand eigentlich auch nie zur debatte(genauso wie string oder vector),da ich einfach zuviele verschiedene datentypen erwarten muss.

    das polymorphe interface hatte ich zuerst auch geplant, aber dann wieder verworfen, da es nicht ohne exzessive benutzung von dynamic_cast ausgekommen wär(auch wenn ich nichts gegen dynamic_cast hab, das war einfach zuviel^^).

    dein basetrick funktioniert da übrigens auch nicht,mehr dazu aber später^^.

    wieso kommt das nicht ohne dynamic_cast aus?
    1. will ich an die daten kommen, muss ich sie abfragen,das interface kann mir diese funktionalität aber nicht bieten, da es zur compilezeit den typ nicht kennt. daraus folgt->dynamic_cast, um an das standardinterface zu kommen.
    zum glück kann ich in meinem fall von fast sicheren downcasts ausgehen(aber nur fast,ich sollte doch noch prüfen ob der datentyp wirklich der ist für den ich ihn halte), da mein send so aussieht:

    void send(Message message,Packet* packet
    

    das plugin kann also den datentyp in packet anhand der message indetifizieren.
    dh wenn zb in message "CreateShadowVolume" steht, kann ich davon ausgehen, dass in packet ein mesh und eine lichtquelle ist.
    Dasselbe könnte ich aber dann auch mit void* pointern erreichen, vielleicht sogar schneller.

    nun könnte man ja wie du gesagt hast mit

    void send(FooPacket* data);
    

    kommen, sicher das geht auch solange man nur einen datentyp zu erwarten hat, es ist aber so, dass es auch noch ein paar standardnachrichten gibt, die jedes interface beantworten können muss, und schon wär fooPacket überfordert. weiteres problem wäre natürlich, dass send teil eines interface ist,welches alle plugins unterstützen müssen, da ist man also schon gebunden.

    2. dein basetrick:
    1. ich steh ihm eh skeptisch gegenüber da man sobald eine neue klasse dazukommt direkt base mitverändern müsste.
    2. da ich zur compilezeit nicht einmal alle typen kenne, würde ich damit sehr große probleme bekommen^^



  • void send(Message message,Packet* packet
    

    das plugin kann also den datentyp in packet anhand der message indetifizieren.

    und hier verstehe ich nicht ganz, warum du 10 verschiedene
    messages verschickst, statt 10 verschiedene methoden aufzurufen.

    auch wenn die konkrete pluginklasse nicht verfügbar sein kann, so existiert
    doch zumindest ein plan, welche 10 verschiedenen messages diese pluginklasse
    essen kann. daher erbt diese pluginklasse von einer allen bekannten
    interfaceklasse, die genau diese 10 messages essen kann, und zwar als
    pur virtuelle methoden verpackt.

    das plugin kann also den datentyp in packet anhand der message indetifizieren.
    dh wenn zb in message "CreateShadowVolume" steht, kann ich davon ausgehen, dass in packet ein mesh und eine lichtquelle ist.

    oder dein plugin erbt von einer klasse, die die (pur) virtuelle methode CreateShadowVolume(Mesh*,LightSource*) hat. wäre doch auch angenehmer, die daten nicht immer vorher in ein paket verpacken zu müssen.
    ich verrate mal nicht, wie man auf angenehme weise pakete schnüren und entschnüren kann, die aus methodenzeiger und allen zugehörigen parametern bestehen.

    Dasselbe könnte ich aber dann auch mit void* pointern erreichen, vielleicht sogar schneller.

    ja, sogar schneller. aber da mußte unheimlich aufpassen, ne kleine fehlentscheidung und du bist langsamer. um in diesen sphären richtig zu entscheiden, muß man wissen, ob bei dem ziel-compiler methodenzeiger (wie üblich) 12 bytes groß sind bem dereferenzieren ein if ausführen, ob der zeiger (vptr) auf dir vtable am anfang oder am ende des objekts ist, wie man switch dazu bringt, die bereichüberprfung sein zu lassen und so weiter und so fort.
    da biste mit möglichst stilechtem c++ besser aufgehoben, weil es da keine teuren fehlentscheidungen gibt, mal angenommen sachen wie vector, dynamic_cast und so, die offensichtlich ihren preis haben, werden nur verwendet, wenn andere lösungen ähnlich teuer wären.

    es ist aber so, dass es auch noch ein paar standardnachrichten gibt, die jedes interface beantworten können muss, und schon wär fooPacket überfordert

    jedes interface muss ein paar standardnachrichten verarbeiten? klingt nach "jedes interface muß ein paar standardmethoden anbieten". genau dafür gibts den total geilen basisklassentrick.
    ich bin von deiner argumentation nicht überzeugt worden, es ist vielmehr so, daß ich langsam vermute, es ginge mit ganz normalen basisklassen und virtuellen methoden.

    2. dein basetrick:
    1. ich steh ihm eh skeptisch gegenüber da man sobald eine neue klasse dazukommt direkt base mitverändern müsste.
    2. da ich zur compilezeit nicht einmal alle typen kenne, würde ich damit sehr große probleme bekommen^^

    ok, das ist ein grund, den ich mal echt einsehe.
    also fummle ich es um:

    class Base{ 
       virtual char const* getClassName()=0;//nur pur, weil klasse eh abstrakt
       ... 
    }; 
    class Foo{ 
       virtual char const* getClassName(){return "Foo";} 
       ... 
    }; 
    ...
    

    ich sage aber nicht, daß du sowas verwenden sollst. ich vermute, daß dynamic_cast in flachen klassenhierarchien noch einigermaßen schnell ist. ausgemessen habe ich dyamic_cast nie, weil ich sowas nicht verwende.



  • Ich finde ja boost::any immer noch viel schöner als void* (und oft auch schöner als Klassenhierachien, im Plugin-Beispiel aber eher nicht). Mein Vorposter wird mir mal wieder den Schädel für Performance-Verschwendung abreißen und beim Plugin-System könnte es auch sehr gerechtfertigt sein, aber meine zwei void*-Probleme habe ich ganz schnell auf boost::any umgestellt und lebe jetzt sehr glücklich damit. (Eines davon war ein klassischer UserData-Parameter, den ich einem kleinen ListView-Control mit jedem Item gebe. Sehr praktisch.)

    C++ ist also nicht ganz machtlos, wo andere Sprachen sich auf ihre allgegenwärtige Object-Basisklasse berufen und void* ist wirklich kein Problem...



  • @volkard wenns so einfach wär^^
    normalerweise eine gute idee, aber mein projekt kann man als "experiment eines verrückten" bezeichnen.
    ich wollte mal aus lernzwecken versuchen(bzw bis jetzt nur aufm blatt papier), ein programm zu schreiben, dessen funktionalität komplett von den plugins abhängt,dh es besitzt keinen "core" bzw einen mainframe sondern nur plugins, die sich gegenseitig die informationen zuschieben und berechnen. Am ende soll irgendwas sinnvolles rauskommen^^.als beispiel ausgedrückt:
    eingabeplugin sendet daten zum berechnungsplugin, berechnungsplugin sendet daten zum ausgabeplugin.

    in dem experiment kenn ich nicht mehr von den plugins, als dass sie daten enpfangen und senden, was sie tun, und wie sie es tun, davon hab ich keine ahnung. Es ist wie gesagt nur ein experiment, und muss nicht unbedingt schnell sein oder am ende einen sinn haben, es soll nur funktionieren(und flexibel sein)^^

    aber des is doch schon ziemlich ot^^



  • otze schrieb:

    in dem experiment kenn ich nicht mehr von den plugins, als dass sie daten enpfangen und senden, was sie tun, und wie sie es tun, davon hab ich keine ahnung. Es ist wie gesagt nur ein experiment, und muss nicht unbedingt schnell sein oder am ende einen sinn haben, es soll nur funktionieren(und flexibel sein)^^

    aber der sender kennt seinen empfänger? das allein würde mir schon reichen.



  • nein 😉
    bzw er kann einen zeiger drauf bekommen, und dann in seinen geliebten typ umcasten, das kann aber schonmal ins auge gehen, wenn plugins gewechselt werden(und das sollte ja durch das ganze experiment ermöglicht werden, beliebige austauschbarkeit)



  • "ich sage aber nicht, daß du sowas verwenden sollst. ich vermute, daß dynamic_cast in flachen klassenhierarchien noch einigermaßen schnell ist. ausgemessen habe ich dyamic_cast nie, weil ich sowas nicht verwende. "

    würd mich mal sehr interesieren, hat das vieleicht schon jemand ?

    wär dankbar fürn link 🙂



  • Was willst du denn messen? Er wird schon nicht der schnellste sein. Und da du ihn eh vermeidest, so gut es geht, macht dir das auch keine Probleme.



  • wahrscheinlich hat dynamic _cast nichtmal eine konstante aufrufzeit...


Anmelden zum Antworten