2012-01-09 2 views
1

최근에 custm CALLBACK의 함수 포인터에 문제가 생겼습니다. 문제는 임시적으로 문제를 해결하는 호출 규칙을 사용하기 시작했습니다. funy CALLBACK은 제대로 작동했지만 호출 함수의 서명은 여전히 ​​잘못되었습니다! BUG을 찾아내는 데 많은 시간을 할애했습니다. 가 ... 규칙을 caling 것은 가끔 확인하지 무슨 일을 할 수 있다는 지난 지금 ... 지금, 나는이 규칙을 호출에 대한 저 작은 더 알고 싶어입니다표준 호출 규칙이 존재합니까?

확인을 실현 : 비주얼 스튜디오가 자신의 __cdecl없는거야, __thiscall 등 (IIRC).

표준 C++은 일부 호출 규칙을 규정합니까? 그렇다면 어떻게 사용할 수 있습니까?


편집 :

class Object; 
    class EventArgs; 

    typedef void(__cdecl Object::*MethodHandler)(Object* sender, EventArgs args); 

///..... this is how I call it..(snapshot) 
(iter->second.sender->*iter->second.memberFunct)(sender, args); 
///... 
    void __cdecl Triger(EventArgs args) //missing "Object* sender" here!!! but it works! 
    { 
     if(args == "test") 
     cout << "test args received" << endl; 
    } 

(. BTW, 유형 이름은 내 사용자 정의 클래스가) 그건 그냥 괜찮 았는데 : 일부 코드가있는 나는 버그를 찾지 못했습니다! 함수가 호출되었지만 __cdecl없이 ESP 레지스터 오류가 발생했습니다.

+0

버그가 무엇입니까? 일반적으로 컴파일러 오류없이 처리기/콜백을 등록하는 함수 포인터의 하드 코딩 된 캐스트는 잘못된 일을하는 데 드는 공짜입니다. 위의 코드에서 함수 인수가 다르므로 "트리거"는 MethodHandler 유형이 아닙니다. – selbie

+0

감사합니다, 아니 그것은 동일한 서명 아니에요하지만 __cdecl을 사용하지 않는 경우 __cdelc를 사용하여 잘 작동합니다, 그리고 신 ESP 레지스터 오류 및 progrm chrush .. 매우 재미 있습니다 .. – codekiddy

+0

당신이 안부와 운 좋게되고 있기 때문에 일하고 있어요 인수가 스택에 푸시되는 순서. cdecl 트릭은 아마도 충돌을 일으키지 않도록 스택을 마사지하는 것일뿐입니다. 어딘가에 코드에서 "(MethodHandler *) Trigger"(또는 void * cast)를 수행하여 컴파일 오류없이 해당 함수를 콜백 서비스에 등록합니다.캐스트를 사용하지 않고 코드를 컴파일하도록 코드를 수정하면 충돌이 사라집니다. 즉, 어디에서나 동일한 호출 규칙과 동일한 인수 목록을 의미합니다. – selbie

답변

1

__cdecl없이 충돌하는 이유는 Windows 컴파일러의 기본값이 __stdcall을 사용하기 때문입니다. 후자는 호출 수신자 함수가 스택을 정리하도록합니다. 따라서 호출자가 두 개의 인수를 스택에 푸시하여 "트리거"를 호출하지만 트리거는 함수 종료시 스택에서 하나의 인수 만 팝핑합니다. 따라서, 당신은 충돌합니다. __cdecl 호출 컨벤션 종류가이 문제를 해결합니다.

위에서 언급 한 것처럼 __cdecl을 사용하여 충돌을 수정하면 실제 버그가 숨겨집니다. 트리거의 인수 목록이 "MethodHandler"에서 예상 한 인수 목록과 일치하지 않습니다. 그게 네 사고의 원인 일거야.

typedef void(Object::*MethodHandler)(Object* sender, EventArgs args); 

void Triger(Object* sender, EventArgs args) 
{ 
    if(args == "test") 
    cout << "test args received" << endl; 
} 

당신은 "트리거가"후속 콜백에 등록됩니다 방법을 보여줍니다 코드를 공유하지 않았다

이 가능성이 더 나은 수정합니다. 그러나 나는 강하게 당신이 이런 식의 캐스트를 뒀다 의심 :

MethodHandler handler = (MethodHandler)Trigger; 

당신이 그렇게하지 않은 경우 때문에, 코드가 컴파일되지 않았다. 캐스트를 컴파일 할 필요가 없도록 코드를 수정하면 실제 버그가 사라지고 충돌은 사라집니다.

+0

heh yeah 정확히 내 문제가 무엇인지 알고 캐스팅이 필요하지만, stackoverflow에 대한 질문을했습니다. 그러나 영원히 캐스팅하지 않고도 똑같이 할 수는 없다고 말했습니다. 이것 좀 봐주세요 http://stackoverflow.com/questions/8756993/how-to-force-implicit-conversion-in-polymorphism – codekiddy

+0

파생 클래스에서 기본 클래스로 멤버 포인터를 캐스팅해야하는 경우에는 다음을 사용하십시오. static_cast는 형식 검사 중 일부를 유지할 수있게 해줍니다. – servn

3

아니오; 호출 규칙은 플랫폼에 따라 다릅니다. 가장 큰 두 개의 밑줄은 이것이 구현 - 예약 된 개념이라는 단서입니다. 언어 자체는이 세부 수준에서 구현되는 방법에 대한 요구 사항을 필요로하지 않습니다.

+0

사실, C++ *은 호출 규칙을 알고 있습니다 : extern "C"와 extern "C++"(후자는 기본값이므로 일반적으로 명시 적으로 작성되지 않았습니다). 그러나 일반적으로 이러한 호출 규칙은 이름 맹 글링 만 다릅니다. – celtschk

+0

답장을 보내 주셔서 감사합니다! 나는 extern "C"를 사용하여 어떤 이점이 있는지 확인하려고 노력할 것입니다. – codekiddy

+0

@celtschk : 감사합니다. 좋은 점은 "linkage specification"이라고 부르지 만, 표준이 완전히 열리는 동안 * (실제로 플랫폼이 임의의 사양을 지원할 수있게 해줌) OP가 관심을 갖고있는 것으로 보이는 "호출 규칙"(스택 프레임 정리, 인수 순서, 레지스터 대 스택 등)은 구현 세부 사항으로 남아 있습니다. –

관련 문제