derived* -> void* -> base*



  • Hallo,
    den cast von derived* nach void* solltest du über einen dynamic_cast erledigen.

    void* a_void = dynamic_cast<void*>(a_derived);
    

    So stellst du sicher, dass a_void auf das vollständige Objekt verweist, was den Cast von a_void nach Base wiederum unproblematisch macht.



  • HumeSikkins schrieb:

    Hallo,
    den cast von derived* nach void* solltest du über einen dynamic_cast erledigen.
    [...]
    So stellst du sicher, dass a_void auf das vollständige Objekt verweist, was den Cast von a_void nach Base wiederum unproblematisch macht.

    gilt das generell oder nur für mein kleines beispiel? denn der void*-pointer wird im konkreten fall x-mal rumgereicht, unter anderem in eine c-bibliothek. woher nimmt der compiler dann die information, dass und wie nach base* gecastet werden kann?



  • Das gilt noch nicht einmal generell, das ist einfach nur Unsinn - aus dem void-Pointer kann der Compiler nicht einmal ableiten, was wirklich dahinter steht - und er kann auch bei der Umwandlung (egal ob ohne oder mit dynamic_cast<>) nicht wissen, welche Adresse später tatsächlich gebraucht wird (und was das "vollständige Objekt" eigentlich ist.

    Du mußt dir schon selber merken, was für einen Typ du da als void* übergeben hast - und später genau in diesen Typ zurückwandeln:

    //C-Funktionen als Minimalbeispiel
    void set_data(void* ptr);
    coid* get_data();
    
    //Anwendung
    derived* pDer = new derived;
    set_data(pDer);
    ...
    derived* ptr2 = (derived*)get_data();
    base* pBas = ptr2;
    


  • CStoll schrieb:

    Das gilt noch nicht einmal generell, das ist einfach nur Unsinn - aus dem void-Pointer kann der Compiler nicht einmal ableiten, was wirklich dahinter steht - und er kann auch bei der Umwandlung (egal ob ohne oder mit dynamic_cast<>) nicht wissen, welche Adresse später tatsächlich gebraucht wird (und was das "vollständige Objekt" eigentlich ist.

    Ich hab's jetzt nicht ausprobiert, aber: Wenn ich ein Objekt mit dynamic_cast auf void* caste, gibt er mir dann nicht tatsächlich die Basisadresse des Objekts, wie Hume das behauptete?



  • Bin ich mir nicht sicher - aber afaik liefert das die selbe Adresse wie der implizite Cast. Auf der anderen Seite kannst du nicht sicher sein, daß die Basisadresse des kompletten derived-Objektes mit der seines base-Anteils übereinstimmt (und der Compiler hat keine Möglichkeit, sich selber mitzuteilen, was hinter dem void* steht).



  • Uh, ich habe Humes Beitrag nicht aufmerksam genug gelesen. Ich dachte eigentlich an sowas hier:

    void* pv = dynamic_cast<Base*>(pd);
    

    Hier sollte IMHO durch den Cast die Typinfo hinzugezogen werden, sodass 'pv' tatsächlich die Basisadresse enthält. Dass man das ganze in einem untypisierten Zeiger speichert, sollte egal sein. Ob Humes Code standardkonform ist, bezweifle ich doch eher intuitiv.



  • CStoll schrieb:

    Das gilt noch nicht einmal generell, das ist einfach nur Unsinn

    Recht hast du. Erst mal zu meinen Annahmen: ich ging, aufgrund des Beispiels, von einer eindeutigen Basisklasse aus und in diesem Fall ist es wichtig, dass man die Adresse des Basisklassenobjekts in den void-Pointer stopft (schließlich will man dahin auch wieder zurück). Mein Denkfehler lag nun darin, dass ich dachte: Adresse des vollständigen Objekts == Adresse des Basisklassenobjekts. Das gilt wohl aber nicht - der Standard garantiert, dass ein dynamic_cast<void*> mir ersteres liefert, dies ist aber nicht identisch zu letzterem. Letztlich geht es viel einfacher: man muss den Derived* schlicht über einen Base* nach void* casten.
    Sprich:

    class Base {
    public:
    	virtual ~Base() {}
    };
    
    class Middle1 : public virtual Base {...};
    
    class Middle2 : public virtual Base {...};
    
    class Der : public Middle1, public Middle2 {...};
    
    void f() {
       Middle2* p = new Der;
       void* q = static_cast<Base*>(p); // nicht q = p;
       // pass q to C-Api   
       // ...   
       Base* x = static_cast<Base*>(q);
       x->irgendWas():
    }
    


  • das letzte stück code von humesikkins leuchtet mir ein. das problem ist, dass ich die casts nicht selber mache. das steckt alles in code aus einem code-generator (tolua++). den müsste ich dann wohl anpassen damit er tut was ich will.

    class base
    {
       void foo();
    };
    
    class derived : public base
    {
    
    };
    

    wenn ich nun in lua ein derived habe und a_derived:foo() aufrufe, wird der funktionsaufruf im c++-teil immer auf ein base* ausgeführt, warum auch immer.

    -- in lua
    bar = derived.new()
    bar:foo()
    

    wird zu

    derived* tolua_ret = (derived*)  new derived();
    tolua_pushusertype(tolua_S,(void*)tolua_ret,"derived");
    [...]
    base* self = (base*)  tolua_tousertype(tolua_S,1,0);  // leider nicht derived*
    self->foo();
    

    böser fehler. syntaktisch korrekt, funktioniert aber nicht immer.
    ich kann das umgehen, indem ich foo() beim export nochmal explizit als memberfunction von derived angebe. ist nur mühselig.



  • foo() ist ja auch eine Methode der Basisklasse - woher soll Lua da wissen, daß es kein Basis-Objekt hat? (vielleicht reicht es ja schon, wenn du die Methode virtuell machst)

    PS: Das Problem scheint weniger auf der C++ Seite, sondern auf der Lua-Seite zu liegen.



  • CStoll schrieb:

    foo() ist ja auch eine Methode der Basisklasse - woher soll Lua da wissen, daß es kein Basis-Objekt hat?

    genau genommen weiß lua überhaupt nichts über das objekt. für lua ist es nur eine tabelle mit ein paar werten. aber der code-generator weiß es, wenn er die bindings generiert.

    CStoll schrieb:

    (vielleicht reicht es ja schon, wenn du die Methode virtuell machst)

    macht keinen unterschied.

    PS: Das Problem scheint weniger auf der C++ Seite, sondern auf der Lua-Seite zu liegen.

    wie schon gesagt, es liegt am code-generator der die bindings generiert. der generiert standardmäßig keine funktion für derived::foo(), weil er meint einfach nach base* casten und dessen foo() aufrufen zu können.
    ich kann ihm einfach eine klassendefinition unterschieben, die derived::foo() nochmal explizit angibt (auch wenn sie eigentlich nicht existiert), dann castet er richtig. ist aber wie gesagt sehr mühselig. und wenig intuitiv.



  • Wie gesagt: Das ist kein Problem von C++ (das hat eine "klare" Semanti für diesen Fall - "undefined behaviour"), sondern von Lua bzw. vom Code-Generator. Und anscheinend verwendet der die statischen Typ-Informationen zur Code-Erzeugung - foo() ist eine Methode der Basisklasse, also holt er sich auch ein Basis-Objekt.

    PS: Nein, ich kenne mich nicht mit Lua aus - das sind nur Vermutungen anhand der Fragmente, die du geliefert hast. Aber ich geb dir gerne einen Gratis-Schubs ins richtige Board.



  • Dieser Thread wurde von Moderator/in CStoll aus dem Forum C++ in das Forum Rund um die Programmierung verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.


Anmelden zum Antworten