I've been searching for answers to this problem for the past hour but can't find a solution that works. I'm trying to use function pointers to call a non-static member function of a specific object. My code compiles fine, but during runtime I get a nasty runtime exception that says:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
A lot of websites said to specify the calling convention in the method header, so I added __cdecl
before it. However, my code encountered the same runtime exception after the change (I tried using other calling conventions as well). I'm not sure why I have to specify cdecl in the first place because my project settings are set to cdecl. I am using some external libraries, but those were working fine before I added this function pointer stuff.
I'm following this: https://stackoverflow.com/a/151449
My code:
A.h
#pragma once
class B;
typedef void (B::*ReceiverFunction)();
class A
{
public:
A();
~A();
void addEventListener(ReceiverFunction receiverFunction);
};
A.cpp
#include "A.h"
A::A(){}
A::~A(){}
void A::addEventListener(ReceiverFunction receiverFunction)
{
//Do nothing
}
B.h
#pragma once
#include <iostream>
#include "A.h"
class B
{
public:
B();
~B();
void testFunction();
void setA(A* a);
void addEvent();
private:
A* a;
};
B.cpp
#include "B.h"
B::B(){}
B::~B(){}
void B::setA(A* a)
{
this->a = a;
}
void B::addEvent()
{
a->addEventListener(&B::testFunction); //This is the offending line for the runtime exception
}
void B::testFunction()
{
//Nothing here
}
main.cpp
#include "A.h"
#include "B.h"
int main()
{
A* a = new A();
B* b = new B();
b->setA(a);
b->addEvent();
}
I'm running with Visual Studio 2010, but I'd like my code to work on other platforms with minimal changes.
Seems not many has reproduced the problem, I'll first show the behavior of VS2010 on this piece of code here. (DEBUG build, 32bit OS)
The problem is in
B::addEven()
andA::addEventListener()
. To give me a reference point to check theESP
value, two additional statements are added toB::addEven()
.A:: addEventListener()
usedret 10h
to clear the stack, but only 4 bytes are pushed into the stack (push offset B::testFunction
), which cause the stack frame to be corrupted.Seem that depending whether
B
is complete or not,sizeof(void B::*func())
would change in VS2010. In OP's code, in A.cppB
is not complete, and the size is10h
. In call site B.cpp, whenB
is already complete, the size becomes04h
. (This can be checked bysizeof(ReceiverFunction)
as shown in the above code). This caused that in the call site, and in the actual code ofA::addEventListener()
, the size of the augment/parameter are not the same, thus caused stack corruption.I changed the order of inclusion to make sure
B
is complete in every translation unit, and the runtime error disappears.This should be a VS2010 bug ...
Compiler Command Line:
Linker Command Line:
I hid some pathes in the command line.
Using /vmg as a compiler option fixed the problem.
However, I decided to use a delegate library instead (http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx), and it works well!
This is a known problem, necessary ingredients are a member pointer declaration using an incomplete class and having it used in different translation units. An optimization in the MSVC compiler, it uses different internal representations for a member pointers depending on the inheritance.
The workaround is to compile with
/vmg
or to declare the inheritance explicitly: