众所周知,我们无法定义一个指向类的非静态成员函数。在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