Frage Rätselhaftes Problem

  • Montag, 13. August 2012 12:36
     
     

    Hallo,

    ich habe ein Programm, bei dem ein mir völlig unverständlicher Fehler auftritt, bei dem ich nicht sicher bin, ob er auf meine Kappe geht.

    In einer Objekt-Funktion wird eine Funktion mit einer member-Variable als Argument aufgerufen. Die aufgerufene Funktion übernimmt als Argument eine Referenz auf einen std::string.

    Aufruf:   elem>>"name">>m_Name; // m_Name ist die member-variable, das x>>"name" muss hier nicht weiter kümmern - liefert ein Objekt zurück, das den >> operator ebenfalls überschreibt.

    Die aufgerufene funktion ist deklariert als:

    template<> element_handle_t _attribute_handle::operator>> (std::string& value) const

    Nun tritt ein mir völlig unbegreiflicher Fehler auf, nämlich, dass in der Aufgerufenen Funktion 'value' ein <Bad Ptr> ist. Der this-Pointer des Objekts scheint in der aufrufenden Funktion in Ordnung. (Im Debugger kein auffallender Fehlerwert und die Members haben richtige Werte - heisst m_Name wird korrekt als mit "" belegt angezeigt und die restlichen members auch). Hat jemand schonmal sowas ähnliches gehabt?

