Frage zu Doppelzeiger (IO-Funktion + GetQueuedCompletionStatus)



  • Hi!

    Irgendwie verschiebt sich der Pointer auf overlapped um 4, wenn ich ihn bekomme.

    Info:
    AcceptEx: http://msdn.microsoft.com/en-us/library/ms737524
    GetQueuedCompletionStatus: http://msdn.microsoft.com/en-us/library/aa364986
    (Man übergibt AcceptEx eine Adresse, und bekommt sie dann bei GetQueuedCompletionStatus wieder. Bei mir allerdings manchmal 4 Bytes verschoben)

    struct Context : public OVERLAPPED
    {
    // ...
    };
    
    // ...
    
    Context* newContext = new Context;
    
    // ...
    
    AcceptEx(..., (OVERLAPPED*)newContext );
    

    Ok gut, nun bekomme ich eine completion.

    So gehts:

    OVERLAPPED* overlapped;
    
    GetQueuedCompletionStatus(..., (OVERLAPPED**)&overlapped);
    
    Context* context = (Context*)overlapped;
    
    // Ok, context == newContext
    

    Und so gehts nicht, weil dann context plötzlich 4 Bytes weiter zeigt, als der dem AcceptEx übergebene Context.

    Context* context
    
    GetQueuedCompletionStatus(..., (OVERLAPPED**)&context);
    
    // Nicht Ok, context == newContext + 4 (???)
    

    Warum ist das so? Zeiger ist doch gleich Zeiger?

    THX!



  • hast du virtuelle Methoden in deinem Context?



  • Danke für den Tip, habs grad mit nem Minimalbeispiel versucht. Liegt tatsächlich am virtuellen Destruktor von Context.

    Bin aber viel zu eingerostet in C++, um mir erklären zu können, warum...



  • 😕



  • Nimm static_cast statt C-Casts!



  • Bringt nix.



  • Need Input



  • --- hier stand blödsinn ----

    Zeiger ist nicht gleich Zeiger - Zeiger auf abgeleitete Klass können anders sein als Zeiger auf Basis Klassen - man denke da nur mal an die Mehrfachvererbung.



  • Ich verwende ähnlichen Code öfters, funktioniert wunderbar.
    Funktioniert bloss nicht, wenn man nicht korrekt castet.
    Wenn du z.B. von "Context" zu "void*" castest, und dann von "void*" zu "OVERLAPPED*", dann wird das nix werden.
    Genau so wenn du über void* zurückcastest.
    Oder sonst irgendwie Unsinn baust.



  • hustbaer schrieb:

    Wenn du z.B. von "Context" zu "void*" castest, und dann von "void*" zu "OVERLAPPED*", dann wird das nix werden.
    Genau so wenn du über void* zurückcastest.

    Und wie sonst? Und warum geht das nicht?



  • Du musst direkt von Context* zu OVERLAPPED* casten, damit der Zeiger entsprechend angepasst wird.

    Wenn du von Context* zu void* castest, dann darfst du den void* den du bekommst nur wieder zurück zu Context* casten. Den so erhaltenen void* zu einer Basisklasse von Context* zu casten *kann* gehen, ist aber laut Standard undefiniert, und daher einfach falsch.
    (Und es geht eben auch oft genug wirklich nicht.)

    Alles C++ Grundlagen.

    ps.:

    BTW: der Fehler ist - wenn dein Beispielcode deinem Programm entspricht - eh ein anderer (sehr ähnlich, aber nix mit void*):

    Context* context 
    
    GetQueuedCompletionStatus(..., (OVERLAPPED**)&context); // das ist Humbug
    

    Richtig geht das so:

    OVERLAPPED* overlapped;
    GetQueuedCompletionStatus(..., &overlapped); // Kein Cast nötig
    Context* context = static_cast<Context*>(overlapped); // Jetzt casten wir den Zeiger (was OK ist), und nicht einen Zeiger auf einen Zeiger (was Humbug wäre)
    

    Der Grund warum es nicht funktioniert, ist dass "Zeiger auf Context" und "Zeiger auf OVERLAPPED" zwei unverwandte Typen sind. Anders gesagt: du kannst nicht T** zu U** casten, egal wie T und U verwandt sind, da T* und U* eben nicht verwandt sind.
    Sorry, kann es grad auf die Schnelle nicht besser erklären, bzw. hab keine Zeit es besser/näher zu erklären.



  • hustbaer schrieb:

    Du musst direkt von Context* zu OVERLAPPED* casten, damit der Zeiger entsprechend angepasst wird.

    Wenn du von Context* zu void* castest, dann darfst du den void* den du bekommst nur wieder zurück zu Context* casten. Den so erhaltenen void* zu einer Basisklasse von Context* zu casten *kann* gehen, ist aber laut Standard undefiniert, und daher einfach falsch.
    (Und es geht eben auch oft genug wirklich nicht.)

    Alles C++ Grundlagen.

    Jo,
    wenn man eine Inheritclass zu einer baseclass über einen void direkt
    konvertiert, muss man darauf achten, dass es die erste Basisklasse ist.
    Ansonsten muss der this ptr adjustiert werden,..

    Mal grob pseudomäßig:

    class Inherit:  BaseA, BaseB
    {}
    //...
    
    LPVOID blub =(LPVOID) new Inerhit;
    BaseA* basA = (BaseA) blub; //geht, da nach msdn spec für MSVC BaseA erste addy ist
    BaseB* basB = (BaseB) (blub +sizeof(BaseA)); //Adjustiere zeiger
    // BaseB liegt hinter BaseA, sizeof(BaseA) sollte auch die die größe der vftbl beinhalten
    

    Die adjustage sollte man aber compiler/architektur-spezifisch explecit nochmal prüfen, bevor man sich darauf verlässt das es "einfach" funktioniert,...

    grüßli



  • Jo,
    wenn man eine Inheritclass zu einer baseclass über einen void direkt
    konvertiert, muss man darauf achten, dass es die erste Basisklasse ist.

    Bist du sicher dass das dann definiert ist (laut Standard)?
    Ich müsste da erst nachsehen...



  • Hi,.

    100% sicher bin ich jetzt gerade net,..wo du fragst.
    Aber ich schaue mal morgen nach.

    gn8



  • Ouch, Grundlagenlücke!
    Aber: Was, wenn man es statt mit Vererbung so macht:

    struct Context
    {
    	OVERLAPPED overlapped;
        // ...
    };
    

    Ist "GetQueuedCompletionStatus(..., (OVERLAPPED**)&context);" dann OK? Ich denke schon, denn so, ganz "roh", dürfte ja "Context" und "OVERLAPPED" bis hin zu sizeof(OVERLAPPED) Bytes "speichergleich" sein...


  • Mod

    Hi schrieb:

    Ouch, Grundlagenlücke!
    Aber: Was, wenn man es statt mit Vererbung so macht:

    struct Context
    {
    	OVERLAPPED overlapped;
        // ...
    };
    

    Ist "GetQueuedCompletionStatus(..., (OVERLAPPED**)&context);" dann OK? Ich denke schon, denn so, ganz "roh", dürfte ja "Context" und "OVERLAPPED" bis hin zu sizeof(OVERLAPPED) Bytes "speichergleich" sein...

    Nein!
    Das hat doch nicht mit Vererbung zu tun.

    Außerdem: Versuche casts zu Vermeiden. Gerade wen es hier in keiner Weise nötig ist.

    1. Würde ich dennoch den Member in der Context Struktur direkt adressieren.
    2. Würde ich nicht darauf bauen, dass overlapped der ertse Parameter der Strutktur bleibt.
    3. Wenn überhaupt würde ich von OVERLAPPED ableiten:

    struct Context : public OVERLAPPED 
    {
        // ...
    };
    

    Aber selbst das ist IMHO mieses Design.
    4. Ein Zeiger auf einen Zeiger Overlapped Struktur ist das:

    struct Context
    {
    	OVERLAPPED overlapped;
        // ...
    };
    
    //...
    Context myContext;
    LPOVERLAPPED p = &myContext.overlapped;
    GetQueuedCompletionStatus(..., &p, ...);
    


  • Geht auch ohne Vererbung mit dem Makro CONTAINING_RECORD() 😉


  • Mod

    Jodocus schrieb:

    Geht auch ohne Vererbung mit dem Makro CONTAINING_RECORD() 😉

    Ich habe doch geschrieben, dass es ohne Vererbung geht und auch besser wäre...
    Ich verstehe Deinen Einwurf nicht.
    Zudem löst es nicht das Grundproblem, dass der OP nämlich einen Zeiger auf einen Zeiger benötigt!



  • > dass der OP nämlich einen Zeiger auf einen Zeiger benötigt!

    Hä? Den braucht er doch überhaupt nicht.
    Mein Post war kein Einwand, sondern eine gängige Möglichkeit, aus einem Member das Container-Objekt zu ermitteln (was auch funktioniert, wenn OVERLAPPED nicht der erste Member der Struktur ist). 😕


  • Mod

    Jodocus schrieb:

    Hä? Den braucht er doch überhaupt nicht.

    Nicht?

    Dann schau Dir mal die Definition von GetQueuedCompletionStatus an.
    http://msdn.microsoft.com/en-us/library/aa364986(VS.85).aspx

    Nach meinem Dafürhalten ist "LPOVERLAPPED *lpOverlapped" ein Zeiger auf einen Zeiger, und damit hat der OP anscheinend Probleme...
    Just my 2 cents!



  • Ich glaube, ich verwende lieber den completion key (unsigned int*) 😃
    Sollte eigentlich genau so klappen. Man hat dann halt keine verschiedenen Kontexte für zB. AcceptEx und WSARecv, aber dann packt man halt alles nötige in eine einzige ClientContext-Struktur.
    Oder warum sollte man das nicht machen?


Anmelden zum Antworten