#include <stdio.h>
#include <string.h>
#include <stddef.h>

#include <Windows.h>

/*
	mingw (tested with gcc4.7.2, gcc4.8.1)
		g++ cpp105-628.cpp -o cpp105-628-gcc

	vc  (tested with vc2008,vc2010,vc2013)
		cl /c cpp105-628.cpp
		link /INCREMENTAL:NO cpp105-628.obj
*/

/*****************************************************************************************/

#ifdef __GNUC__
	#define CALLMEMBERFUNC_DECL_1 __attribute__ ((__optimize__(0,"omit-frame-pointer")))
	#define CALLMEMBERFUNC_DECL_2
#else	//_MSC_VER
	#define CALLMEMBERFUNC_DECL_1
	#define CALLMEMBERFUNC_DECL_2 __declspec(naked)
#endif

#pragma pack(push,1)
template<class T>
struct CALLMEMBERFUNC
{
	typedef void(*CALLMEMBERFUNC_FUNC)();
	typedef void(*BYPASSFUNC)(T* This, void* /*return address*/, ...);

	T* This;
	BYPASSFUNC classfunc;
	char funcdata[40];

	template<class F>
	CALLMEMBERFUNC(T* cls, F clsfunc)
	{
		memcpy((void*)funcdata, (void*)CallMemberFunc, sizeof(funcdata));
		set(cls, clsfunc);
	}

	void *operator new(size_t size)
	{
		return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	}

	void operator delete(void* p)
	{
		VirtualFree(p, 0, MEM_RELEASE);
	}

	template<class F>
	void set(T* cls, F clsfunc)
	{
		This = cls;
		classfunc = (BYPASSFUNC)clsfunc;
	}

	CALLMEMBERFUNC_FUNC getfunc()
	{
		return reinterpret_cast<CALLMEMBERFUNC_FUNC>(&funcdata[0]);
	}

	static void CallMemberFunc() CALLMEMBERFUNC_DECL_1;
	static const int func_offset;
	static const int funcdata_offset;

};
#pragma pack(pop)

#ifdef __GNUC__
template<class T> const int CALLMEMBERFUNC<T>::func_offset = offsetof(CALLMEMBERFUNC<T>,classfunc);
template<class T> const int CALLMEMBERFUNC<T>::funcdata_offset = offsetof(CALLMEMBERFUNC<T>,funcdata);
#else	//_MSC_VER
//#define FIELD_OFFSET(type, field)    ((int)(int*)&(((type *)0)->field))

template<class T> const int CALLMEMBERFUNC<T>::func_offset = FIELD_OFFSET(CALLMEMBERFUNC,classfunc);
template<class T> const int CALLMEMBERFUNC<T>::funcdata_offset = FIELD_OFFSET(CALLMEMBERFUNC,funcdata);
#endif

template<class T>
CALLMEMBERFUNC_DECL_2 void CALLMEMBERFUNC<T>::CallMemberFunc() 
{
#ifdef __GNUC__
	__asm__("call 1f\n\t"
		"1: popl %%eax\n"
		"subl $5,%%eax\n"

		"subl %0,%%eax\n"
		"movl (%%eax), %%ecx\n"

		"add %1, %%eax\n"
		"mov (%%eax), %%eax\n"

		"push %%ecx\n"
		"call *%%eax\n"
		"addl $4,%%esp\n"
		"ret"
		:
		: "i"(funcdata_offset), "i"(func_offset)
	);
#else	//_MSC_VER
	_asm{
		call CallMemberFunc_check_pc
		CallMemberFunc_check_pc:
		pop eax
		sub eax,5

		sub eax,funcdata_offset
		mov ecx,[eax]

		add eax,func_offset
		mov eax, [eax]

		push ecx
		call eax
		add esp,4
		ret
	}
#endif
}

/*****************************************************************************************/

extern "C" typedef int(*FUNC_ADD)(int);
extern "C" int add(FUNC_ADD func, int x, int y)
{
	return func(x) + func(y);
}

extern "C" typedef void(*FUNC_PRINT)(const char*, int, int);
extern "C" void print(FUNC_PRINT func, int x, int y)
{
	func("print x:%d, y:%d\n", x, y);
}

class TEST
{
	int a;
	CALLMEMBERFUNC<TEST> *cmf_add, *cmf_print;
public:

	TEST(int x) : a(x)
	{
		cmf_add = new CALLMEMBERFUNC<TEST>(this, cmf_func_add);
		cmf_print = new CALLMEMBERFUNC<TEST>(this, cmf_func_print);
	}

	~TEST()
	{
		delete cmf_add;
		delete cmf_print;
	}

	int func_add(int x)
	{
		return a * x;
	}

	static int cmf_func_add(TEST* This, void*, int x)
	{
		return This->func_add(x);
	}

	void func_print(const char* fmt, int x, int y)
	{
		printf(fmt, x * a, y * a);
	}

	static void cmf_func_print(TEST* This, void*, const char* fmt, int x, int y)
	{
		This->func_print(fmt, x, y);
	}

	operator FUNC_ADD()
	{
		return reinterpret_cast<FUNC_ADD>(cmf_add->getfunc());
	}

	operator FUNC_PRINT()
	{
		return reinterpret_cast<FUNC_PRINT>(cmf_print->getfunc());
	}
};


int main()
{
	TEST t(100);

	printf("add %d\n", add(t, 1, 2));

	print(t, 3, 4);

	return 0;
}
