众所周知,我们无法定义一个指向类的非静态成员函数。在Borland C/C++中,Bolrand公司添加了关键字__closure用来定义这种特殊的指针。在Visual C/C++中虽然也有解决方染,但是使用麻烦,且不易于使用。网络上下存在很多解决方案,但作者均未看到较为好用的解决方案。
本文即提供一种于x86-32平台下的解决方案,使用方式类似于Borland C/C++,十分方便。代码在Borland C/C++ 5.82和Microsoft C/C++ 14.0 (Visual C/C++ 8.0)下测试通过。
#include<stdio.h>
#include<stddef.h>
class _Object{};
#if defined(SetEventCall)||defined(CallEvent)||defined(DefineEvent)
#error SetEventCall,CallEvent,DefineEvent 已经定义
#endif//
#ifdef __BORLANDC__
#define SetEventCall(event_obj,event_func) event_obj=event_func
#define CallEvent(event_obj) event_obj
#define DefineEvent(name,result,intro) result (__closure *name)intro
#else
#pragma warning(disable:4311)
template <typename T> struct EventFunc
{
unsigned __int32 this_address;
unsigned __int32 func_address;
_Object *This;
T Func;
EventFunc()
{
this_address=offsetof(EventFunc,This);
this_address+=(unsigned __int32)this;
func_address=offsetof(EventFunc,Func);
func_address+=(unsigned __int32)this;
}
};
#define SetEventCall(event_obj,event_func) { \
unsigned __int32 this_address=event_obj.this_address; \
unsigned __int32 func_address=event_obj.func_address; \
\
{ \
__asm mov eax,this_address \
__asm mov ebx,this \
__asm mov [eax],ebx \
\
__asm mov eax,func_address \
__asm mov ebx,event_func \
__asm mov [eax],ebx \
} \
}
#define CallEvent(event_obj) (event_obj.This->*(event_obj.Func)) //(*(event_obj.This).*(event_obj.Func))
#define DefineEvent(name,result,intro) EventFunc<result (_Object:: *)intro> name;
#endif//__BORLANDC__
class Button
{
public:
DefineEvent(OnClick,void,(Button *,int)); //定义事件,原型为: void OnClick(Button *,int)
public:
Button()
{
printf("Button this=%p\n",this);
}
void TestButtonClick()
{
CallEvent(OnClick)(this,0); //呼叫OnClick,原型为: OnClick(this,0)
}
};
class Test
{
Button *button;
public:
void OnButtonClick(Button *but,int)
{
printf("Test::OnButtonClick,this=%p,but=%p\n",this,but);
};
public:
Test()
{
printf("Test this=%p\n",this);
button=new Button;
SetEventCall(button->OnClick,OnButtonClick); //设定button->OnClick事件的处理函数为OnButtonClick
button->TestButtonClick();
}
};
void main(int,char **)
{
Test *test;
#ifdef __BORLANDC__
printf("Compiler: Borland C/C++ or Turbo C/C++ %d.%d%d\n",(__BORLANDC__>>8),((__BORLANDC__&0xF0)>>4),(__BORLANDC__&0x0F));
#endif
#ifdef _MSC_VER
printf("Compiler: Microsoft C/C++ %.2f (Visual C/C++ %.2f)\n",_MSC_VER/100.f,_MSC_VER/100.f-6);
#endif//
printf("Compile Time: %s %s\n\n",__DATE__,__TIME__);
test=new Test;
delete test;
}
Compiler: Borland C/C++ or Turbo C/C++ 5.82 Compile Time: Dec 23 2006 17:34:48 Test this=00902D50 Button this=00902D64 Test::OnButtonClick,this=00902D50,but=00902D64
Compiler: Microsoft C/C++ 14.00 (Visual C/C++ 8.00) Compile Time: Dec 23 2006 17:34:00 Test this=003826D0 Button this=00382700 Test::OnButtonClick,this=003826D0,but=00382700