#include <iostream>

class Null { };

#define DELEGATE(returnType, ...) Delegate<returnType (*)(__VA_ARGS__), returnType (Null::*)(__VA_ARGS__)>
#define ARGS(...) __VA_ARGS__

template<class _FunctionType, class _MemberType>
struct Delegate {
	typedef _FunctionType FunctionType;
	typedef _MemberType MemberType;

	Null* mpThis;
	union DelPtr {
		_FunctionType asStatic;
		_MemberType asMember;
		DelPtr(_FunctionType p) : asStatic(p) { }
		DelPtr(_MemberType p) : asMember(p) { }
	};
	DelPtr mpfn;
	
	Delegate(_FunctionType pfn) : mpThis(NULL), mpfn(pfn) { }
	Delegate(_MemberType pfn, void* pThis) : mpThis((Null*)pThis), mpfn(pfn) { }
};

#define INVOKE(_delegate, ...) ( \
	(_delegate).mpThis ? \
		(((_delegate).mpThis)->*((_delegate).mpfn.asMember))(__VA_ARGS__) \
	: \
		((_delegate).mpfn.asStatic)(__VA_ARGS__) \
	)


float test1(int a, int b) {
	std::cout << "a:" << a << std::endl;
	std::cout << "b:" << b << std::endl;
	return 7.0f;
}

class TestClass {
public:
	double x;
	float test2(int a, int b) {
		std::cout << "TestClass a:" << a << std::endl;
		std::cout << "TestClass b:" << b << std::endl;
		std::cout << "TestClass x:" << x << std::endl;
		return 5.0f;
	}
};

class TestClass2 {
public:
	char c;
	float  test3(int a, int b) {
		std::cout << "TestClass2 a:" << a << std::endl;
		std::cout << "TestClass2 b:" << b << std::endl;
		std::cout << "TestClass2 c:" << c << std::endl;
		return 3.0f;
	}
};

// Declare MANUALLY
// typedef float (*FnStatic)(int, int) ;
// typedef float (NullClass::*FnMember)(int, int);
// typedef Delegate<FnStatic, FnMember> Delegate1;

// Declare using DELEGATE macro
typedef DELEGATE(float, ARGS(int, int)) Delegate1;

int main() {
	TestClass obj;
	obj.x = 23.3;
	TestClass2 obj2;
	obj2.c = 'h';

	std::cout << "test\n";

	Delegate1 d = Delegate1(test1);
	Delegate1 e = Delegate1((Delegate1::MemberType)&TestClass::test2, &obj);
	Delegate1 f = Delegate1((Delegate1::MemberType)&TestClass2::test3, &obj2);

	std::cout << "d return value: " << (INVOKE(d, ARGS(5, 6))) << std::endl;
	std::cout << "e return value: " << (INVOKE(e, ARGS(8, 9))) << std::endl;
	std::cout << "f return value: " << (INVOKE(f, ARGS(1, 2))) << std::endl;

	int x;
	std::cin >> x;
	return 0;
}