1

I've recently having problems with custm CALLBACK's function pointers, and that boiled down into using calling conventions which solve the problem temporaly, funy CALLBACK did work well then but signature of the calling function was still misstyped!! and I've spend a lot of time finding that BUG out. realized that caling convention let you do something what isn't OK sometimes...

OK that's past now... Now, I want to know a litle more about calling conventions: Visual Studio has it's own __cdecl, __thiscall, etc (IIRC).

Does standard C++ regulates some calling conventions and how can I use them if so?


EDIT: Some code on which I failed to find a bug:

    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, type names are my custom classes.) That worked just fine! Function was called but without __cdecl I have received ESP register errors.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
codekiddy
  • 5,897
  • 9
  • 50
  • 80
  • What was the bug? Typically, a hardcoded cast of a function pointer to register a handler/callback without a compiler error is a dead giveaway of doing something wrong. In the above code, "Trigger" is most certainly not of type MethodHandler since the function arguments are different. – selbie Jan 09 '12 at 02:44
  • thanks, no it's not the same signature but it work just fine using __cdelc, if I don't use __cdecl then god ESP register error and progrm chrush.. very interesting to me.. – codekiddy Jan 09 '12 at 02:47
  • It's working because you are getting lucky with regards to the order in which arguments are getting pushed onto the stack. The cdecl trick is probably just massaging the stack such that it won't crash. Somewhere in your code, you are doing "(MethodHandler*)Trigger" (or a void* cast)so as to register that function with a callback service without a compile error. If you fix your code such that the code compiles without a cast, your crash will go away. That means same calling convention and same argument list everywhere. – selbie Jan 09 '12 at 02:56
  • thanks selbie, same signature option work's just fine even when casting is involved... if some other error ocurs I'll consider putting calling convention into ma code.. cheers! – codekiddy Jan 09 '12 at 03:02

2 Answers2

3

No; calling conventions are platform specific. The leading double underscore is a clue that this is an implementation-reserved concept. The language itself makes no requirements about how it is to be implemented at this level of detail.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Actually, C++ *does* know calling conventions: extern "C" and extern "C++" (the latter being the default, and therefore normally not explicitly written). However, usually those calling conventions differ only in name mangling. – celtschk Jan 09 '12 at 02:47
  • thanks for reply! I'll try to use extern "C" and see if that has some benefit :) – codekiddy Jan 09 '12 at 03:03
  • @celtschk: Thanks, good point, though those are called "linkage specifications", and while the standard leaves it completely open what that *does* (and indeed allows a platform to support arbitrary specifications), the "calling conventions" (stack frame cleanup, argument order, register vs stack, etc.) that the OP appears to be interested in remain an implementation detail. – Kerrek SB Jan 09 '12 at 03:10
  • That is certainly true in 32-bit anyway. There are plenty of calling conventions in 32-bit - `__cdecl`, `__stdcall`, `__fastcall`, `__thiscall`, etc. But in 64-bit, they all go way. There is only one calling convention in 64-bit (on Windows anyway, not sure about other platforms). – Remy Lebeau Jan 09 '12 at 05:41
  • @RemyLebeau-TeamB: that's actually x86 specific; other 32 bits architectures obviously differ (how do you store `this` in `ECX` when your ISA doesn't have an `ECX` register?) – MSalters Jan 09 '12 at 07:44
  • Sure, but x86 is all I know :-) The `this` pointer is not passed via register in `__stdcall` and `__cdecl`, it has to be passed on the stack instead. Does ISA support those? Anything that uses registers for passing parameters would certainly be platform-specific. – Remy Lebeau Jan 09 '12 at 21:35
1

The reason why you crash without __cdecl is because the Windows compiler default is to use __stdcall. The latter forces the callee function to cleanup the stack. Hence, your caller is pushing two arguments on to the stack to invoke "Trigger", but Trigger is only popping one argument off the stack on function exit. Hence, you crash. __cdecl calling convention kind of gets around this.

As I said in my comments above, using __cdecl to fix the crash is hiding the real bug. Your argument list of trigger is not matching the argument list expected of "MethodHandler". That's likely the cause of your crash.

This is likely a better fix:

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

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

You didn't share the code that shows how "Trigger" gets registered for a subsequent callback. But I strongly suspect you have put a cast of something like this:

MethodHandler handler = (MethodHandler)Trigger;

Because if you didn't do that, your code didn't compile. Fix your code such that the cast isn't needed for compile, and your real bug goes away as well as your crash.

selbie
  • 100,020
  • 15
  • 103
  • 173
  • heh yeah you've exactly know what was my problem, however casting is needed, i've asked a question on stackoverflow on that but everone said it's impossible to do the same without casting... recheking you answer as the best thanks! please have a look at this http://stackoverflow.com/questions/8756993/how-to-force-implicit-conversion-in-polymorphism – codekiddy Jan 09 '12 at 03:17
  • If you need to cast a member pointer from a derived class to a base class, use a static_cast, which will let you keep some of the type-checking. – servn Jan 09 '12 at 03:48