Alle Antworten

  • Montag, 13. August 2012 15:41
     
      Enthält Code

    Ich hab mir den Disassembly mal angesehen, und der enthält tatsächlich etwas, was ich nicht verstehe.

    5A0D0B90  push        ebp 

    5A0D0B91  mov         ebp,esp 

    5A0D0B93  push        0FFFFFFFFh 

    5A0D0B95  push        offset __ehhandler$?inputXML@PoliticalFaction@GalaxyExplorer@@QAE_NU_element_handle@XML_Operators@@@Z (5A0EEBA3h) 

    5A0D0B9A  mov         eax,dword ptr fs:[00000000h] 

    5A0D0BA0  push        eax 

    5A0D0BA1  sub         esp,370h 

    5A0D0BA7  push        ebx 

    5A0D0BA8  push        esi 

    5A0D0BA9  push        edi 

    5A0D0BAA  push        ecx 

    5A0D0BAB  lea         edi,[ebp-37Ch] 

    5A0D0BB1  mov         ecx,0DCh 

    5A0D0BB6  mov         eax,0CCCCCCCCh 

    5A0D0BBB  rep stos    dword ptr es:[edi] 

    5A0D0BBD  pop         ecx 

    5A0D0BBE  mov         eax,dword ptr [___security_cookie (5A1006D0h)] 

    5A0D0BC3  xor         eax,ebp 

    5A0D0BC5  push        eax 

    5A0D0BC6  lea         eax,[ebp-0Ch] 

    5A0D0BC9  mov         dword ptr fs:[00000000h],eax 

    5A0D0BCF  mov         dword ptr [ebp-14h],ecx 

    5A0D0BD2  mov         dword ptr [ebp-4],0 

    elem>>"name">>m_Name; 5A0D0BD9 mov esi,esp 5A0D0BDB mov eax,dword ptr [ebp-14h] 5A0D0BDE push eax 5A0D0BDF lea ecx,[ebp-30Ch] 5A0D0BE5 push ecx 5A0D0BE6 mov edi,esp 5A0D0BE8 push offset string "name" (5A0F522Ch) 5A0D0BED lea edx,[ebp+8] 5A0D0BF0 push edx 5A0D0BF1 lea eax,[ebp-318h] 5A0D0BF7 push eax 5A0D0BF8 call dword ptr [__imp_XML_Operators::operator>> (5A101CD0h)]

    Soweit ist noch alles wenigstens halbwegs klar. Zumindest werden This-Pointer des Objekts und Argument(der String) auf den Stack gepusht (Auch wenn ich es nicht bis ins kleinste nachvollzogen habe...) . EAX enthält jetzt das weiter zu verwendende Attribute-handle.

    5A0D0BFE add esp,0Ch 5A0D0C01 cmp edi,esp 5A0D0C03 call @ILT+5915(__RTC_CheckEsp) (5A0A6720h) 5A0D0C08 mov dword ptr [ebp-320h],eax

    5A0D0C0E mov ecx,dword ptr [ebp-320h] 5A0D0C14 mov dword ptr [ebp-324h],ecx

    // Das handle ins register um das objekt zu übergeben - aber wo ist das Argument?

    5A0D0C1A mov byte ptr [ebp-4],1 5A0D0C1E mov ecx,dword ptr [ebp-324h]

    // Und jetzt der Aufruf mit folgender Access-violation

    5A0D0C24 call dword ptr [__imp_XML_Operators::_attribute_handle::operator>><std::basic_string<char,std::char_traits<char>,std::allocator<char> > > (5A101CD4h)]

    (Kleines edit - Eine Sache war mein Fehler beim Debuggen. Erklärt das Problem aber trotzdem nicht. Edit 2: d0c14 bereitet nicht den CallStack vor, das war geraten.)





    • Bearbeitet Heiko Lewin Montag, 13. August 2012 17:19
    • Bearbeitet Heiko Lewin Montag, 13. August 2012 18:08
    • Bearbeitet Heiko Lewin Dienstag, 14. August 2012 08:32
    • Bearbeitet Heiko Lewin Dienstag, 14. August 2012 08:35
    •  
  • Donnerstag, 23. August 2012 15:32
     
     

    Keiner eine Antwort parat? Weiss jemand, ob Visual Studio 11 Beta und RC12 die selben Compiler etc. verwenden, so dass der Versuch, das Projekt in eine neuere Version (der Fehler trat jetzt in VC2010 auf) zu importieren Erfolg verspricht/verspräche? -- So auf die schnelle scheints da Probleme mit dem verwendeten WindowsSDK zu geben. --


    .... Ich: *haps*

  • Montag, 27. August 2012 13:34
     
     
    Haaallloooo?!?
  • Montag, 27. August 2012 13:56
    Moderator
     
     

    Ja! Vielleicht weiß eben keinr etwas.

    Wie wäre es mit etwas mehr Code?
    Oder einem Mini-Sample, der ds reproduziert?


    Martin Richter -- MVP for VC++ [Germany] -- http://blog.m-ri.de

  • Montag, 27. August 2012 14:51
     
     
    Das ist ja gerade das komische: Das tritt/trat zwar deterministisch auf, aber nur in einem größeren Kontext. Etwas mehr code, um sonstige Fehler auszuschließen ist also zunächst nicht so einfach... Die member-variable ist ein std::string und die aufgerufene Funktion nimmt eine Referenz auf einen solchen als Argument. In der aufgerufenen Funktion ist das Argument dann ein bad-pointer, in der aufrufenden Funktion ein korrekter Wert.

    HAL: "Well, I don't think there is any question about it: It can only be attributable to human error. This sort of things has cropped up before and it has always been due to human error."

  • Dienstag, 28. August 2012 08:13
    Moderator
     
     

    Ist da eine DLL Grenze dazwischen?

    Stimmt pragma pack in den Modulen nicht überein?


    Martin Richter -- MVP for VC++ [Germany] -- http://blog.m-ri.de

  • Dienstag, 28. August 2012 13:42
     
     

    Sind zwar unterschiedliche dlls-aber alle selbst gebaut. Das pack verstehe ich nicht so ganz - ist aber nicht explizit gesetzt.

    Aber trotzdem hast du micht auf den gedanken gebracht, das mal zu prüfen und es stellte sich aber heraus, dass das string-objekt einmal 28 und einmal 32 byte groß ist. struktur-member-alignment ist überall auf default. das platform-toolset ist überall das selbe. wie kann das sein? ist immerhin ne template-funktion, die aufgerufen wird und selbst, wenn das modul mit der aufrufenden funktion eine anderes alignment/pack verwendet müsste das doch in sich konsistent bleiben, oder irre ich mich da?


    HAL: "Well, I don't think there is any question about it: It can only be attributable to human error. This sort of things has cropped up before and it has always been due to human error."

  • Dienstag, 4. September 2012 17:14
     
     

    Ich hab den Fehler gefunden: Beim den build-settings der bibilothek, die die aufgerufene Funktion enthält war _DEBUG definiert, wodurch der std::string ein paar bytes größer war als in der anderen.

    Zumindest beim debug-build sind die beiden std::string-klassen, die sich durch unterschiedliche präprozessordefinitionen ergeben, zwar nicht die selben, werden aber bei der template-instanzierung als die selbe behandelt. Ist das so gedacht? Werden bei der template-instanzierung die klassen nur durch ihren namen identifiziert? Ich hab auch schon mal versucht, die spezialisierung explizit als "inline" zu deklarieren, sie wird aber trotzdem mit der debug-string-klasse instanziert und auch aufgerufen. mit release-builds habe ich bisher noch keine tests gemacht (weiss auch so gar nicht, ob man problemlos _DEBUG definieren kann, wenns ein release-build ist).



    • Bearbeitet Heiko Lewin Dienstag, 4. September 2012 17:17
    •  
  • Sonntag, 16. Dezember 2012 05:39
     
      Enthält Code

    Ich hab das übrigens noch einmal getestet. Das Kernproblem scheint zu sein, dass die Typgleichheit tatsächlich nur über den Namen definiert zu sein scheint.

    Definiert man zB einen Funktionsoverload mit zwei Namensgleichen, unterschiedlichen Datentypen, erhält man einen Linker-Error.

    /* file1.cpp */ struct cl { int a }; void clfn(struct cl& c) { } /* file2.cpp */ struct cl { float a; int b; };

    void clfn(struct cl& c) { };

    Der Fehler tritt übrigens sowohl bei Visual-C++ als auch bei GNU-ld auf. Ich hab mal den C++ Standard-Draft überflogen, aber da stand dazu natürlich nichts drin, weil es erst beim Linken ein Problem wird und jede Datei für sich korrekt ist.

    Das erklärt dann (spekulativ) auch den Effekt mit der template-Instanzierung: Es wurde eine Instanzierung der Funktion für eine der namensgleichen Klassen gefunden und für die andere Namensgleiche Klasse verwendet.

    Ein Beispiel:

    /* template.h */
    #include <stdio.h>
    
    template<class X>
    struct printSize {
    
    	void operator()() {
    		printf("%d\n", sizeof(X));
    	}
    
    };
    
    extern void clfn1();
    extern void clfn2();
    
    /* fn1.cpp */
    #include "template.h"
    
    struct cl
    {
    	int a;
    };
    
    printSize<cl> PrintSize1;
    
    void  clfn1() {
    
    	PrintSize1();
    }
    
    /* fn2.cpp */
    #include "template.h"
    
    struct cl {
    	float a;
    	int b;
    };
    
    printSize<cl> PrintSize2;
    
    void clfn2() {
    	PrintSize2();
    };
    
    
    /* main.cpp */
    #include "template.h"
    
    void main() {
    	clfn1();
    	clfn2();
    
    }
    

    Produziert für x86-Platformen den output "4 4", wenn fn1.cpp vor fn2.cpp gelinkt wird, "8 8" wenn sie anders herum gelinkt werden (zb indem man fn1.cpp umbenennt und als fn3.cpp wieder zum projekt hinzufügt).


    HAL: "Well, I don't think there is any question about it: It can only be attributable to human error. This sort of things has cropped up before and it has always been due to human error."