成员函数指针以及C++委托

来源:互联网 发布:python自动化测试工具 编辑:程序博客网 时间:2024/06/05 17:13

原文地址:点击打开链接

说明

面对对象的函数指针,这一可称为闭包或者委托的特性在其他语言中证实了自己存在的价值。然而,在标准C++中并不支持面对对象的函数指针,这对于C++程序员而言是十分不幸的。在Delphi(Object Pascal)中,他们是Borland公司可视化组件库(VCL)的基础。C#的成功在一定意义上是因为推广了委托的概念。对于许多应用程序而言,委托简化了利用设计模式(观察者模式,策略模式,状态模式[GOF])构建松散耦合的对象。毫无疑问,在标准C++中这也是相当有用的。

在标准C++中,仅提供了成员函数指针而不是委托,然而大部分C++程序员由于各自不同的原因(比如,成员函数指针有稀奇古怪的语法->,*运算符,难以确定准确的运行过程)从不使用成员函数指针,通常而言,利用其它方式解决问题比使用成员函数指针更为方便而且易于理解。这有点反感的是:事实上,编译器层面上实现委托比程序员自己封装成员函数指针实现委托容易的多。

本文中,作者将详细的解释成员函数指针。在概括性的描述成员函数指针的特性以及语法之后,作者将解释成员函数指针在常用编译器中是如何实现的。作者将展示如何有效的通过编译器实现委托。最后,作者将通过成员函数指针的编译器中的实现方式这一特性,高效的实现在大多数C++编译器上可用的委托。比如:Visual C++单播委托仅仅生成两行汇编代码。


函数指针

我们从一个函数指针的描述开始。在C和C++中,一个函数名为my_func_ptr,形参为int和char*,而返回值为float的函数指针类型的声明如下:

float (*my_func_ptr)(int, char *);// To make it more understandable, I strongly recommend that you use a typedef.// Things can get particularly confusing when// the function pointer is a parameter to a function.// The declaration would then look like this:typedef float (*MyFuncPtrType)(int, char *);MyFuncPtrType my_func_ptr;

要注意的是对于不同的参数组合存在不同的函数指针。在MSVC中,对于某种函数指针又存在三种不同的调用约定(维持堆栈平衡等):__cdecl__stdcall, 和 __fastcall。你可以令你的函数指针像这样指向函数float some_func(intchar *):

 my_func_ptr = some_func;

当成功赋值以后,可以像这样调用函数:

 (*my_func_ptr)(7, "Arbitrary String");

对于函数指针而言,你可以令指针指向相同形式(形参类型以及返回值类型形同)的任意指针,但却不能令指针指向void*类型指针,而其他的操作对于我们而言则是可有可无。一个函数指针可以设置为0以表示其为空指针,不指向任何函数。对于函数指针这一类型而言,所有的比较运算符都是有效的(==!=<><=>=),你可以使用==0或者隐身转换为bool测试空指针。有趣的是,一个函数指针可以当做一种非类型模板参数来使用,这是基于函数指针中一个类型参数的不同,整个函数指针将存在差异的这一事实。基于名称的模板参数并不是所有的编译器都支持,也并不全部支持模板特化。

在C中,函数指针最常用的用途是用做参数的函数库,想qsort,Windows函数回调,等许多其他应用。函数指针的实现很简单:他们只是“代码指针”:汇编语言中的其实地址,函数指针的不同类型只是为了确保正确的调用约定。

成员函数指针

在C++程序中,大多数的功能都是通过成员函数实现的:即他们是类的一部分。你不能使用一个普通的函数指针指向一个类的成员函数,而只能使用成员函数的函数指针。一个指向类SomeClass类的成员函数指针声明如下:

float (SomeClass::*my_memfunc_ptr)(int, char *);// For const member functions, it's declared like this:float (SomeClass::*my_const_memfunc_ptr)(int, char *) const;

注意一个特殊的操作符(::*) ,这是SomeClass类声明的一部分。成员函数指针有如下的限制:他们只能指向一个单一的类成员函数。对于不同的参数组合存在不同的成员函数指针。在MSVC中,对于某种成员函数指针又存在四种不同的调用约定(维持堆栈平衡等):__cdecl__stdcall, 、__fastcall和__thiscall.(__thiscall是默认的),有趣的是,并没有__thiscall这个关键字,但是某些错误信息中却能看到他的身影(说明他是保留工将来使用的)。如果你使用成员函数指针,你应该总是使用typedef避免混乱。

你可以这样使用成员函数指针指向函数:

 my_memfunc_ptr = &SomeClass::some_member_func; // This is the syntax for operators: my_memfunc_ptr = &SomeClass::operator !; // There is no way to take the address of a constructor or destructor
一些编译器(最明显的是其中的6和7)会让你忽略&,尽管他是非标准或混乱的。更符合标准的编译器(例如,GNU G++和MSVC 8(又名VS 2005))需要它,所以你应该确保已经写上。调用这个成员函数指针,你需要提供SomeClass实例,你必须使用特殊的运算符->*。该运算符具有较低的优先级,所以你需要把它放在括号中:

 SomeClass *x = new SomeClass;  (x->*my_memfunc_ptr)(6, "Another Arbitrary Parameter");// You can also use the .* operator if your class is on the stack.  SomeClass y;  (y.*my_memfunc_ptr)(15, "Different parameters this time");

不要奇怪这个语法--这似乎是C++的设计喜欢的标点符号!C++相对于C增加了三个特殊的运算符用于支持成员指针,::*专门用于支持成员指针。而->*和.*用于调用指针指向的函数。看来,这需要花费额外的注意力来关注这些极少用到的语言特性(你甚至可以重载->*操作符)

成员函数指针可以设置为0,并提供了运算符== 和!=,但只能是同一类中的成员函数指针。任何成员函数指针都可以与0比较,看他是否为空。(这并不是所有的编译器上都有效,在Metrowerks MWCC中,一个指向类的第一个虚拟函数的指针将等于0),不同于简单的函数指针,运算符(<,>,<=,>=)都是不可用的。像函数函数指针一样,他们可以被用来做为非类型的模板参数,但这似乎只有在少数编译器上才有效。


一些关于成员函数指针的怪异

对于成员函数指针存在一些奇怪的现象。首先,你不能使用一个成员函数指针指向一个静态成员函数。你必须使用一个普通的函数指针。(因此,名称“成员函数指针”有些误导:他们是“非静态成员函数的指针”。)其次,在处理派生类的时,还有一些意外,例如:

class SomeClass { public:     virtual void some_member_func(int x, char *p) {       printf("In SomeClass"); };};class DerivedClass : public SomeClass { public: // If you uncomment the next line, the code at line (*) will fail!//    virtual void some_member_func(int x, char *p) { printf("In DerivedClass"); };};int main() {    // Declare a member function pointer for SomeClass    typedef void (SomeClass::*SomeClassMFP)(int, char *);    SomeClassMFP my_memfunc_ptr;    my_memfunc_ptr = &DerivedClass::some_member_func; // ---- line (*)}
奇怪的是,&DerivedClass::some_member_func是类SomeClass的成员,而不是类DerivedClass的成员(部分编译器可能有不同的行为:例如,对于Digital Mars C++,&DerivedClass::some_member_func是未定义的)。但是,如果DerivedClass覆盖了some_member_func,代码则不能编译,因为DerivedClass::some_member_func现在已经成为一个DerivedClass的成员函数指针了。

成员函数指针的转换是一个非常危险的区域。C++标准化过程中,很多人在讨论对于从一个类的基类到派生类是否应该自动转化成员函数指针以及是否可以转化不相关的类成员指针。在标准委员会定下标准之时,不同的编译器供应商已经对这个问题采取不同的回答。根据C++标准(5.5.10、9节),你可以使用reinterpret_cast从一个指向类成员函数指针到一个指向不相关类的成员函数指针转化。调用转化指针指向的函数的结果将是未定义的。你可以利用他的唯一一件事就是将她转化为原本的类成员函数指针类型。因为这是标准与编译器相似性很小的一个区域。

在一些编译器中,转化基类和派生类的成员函数指针时,将发生一些出乎意料之外的事,当涉及派生类的时候,使用reinterpret_cast转化一个派生类成员函数指针到基类时,有时可以成功,有时却失败,这取决于派生类中的声明。例如:

class Derived: public Base1, public Base2 // case (a)class Derived2: public Base2, public Base1 // case (b)typedef void (Derived::* Derived_mfp)();typedef void (Derived2::* Derived2_mfp)();typedef void (Base1::* Base1mfp) ();typedef void (Base2::* Base2mfp) ();Derived_mfp x;

在case(a)中,static_cast<Base1mfp>(x)将正常工作,但是static_cast<Base2mfp>(x)则失败。而在case(b)中结果则正好相反,你只可以安全的把成员函数指针从派生类转化为第一个基类!如果你尝试转化,MSVC将出现C4407警告,Digital Mars C++则出现一个错误。如果你使用reinterpret代替static_cast,上例基于不同原因都将得到保护,提示开发者。但是某些编译器将成功编译,不管你做了什么。当心!!

在C++标准中有另外一条有趣的规则:在类定义之前你可以声明一个类的成员函数指针。甚至可以调用这个不完全类型的成员函数!这将在本文后面进行讨论。请注意,一些编译器成功编译。

值得注意的是,C++除了支持成员函数指针外,还支持数据成员的指针,他们共享一些操作以及一些问题的实现。他们都被用于std::stable_sort,除此之外没发现有其他的使用。

成员函数指针的使用

现在,作者已经通过实例显示出,成员函数指针的某些奇怪的特性。但是成员函数指针到底有什么作用呢?我通过对网上查找到的开源代码进行广泛的探索。发现成员函数指针的两个常见的使用方式:

a.勉强的用例。用于为C++初学者演示语法特性

b.委托的实现

另外,在STL和Boost库中,也允许在标准算法中使用成员函数。在这种情况下,他们出现在编译过程中,而不是出现在所要编译的代码中。成员函数指针最有趣的应用在于定义复杂的接口。一些令人印象深刻的是可以这样做的,但是作者没有找到很多例子。大多数时候,这些工作大多数可以通过虚函数或者问题的重构而得到更优雅的实现。但到目前为止,最著名的成员函数指责你的使用还是在各种应用框架的构造上。比如:成员函数指针是MFC消息系统的核心。

当你使用MFC的消息映射宏(例如,ON_COMMAND)时,实际上,你是在填充一个包含消息ID和成员函数的指针(特别是,CCmdTarget::*成员函数指针)。这就是MFC类如果要相应消息必须继承自CCmdTarget的原因。但不同的消息处理函数有不同的参数列表(例如,OnDraw处理程序有CDC*作为第一个参数),所以数组中必须各种类型的成员函数指针。然而MFC是如何处理这种情况的?他们将所有可能的成员函数指针汇聚程一个巨大的组合,颠覆了常规的C++类型检查(详细阅读在afximpl.h中MessageMapFunctions union和cmdtarg.cpp ),因为MFC的这个重要特性,所有的C++编译器都支持这一点。

在查阅资料过程中,作者没有找到非编译时使用得很好的例子。正因为其复杂性,成员函数指针并不被多数语言所接受。最终不得不说,C++成员函数指针十个有缺陷的设计。

在作者写这篇文章之时,C++标准允许成员函数指针进行强制转换,但是一旦完成转换后,该指针却不允许直接调用。基于以下三个原因,C++的这一标准显得有些荒谬。首先,并不是所有的编译器都支持这一强制转换(因此,进行转换是C++标准,但却非所有编译器都支持)。其次,在所有编译器上,假如转换成功后,调用转换后的成员函数指针能够得到你所期望的结果时,不要认为这是一种未定义的行为(调用过程是由编译器所决定的,而不是C++标准)。第三,允许强制转换,但却不允许调用完全没有价值可言;但是如果可以进行强制转换并调用的话,却能够实现有效的委托特性,这一点无疑对于C++而言非常有用。

作者将使用下述的合法C++代码为读者说明这一有争论的论断:

class SomeClass;typedef void (SomeClass::* SomeClassFunction)(void);void Invoke(SomeClass *pClass, SomeClassFunction funcptr) {  (pClass->*funcptr)(); };

请牢记的是,在编译器是直接生成汇编代码进行调用,却不知道SomeClass这个类的存在的。现在,除非连接器对代码进行了一些类似_extremely_之类的优化,否则代码是能够不管实际类的定义而正确的调用了函数。但这样导致一个直接的后果是,你可以十分安全的调用一个不相关类进行强制转换后的成员函数指针。为了解释作者所给出的论断,作者在此阐述了关于编译器在实现成员函数指针时做了哪些工作。这也有助于解释为什么成员函数指针的使用有这样的限制。为了更准确的证实这一观点,作者将通过大量的汇编代码进行解释。


为何成员函数指针如此复杂

类成员函数与标准C函数之间是存在一点差异的。在类成员函数之中,他存在一个隐含的参数:this指针,用于指向该类的实例。在不同的编译器上,该指针通常被视为一个正常的内部参数,但某些编译器却对此进行的额外的处理(比如,在VC++中,通常使用ECX寄存器进行存储)。this指针是一个不同寻常的指针,对于虚函数而言,他控制了运行时具体执行的函数。即使一个成员函数实现的功能与普通函数一样,但C++标准却不能保证普通函数的行为与成员函数一致:这是由于没有thiscall这种类型的调用约定,成员函数与普通函数同源而不同宗(都是函数却存在差异)。

 也许你可能觉得,成员函数指针只是想普通函数指针一样持有代码的首地址的话,那么只能说这是一种误解。几乎在所有的编译器中,成员函数指针所包含的内容都是大于普通函数指针的。更为奇怪的是,在VC++中,成员函数指针可以是4,8,12,甚至16个字节,这取决与他与类之间所存在的联系以及编译器的设置。显然,某些时候成员函数指针比我们想象的复杂。

在80年代初,当初代C++编译器(CFront)刚开始出来的时候,他仅仅支持单继承而已。而在介绍成员函数的时候,他们也仅仅只是一个带有额外参数作为第一参数的函数指针而已。当调用虚函数时,函数指针指向一段“thunk”代码(更新:在现代C++的讨论中,认为CFront并未真正使用到thunks,由于其并非优雅的设计,但实际上CFront可能已经使用了这种方法)。

但在CFront2发布之时,打破了这一现状,出现了模板以及多继承。多重继承带来了成员函数指针的阉割。更为严重的问题是,直到调用的时候,你才知道多重继承类中成员函数指针的指向。假如,存在如下四个类定义:

class A { public:       virtual int Afunc() { return 2; };};class B { public:       int Bfunc() { return 3; };};// C is a single inheritance class, derives only from Aclass C: public A { public:      int Cfunc() { return 4; };};// D uses multiple inheritanceclass D: public A, public B { public:     int Dfunc() { return 5; };};
假设我们创建了一个C类的成员函数指针。在这个例子中,Afunc和Cfunc都是C类的成员函数,所以我们的成员函数指针可以指向Afunc或Cfunc,但Afunc需要一个指向C::A的指针(本文中称为Athis),而Cfunc则需要一个指向C类的指针(本文中称为Cthis)。

在编译器开发者处理这一点的时候,他们保证了C类物理的首地址,这意味着Athis==Cthis,这样我们就可以仅仅考虑一个this指针就行了。

现在,假如我们又创建了一个D类的成员函数指针,同样的道理,该成员函数指针可以指向Afunc,Bfunc,或Dfunc,但Afunc需要一个指向D::A的指针,而Bfunc需要一个指向D::B的指针,但现在,上述利用同一首地址相同的技巧在编译器上却不能使用了,我们不能把A.B.D三个首地址相同,因此指向D的指针就需要特别的处理了,不仅需要指明哪个方程被调用还要明确指针的指向。在编译器中,编译器是能够获取A类的大小,所以实际上D的指针可以通过A的指针加上一个便宜量来获取(data = sizeof(A));

如果你使用了虚拟继承(虚基类),这会将导致事情更加复杂。这可能让你在了解这一机制的同时把自己搞混了。通常情况下,编译器使用虚表来存储每一个虚函数地址,以及Virtual_delta字节来提供该地址指针到this指针的转化。

假如C++定义给成员函数另一个确切的定义则可以免去了如此复杂的机制。在上诉代码中,复杂的来源是因为你能够将A::Afunc作为D::Afunc的一个引用。这不是一种良好的风格。通常情况下,这应该使用基类的接口,而不是指针。然而假如你不得不这样做的话,成员函数指针可以当做是一个有特殊调用约定的普通函数指针。但这导致的一些列后果不是你所期望看到的。


成员函数指针的实现

到此,我们来讨论一下编译器内部是如何实现成员函数指针的?下表中给出了对各个结构使用sizeof获取字节数的结果。

CompilerOptionsintDataPtrCodePtrSingleMultiVirtualUnknownMSVC
444481216MSVC/vmg44416#16#16#16MSVC/vmg /vmm4448#8#--8Intel_IA32
444481216Intel_IA32/vmg /vmm44448--8Intel_Itanium
4888121620G++
4448888Comeau
4448888DMC
4444444BCC32
44412121212BCC32/Vmd444481212WCL386
44412121212CodeWarrior
44412121212XLC
48820202020DMCsmall2222222
medium2244444WCLsmall2226666
compact2426666
medium2248888
large2448888

# Or 4,8, or 12 if the __single__multi__virtual_inheritance keyword is used.

好的编译器

几乎所有的编译器,都有偏移和索引来调整设置指针的指向,如下是C++和Borland使用到的技巧:

struct BorlandMFP { // also used by Watcom   CODEPTR m_func_address;   int delta;   int vindex; // or 0 if no virtual inheritance};if (vindex==0) adjustedthis = this + delta; else adjustedthis = *(this + vindex -1) + deltaCALL funcadr

如果使用了虚函数,则函数指针将指向一段thrunk来获取实际调用的函数地址,Borland却应用了一个优化:如果他知道类只是使用了单一继承,那么他的偏移和索引都永远是零,所以他讲跳过计算实际地址这个步骤。更重要的是,他只是改变调用地址,而不改变自身的结构。

// Metrowerks CodeWarrior uses a slight variation of this theme.// It uses this structure even in Embedded C++ mode, in which// multiple inheritance is disabled!struct MetrowerksMFP {   int delta;   int vindex; // or -1 if no virtual inheritance   CODEPTR func_address;};// An early version of SunCC apparently used yet another ordering:struct {   int vindex; // or 0 if a non-virtual function   CODEPTR func_address; // or 0 if a virtual function   int delta;};
Metrowerks并不需要内敛的计算偏移地址,取而代之的是,他执行了一小段'member function invoker'的跳转,这减少的代码尺度的同时却额外的增加了成员函数指针执行的时间。

Digital Mars C++(原Zortech C++,然后更名为Symantec C++)采取了不同的优化。单继承类,成员函数指针指向的是函数的地址,当有更复杂的继承关系时,成员函数指针指向“thrunk”函数,进行必要的调整this指针,然后调用正确的函数。一个小函数解决了所有复杂继承的成员函数调用,这是作者最为中意的实现方式。

struct DigitalMarsMFP { // Why doesn't everyone else do it this way?   CODEPTR func_address;};

当前版本的GNU编译器则使用了一个奇怪而且复杂的优化。他认为当存在虚拟继承时,你必须寻找虚表获得偏移来计算this指针的位置。

// GNU g++ uses a tricky space optimisation, also adopted by IBM's VisualAge and XLC.struct GnuMFP {   union {     CODEPTR funcadr; // always even     int vtable_index_2; //  = vindex*2+1, always odd   };   int delta;};adjustedthis = this + deltaif (funcadr & 1) CALL (* ( *delta + (vindex+1)/2) + 4)else CALL funcadr

struct Pro64MFP {     short delta;     short vindex;     union {       CODEPTR funcadr; // if vindex==-1       short __delta2;     } __funcadr_or_delta2;   };// If vindex==0, then it is a null pointer-to-member.

// Compilers using the EDG front-end (Comeau, Portland Group, Greenhills, etc)struct EdisonMFP{    short delta;    short vindex;    union {     CODEPTR funcadr; // if vindex=0     long vtordisp;   // if vindex!=0    };};if (vindex==0) {   adjustedthis=this + delta;   CALL funcadr;  } else {    adjustedthis = this+delta + *(*(this+delta+vtordisp) + vindex*8);   CALL *(*(this+delta+funcadr)+vindex*8 + 4); };

微软的“小类”方法

微软编译器使用的优化类似Borlannd,他们对但以继承具有最佳效率。但不像Borland,默认为0。这意味着单一继承时,成员函数指针与简单函数指针大小相同,而多重继承则成员函数指针偏大,而虚拟继承则更加大。这节省了控件,但却不合适标准,更是带来了一些奇怪的副作用。

首先,在一个派生类和基类之间进行强制转换的时候会改变其大小,因此很多信息可能丢失。其次,当在类声明中声明成员函数的时候(类未定义),编译器必须解决分配多少空间给他这个问题,但是这个分配又不可靠,因为只有定义以后才能看到他的继承关系。这就猜。这样就可能导致程序死机。所以在微软编译器中添加了一些保留字:__single_inheritance,__multiple_inheritance,和__virtual_inheritance。他们还正价了一个编译器开关:/VMG,使得通过保留0域,所有MFPs具有相同的尺寸。这让问题变得更麻烦。

文档中说明了一点:/VMG无异于对没给类添加了__virtual_inheritance关键词。其实不然,相反,他使用了一个更加的结构,在此称为unknown_inheritance。还有就是不管类中是否有成员函数指针,他都添加了一个生命。他们不能使用_virtual_inheritance。因为他们使用了一个愚蠢的优化。算法如下:

// Microsoft and Intel use this for the 'Unknown' case.// Microsoft also use it when the /vmg option is used// In VC1.5 - VC6, this structure is broken! See below. struct MicrosoftUnknownMFP{   FunctionPointer m_func_address; // 64 bits for Itanium.   int m_delta;   int m_vtordisp;   int m_vtable_index; // or 0 if no virtual inheritance}; if (vindex=0) adjustedthis = this + delta else adjustedthis = this + delta + vtordisp + *(*(this + vtordisp) + vindex) CALL funcadr

在虚拟继承的情况下,该vtordisp值不存储在__virtual_inheritance指针!取而代之的是,当函数被调用时,编译器硬编码汇编代码中。但是,处理不完整的类型,你需要知道它。所以他们最终与两种类型的虚拟继承的指针。但直到VC7的情况下,unknown_inheritance是无可救药的Bug。vtordisp 和 vindex总是0可怕的后果:对vc4-vc6,指定/ VMG选项(无/ VMM或/ VMS)可能会导致错误的调用函数!这将是很难追踪。在VC4,IDE选择/ VMG选项有一个选项,但它被禁用。我怀疑有人在MS知道有这个错误,但他们从来没有列出来。他们终于固定VC7

英特尔使用相同的计算环境,但他们的/ VMG选项的行为完全不同(它几乎没有影响,它不仅影响unknown_inheritance案例)。发行说明他们的编译状态,成员类型指针之间的转换不在虚拟继承的情况下完全支持,并警告说,不正确的代码生成的编译器崩溃或如果你尝试的结果。这是语言非常肮脏的角落

然后有codeplay。codeplay的早期版本的vectorc与微软VC6,GNU链接兼容性选项,并metrowerks。但是,他们总是用微软的方法。他们逆向工程它,正如我,但他们没有检测unknown_inheritance案例,或vtordisp价值。他们的计算式(错误地)认为vtordisp = 0,那么错误的功能可以被一些(模糊)例。但codeplay即将发布的vectorc 2.2.1有固定的这些问题。成员函数指针现在二进制与微软或GNU兼容。一些高性能的优化和一个巨大的改进的标准一致性模板部分特化等)这是成为一个非常令人印象深刻的编译器


我们从中学到了什么?

在理论上,所有这些供应商能从根本上改变他们的技术以MFPSs为基础。在实践中,这是极不可能的,因为这将打破了很多现有的代码。在MSDN,有一个很老的文章,出版了微软解释运行的实现细节的Visual C + + [ jangray ]。它是由 Jan Gray写的,MS C++对象模型1990的作者。虽然文章的日期从1994,它仍然是相关的排除bug修复,15年间微软并没有改变它。同样,最早的编译器Borland C ++ 3(1990产生相同的代码,Borland公司的最新的编译器当然除了16位寄存器是32位的取代

现在,你知道成员函数指针太多。这有什么意义?虽然这些实现彼此有很大的不同,他们有一些有用的共同点:需要调用成员函数指针的汇编代码是相同的,无论是什么层和涉及的参数。一些编译器应用优化取决于类的继承性,但当类被调用是不完全类型,所有这样的优化是不可能的。这其实可以被利用来建立有效的代表。


委托

在委托中并不难看见成员函数指针的身影。他们可以使用在C程序函数指针出现的任意地方。也许更重要的是他改进并实现了Subject/Oberver设计模式。Oberver模式在GUI代码中是非常明显的,是一个应用程序的核心。同时,委托更优雅的实现了Strategy和State设计模式。

委托不止比成员函数指针更有用,而且更为简单。由于.NET语言提供了委托,我们可能会认为这是一个非常高层的高年,用汇编代码难以实现。但事实并非如此:调用一个委托是一个非常底层的概念,其效率与调用普通函数相差无几。一个C++委托仅仅需要包含一个this指针和一些简单的函数指针。当你设置一个委托时,你必须指定委托所需要的this指针以便进行函数调用。下面是在X86中调用委托生成的汇编代码:

    mov ecx, [this]    call [pfunc]
然而,C++中无法产生如此高效的代码,Borland通过添加新的关键字(__closure)解决了这个问题,允许使用方便的语法和优化生成的代码。GNU编译器增加了一个语言扩展,但与Borland不兼容。如果你使用了这些语言扩展,你将把自己限制到一个单一的编译器之上。相反的是,你把自己限制在C++标准上,这委托实现得就没那么有效了。

有趣的是,在C#等.net语言中,委托的实现在效率上比这慢了几十倍(MSDN),作者怀疑这是垃圾回收和网络安全的需求到导致的额外消耗。最近,微软在VC++中添加了“unified event model”,随之而来的是增加了关键词__event,__raise,__hook,__unhook,event_source和event_receiver.然而作者认为这是相当可怕的,因为这些完全非标准,语法丑陋并且产生了非常低效的代码。


动机:快速委托存在的意思

现在已经存在有大量的使用标准C++实现的委托。所有这些代码都是基于这样的思想,将成员函数指针封装为委托。但这些委托都作用于一个类上,为了摆脱这个限制,你需要增加额外的间接层:通过模板创建一个类的成员函数调用。委托持有一个this指针以及一个指向调用函数的指针。而且成员函数调用还需要在退栈上分配。

在codeproject中已存在了许多各种各样的委托实现。但我想要的委托是仅仅只产生两行汇编代码的实现。但这则必须依赖于编译器的实现,并不符合标准。


使用代码

using namespace fastdelegate;int main(void){    // Delegates with up to 8 parameters are supported.    // Here's the case for a void function.    // We declare a delegate and attach it to SimpleVoidFunction()    printf("-- FastDelegate demo --\nA no-parameter              delegate is declared using FastDelegate0\n\n");                            FastDelegate0 noparameterdelegate(&SimpleVoidFunction);    noparameterdelegate();     // invoke the delegate - this calls SimpleVoidFunction()    printf("\n-- Examples using two-parameter delegates (int, char *) --\n\n");    typedef FastDelegate2<int, char *> MyDelegate;    MyDelegate funclist[10]; // delegates are initialized to empty    CBaseClass a("Base A");    CBaseClass b("Base B");    CDerivedClass d;    CDerivedClass c;      // Binding a simple member function    funclist[0].bind(&a, &CBaseClass::SimpleMemberFunction);  // You can also bind static (free) functions    funclist[1].bind(&SimpleStaticFunction);  // and static member functions    funclist[2].bind(&CBaseClass::StaticMemberFunction);  // and const member functions    funclist[3].bind(&a, &CBaseClass::ConstMemberFunction);  // and virtual member functions.    funclist[4].bind(&b, &CBaseClass::SimpleVirtualFunction);  // You can also use the = operator. For static functions,  // a fastdelegate looks identical to a simple function pointer.    funclist[5] = &CBaseClass::StaticMemberFunction;  // The weird rule about the class of derived  // member function pointers is avoided.  // Note that as well as .bind(), you can also use the   // MakeDelegate() global function.    funclist[6] = MakeDelegate(&d, &CBaseClass::SimpleVirtualFunction);  // The worst case is an abstract virtual function of a   // virtually-derived class with at least one non-virtual base class.  // This is a VERY obscure situation, which you're unlikely to encounter   // in the real world, but it's included as an extreme test.    funclist[7].bind(&c, &CDerivedClass::TrickyVirtualFunction);  // ...BUT in such cases you should be using the base class as an   // interface, anyway. The next line calls exactly the same function.    funclist[8].bind(&c, &COtherClass::TrickyVirtualFunction);  // You can also bind directly using the constructor    MyDelegate dg(&b, &CBaseClass::SimpleVirtualFunction);    char *msg = "Looking for equal delegate";    for (int i=0; i<10; i++) {        printf("%d :", i);        // The ==, !=, <=,<,>, and >= operators are provided        // Note that they work even for inline functions.        if (funclist[i]==dg) { msg = "Found equal delegate"; };        // There are several ways to test for an empty delegate        // You can use if (funclist[i])        // or          if (!funclist.empty())        // or          if (funclist[i]!=0)        // or          if (!!funclist[i])        if (funclist[i]) {            // Invocation generates optimal assembly code.            funclist[i](i, msg);        } else {             printf("Delegate is empty\n");        };    }};

CString.hpp

#ifndef altstr__h_#define altstr__h_#include <string>#include <stdarg.h>#include <stdio.h>// This is a quick-n-dirty implementation of a CString replacement.// It promises nothing more than to provide enough functionality to get// this program to run.// Note that I have really never used CString, so the information to// implement the members that are used in this program come from// online documentation at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmfc98/html/_mfc_cstring.asp// Also, I have only used VC++ a few times, a long time ago, so// I really do not know what all their alphabet soup is suppoed to// mean, so I just made a guess...class CString{public:  enum { MAX_FORMAT_STRING_SIZE = 8 * 1044 };  CString()    : str_()  {  }  CString(      CString const & rhs)    : str_(rhs.str_)  {  }  CString(      char ch,      int repeat = 1)    : str_(repeat, ch)  {  }  CString(      char const * s)    : str_(s)  {  }  CString & operator=(      CString const & rhs)  {    str_ = rhs.str_;    return *this;  }  CString & operator=(      char const * s)  {    str_ = s;    return *this;  }  CString & operator=(      char ch)  {    str_ = ch;    return *this;  }  void Format(      char const * fmt,      ...)  {    char buffer[ MAX_FORMAT_STRING_SIZE ];    va_list ap;    va_start(ap, fmt);    vsprintf(buffer, fmt, ap);    va_end(ap);    str_ = buffer;  }  char operator[](      int x) const  {    return str_[x];  }    CString & operator+=(      CString const & x)  {    str_ += x.str_; return *this;  }  int Replace(      CString const & lhs,      CString const & rhs)  {    int rval = 0;    std::string::size_type pos = 0;    while (pos < str_.length())    {      pos = str_.find(lhs.str_, pos);      if (pos != std::string::npos)      {        str_.replace(pos, lhs.GetLength(), rhs.str_);        pos += rhs.GetLength();        ++rval;      }    }    return rval;  }  int GetLength() const  {    return str_.length();  }  operator char const * () const  {    return str_.c_str();  }private:  std::string str_;};CString operator+(    CString const & x,    CString const & y){  CString result(x);  result += y;  return result;}#endif // altstr_h_


FastDelegate.hxx

//FastDelegate.h //Efficient delegates in C++ that generate only two lines of asm code!//  Documentation is found at http://www.codeproject.com/cpp/FastDelegate.asp////- Don Clugston, Mar 2004.//Major contributions were made by Jody Hagins.// History:// 24-Apr-04 1.0  * Submitted to CodeProject. // 28-Apr-04 1.1  * Prevent most unsafe uses of evil static function hack.//  * Improved syntax for horrible_cast (thanks Paul Bludov).//  * Tested on Metrowerks MWCC and Intel ICL (IA32)//  * Compiled, but not run, on Comeau C++ and Intel Itanium ICL.//27-Jun-04 1.2 * Now works on Borland C++ Builder 5.5//  * Now works on /clr "managed C++" code on VC7, VC7.1//  * Comeau C++ now compiles without warnings.//  * Prevent the virtual inheritance case from being used on //  VC6 and earlier, which generate incorrect code.//  * Improved warning and error messages. Non-standard hacks// now have compile-time checks to make them safer.//  * implicit_cast used instead of static_cast in many cases.//  * If calling a const member function, a const class pointer can be used.//  * MakeDelegate() global helper function added to simplify pass-by-value.//  * Added fastdelegate.clear()// 16-Jul-04 1.2.1* Workaround for gcc bug (const member function pointers in templates)// 30-Oct-04 1.3  * Support for (non-void) return values.//  * No more workarounds in client code!// MSVC and Intel now use a clever hack invented by John Dlugosz://     - The FASTDELEGATEDECLARE workaround is no longer necessary.// - No more warning messages for VC6//  * Less use of macros. Error messages should be more comprehensible.//  * Added include guards//  * Added FastDelegate::empty() to test if invocation is safe (Thanks Neville Franks).//  * Now tested on VS 2005 Express Beta, PGI C++// 24-Dec-04 1.4  * Added DelegateMemento, to allow collections of disparate delegates.//                * <,>,<=,>= comparison operators to allow storage in ordered containers.//  * Substantial reduction of code size, especially the 'Closure' class.//  * Standardised all the compiler-specific workarounds.//                * MFP conversion now works for CodePlay (but not yet supported in the full code).//                * Now compiles without warnings on _any_ supported compiler, including BCC 5.5.1//  * New syntax: FastDelegate< int (char *, double) >. // 14-Feb-05 1.4.1* Now treats =0 as equivalent to .clear(), ==0 as equivalent to .empty(). (Thanks elfric).//  * Now tested on Intel ICL for AMD64, VS2005 Beta for AMD64 and Itanium.// 30-Mar-05 1.5  * Safebool idiom: "if (dg)" is now equivalent to "if (!dg.empty())"//  * Fully supported by CodePlay VectorC//                * Bugfix for Metrowerks: empty() was buggy because a valid MFP can be 0 on MWCC!//                * More optimal assignment,== and != operators for static function pointers.#ifndef FASTDELEGATE_H#define FASTDELEGATE_H#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000#include <memory.h> // to allow <,> comparisons//////////////////////////////////////////////////////////////////////////////////Configuration options//////////////////////////////////////////////////////////////////////////////////// Uncomment the following #define for optimally-sized delegates.// In this case, the generated asm code is almost identical to the code you'd get// if the compiler had native support for delegates.// It will not work on systems where sizeof(dataptr) < sizeof(codeptr). // Thus, it will not work for DOS compilers using the medium model.// It will also probably fail on some DSP systems.#define FASTDELEGATE_USESTATICFUNCTIONHACK// Uncomment the next line to allow function declarator syntax.// It is automatically enabled for those compilers where it is known to work.//#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX//////////////////////////////////////////////////////////////////////////////////Compiler identification for workarounds//////////////////////////////////////////////////////////////////////////////////// Compiler identification. It's not easy to identify Visual C++ because// many vendors fraudulently define Microsoft's identifiers.#if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(__VECTOR_C) && !defined(__ICL) && !defined(__BORLANDC__)#define FASTDLGT_ISMSVC#if (_MSC_VER <1300) // Many workarounds are required for VC6.#define FASTDLGT_VC6#pragma warning(disable:4786) // disable this ridiculous warning#endif#endif// Does the compiler uses Microsoft's member function pointer structure?// If so, it needs special treatment.// Metrowerks CodeWarrior, Intel, and CodePlay fraudulently define Microsoft's // identifier, _MSC_VER. We need to filter Metrowerks out.#if defined(_MSC_VER) && !defined(__MWERKS__)#define FASTDLGT_MICROSOFT_MFP#if !defined(__VECTOR_C)// CodePlay doesn't have the __single/multi/virtual_inheritance keywords#define FASTDLGT_HASINHERITANCE_KEYWORDS#endif#endif// Does it allow function declarator syntax? The following compilers are known to work:#if defined(FASTDLGT_ISMSVC) && (_MSC_VER >=1310) // VC 7.1#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX#endif// Gcc(2.95+), and versions of Digital Mars, Intel and Comeau in common use.#if defined (__DMC__) || defined(__GNUC__) || defined(__ICL) || defined(__COMO__)#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX#endif// It works on Metrowerks MWCC 3.2.2. From boost.Config it should work on earlier ones too.#if defined (__MWERKS__)#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX#endif#ifdef __GNUC__ // Workaround GCC bug #8271 // At present, GCC doesn't recognize constness of MFPs in templates#define FASTDELEGATE_GCC_BUG_8271#endif//////////////////////////////////////////////////////////////////////////////////General tricks used in this code//// (a) Error messages are generated by typdefing an array of negative size to//     generate compile-time errors.// (b) Warning messages on MSVC are generated by declaring unused variables, and//    enabling the "variable XXX is never used" warning.// (c) Unions are used in a few compiler-specific cases to perform illegal casts.// (d) For Microsoft and Intel, when adjusting the 'this' pointer, it's cast to//     (char *) first to ensure that the correct number of *bytes* are added.////////////////////////////////////////////////////////////////////////////////////Helper templates//////////////////////////////////////////////////////////////////////////////////namespace fastdelegate {namespace detail {// we'll hide the implementation details in a nested namespace.//implicit_cast< >// I believe this was originally going to be in the C++ standard but // was left out by accident. It's even milder than static_cast.// I use it instead of static_cast<> to emphasize that I'm not doing// anything nasty. // Usage is identical to static_cast<>template <class OutputClass, class InputClass>inline OutputClass implicit_cast(InputClass input){return input;}//horrible_cast< >// This is truly evil. It completely subverts C++'s type system, allowing you // to cast from any class to any other class. Technically, using a union // to perform the cast is undefined behaviour (even in C). But we can see if// it is OK by checking that the union is the same size as each of its members.// horrible_cast<> should only be used for compiler-specific workarounds. // Usage is identical to reinterpret_cast<>.// This union is declared outside the horrible_cast because BCC 5.5.1// can't inline a function with a nested class, and gives a warning.template <class OutputClass, class InputClass>union horrible_union{OutputClass out;InputClass in;};template <class OutputClass, class InputClass>inline OutputClass horrible_cast(const InputClass input){horrible_union<OutputClass, InputClass> u;// Cause a compile-time error if in, out and u are not the same size.// If the compile fails here, it means the compiler has peculiar// unions which would prevent the cast from working.typedef int ERROR_CantUseHorrible_cast[sizeof(InputClass)==sizeof(u) && sizeof(InputClass)==sizeof(OutputClass) ? 1 : -1];u.in = input;return u.out;}//////////////////////////////////////////////////////////////////////////////////Workarounds//////////////////////////////////////////////////////////////////////////////////// Backwards compatibility: This macro used to be necessary in the virtual inheritance// case for Intel and Microsoft. Now it just forward-declares the class.#define FASTDELEGATEDECLARE(CLASSNAME)class CLASSNAME;// Prevent use of the static function hack with the DOS medium model.#ifdef __MEDIUM__#undef FASTDELEGATE_USESTATICFUNCTIONHACK#endif//DefaultVoid - a workaround for 'void' templates in VC6.////  (1) VC6 and earlier do not allow 'void' as a default template argument.//  (2) They also doesn't allow you to return 'void' from a function.//// Workaround for (1): Declare a dummy type 'DefaultVoid' which we use//   when we'd like to use 'void'. We convert it into 'void' and back//   using the templates DefaultVoidToVoid<> and VoidToDefaultVoid<>.// Workaround for (2): On VC6, the code for calling a void function is//   identical to the code for calling a non-void function in which the//   return value is never used, provided the return value is returned//   in the EAX register, rather than on the stack. //   This is true for most fundamental types such as int, enum, void *.//   Const void * is the safest option since it doesn't participate //   in any automatic conversions. But on a 16-bit compiler it might//   cause extra code to be generated, so we disable it for all compilers//   except for VC6 (and VC5).#ifdef FASTDLGT_VC6// VC6 workaroundtypedef const void * DefaultVoid;#else// On any other compiler, just use a normal void.typedef void DefaultVoid;#endif// Translate from 'DefaultVoid' to 'void'.// Everything else is unchangedtemplate <class T>struct DefaultVoidToVoid { typedef T type; };template <>struct DefaultVoidToVoid<DefaultVoid> {typedef void type; };// Translate from 'void' into 'DefaultVoid'// Everything else is unchangedtemplate <class T>struct VoidToDefaultVoid { typedef T type; };template <>struct VoidToDefaultVoid<void> { typedef DefaultVoid type; };//////////////////////////////////////////////////////////////////////////////////Fast Delegates, part 1:////Conversion of member function pointer to a standard form//////////////////////////////////////////////////////////////////////////////////// GenericClass is a fake class, ONLY used to provide a type.// It is vitally important that it is never defined, so that the compiler doesn't// think it can optimize the invocation. For example, Borland generates simpler// code if it knows the class only uses single inheritance.// Compilers using Microsoft's structure need to be treated as a special case.#ifdef  FASTDLGT_MICROSOFT_MFP#ifdef FASTDLGT_HASINHERITANCE_KEYWORDS// For Microsoft and Intel, we want to ensure that it's the most efficient type of MFP // (4 bytes), even when the /vmg option is used. Declaring an empty class // would give 16 byte pointers in this case....class __single_inheritance GenericClass;#endif// ...but for Codeplay, an empty class *always* gives 4 byte pointers.// If compiled with the /clr option ("managed C++"), the JIT compiler thinks// it needs to load GenericClass before it can call any of its functions,// (compiles OK but crashes at runtime!), so we need to declare an // empty class to make it happy.// Codeplay and VC4 can't cope with the unknown_inheritance case either.class GenericClass {};#elseclass GenericClass;#endif// The size of a single inheritance member function pointer.const int SINGLE_MEMFUNCPTR_SIZE = sizeof(void (GenericClass::*)());//SimplifyMemFunc< >::Convert()////A template function that converts an arbitrary member function pointer into the //simplest possible form of member function pointer, using a supplied 'this' pointer.//  According to the standard, this can be done legally with reinterpret_cast<>.//For (non-standard) compilers which use member function pointers which vary in size //  depending on the class, we need to useknowledge of the internal structure of a //  member function pointer, as used by the compiler. Template specialization is used//  to distinguish between the sizes. Because some compilers don't support partial //template specialisation, I use full specialisation of a wrapper struct.// general case -- don't know how to convert it. Force a compile failuretemplate <int N>struct SimplifyMemFunc {template <class X, class XFuncType, class GenericMemFuncType>inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, GenericMemFuncType &bound_func) { // Unsupported member function type -- force a compile failure.    // (it's illegal to have a array with negative size).typedef char ERROR_Unsupported_member_function_pointer_on_this_compiler[N-100];return 0; }};// For compilers where all member func ptrs are the same size, everything goes here.// For non-standard compilers, only single_inheritance classes go here.template <>struct SimplifyMemFunc<SINGLE_MEMFUNCPTR_SIZE>  {template <class X, class XFuncType, class GenericMemFuncType>inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, GenericMemFuncType &bound_func) {#if defined __DMC__  // Digital Mars doesn't allow you to cast between abitrary PMF's, // even though the standard says you can. The 32-bit compiler lets you// static_cast through an int, but the DOS compiler doesn't.bound_func = horrible_cast<GenericMemFuncType>(function_to_bind);#else         bound_func = reinterpret_cast<GenericMemFuncType>(function_to_bind);#endif        return reinterpret_cast<GenericClass *>(pthis);}};//////////////////////////////////////////////////////////////////////////////////Fast Delegates, part 1b:////Workarounds for Microsoft and Intel//////////////////////////////////////////////////////////////////////////////////// Compilers with member function pointers which violate the standard (MSVC, Intel, Codeplay),// need to be treated as a special case.#ifdef FASTDLGT_MICROSOFT_MFP// We use unions to perform horrible_casts. I would like to use #pragma pack(push, 1)// at the start of each function for extra safety, but VC6 seems to ICE// intermittently if you do this inside a template.// __multiple_inheritance classes go here// Nasty hack for Microsoft and Intel (IA32 and Itanium)template<>struct SimplifyMemFunc< SINGLE_MEMFUNCPTR_SIZE + sizeof(int) >  {template <class X, class XFuncType, class GenericMemFuncType>inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, GenericMemFuncType &bound_func) { // We need to use a horrible_cast to do this conversion.// In MSVC, a multiple inheritance member pointer is internally defined as:        union {XFuncType func;struct { GenericMemFuncType funcaddress; // points to the actual member functionint delta;     // #BYTES to be added to the 'this' pointer}s;        } u;// Check that the horrible_cast will worktypedef int ERROR_CantUsehorrible_cast[sizeof(function_to_bind)==sizeof(u.s)? 1 : -1];        u.func = function_to_bind;bound_func = u.s.funcaddress;return reinterpret_cast<GenericClass *>(reinterpret_cast<char *>(pthis) + u.s.delta); }};// virtual inheritance is a real nuisance. It's inefficient and complicated.// On MSVC and Intel, there isn't enough information in the pointer itself to// enable conversion to a closure pointer. Earlier versions of this code didn't// work for all cases, and generated a compile-time error instead.// But a very clever hack invented by John M. Dlugosz solves this problem.// My code is somewhat different to his: I have no asm code, and I make no // assumptions about the calling convention that is used.// In VC++ and ICL, a virtual_inheritance member pointer // is internally defined as:struct MicrosoftVirtualMFP {void (GenericClass::*codeptr)(); // points to the actual member functionint delta;// #bytes to be added to the 'this' pointerint vtable_index; // or 0 if no virtual inheritance};// The CRUCIAL feature of Microsoft/Intel MFPs which we exploit is that the// m_codeptr member is *always* called, regardless of the values of the other// members. (This is *not* true for other compilers, eg GCC, which obtain the// function address from the vtable if a virtual function is being called).// Dlugosz's trick is to make the codeptr point to a probe function which// returns the 'this' pointer that was used.// Define a generic class that uses virtual inheritance.// It has a trival member function that returns the value of the 'this' pointer.struct GenericVirtualClass : virtual public GenericClass{typedef GenericVirtualClass * (GenericVirtualClass::*ProbePtrType)();GenericVirtualClass * GetThis() { return this; }};// __virtual_inheritance classes go heretemplate <>struct SimplifyMemFunc<SINGLE_MEMFUNCPTR_SIZE + 2*sizeof(int) >{template <class X, class XFuncType, class GenericMemFuncType>inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, GenericMemFuncType &bound_func) {union {XFuncType func;GenericClass* (X::*ProbeFunc)();MicrosoftVirtualMFP s;} u;u.func = function_to_bind;bound_func = reinterpret_cast<GenericMemFuncType>(u.s.codeptr);union {GenericVirtualClass::ProbePtrType virtfunc;MicrosoftVirtualMFP s;} u2;// Check that the horrible_cast<>s will worktypedef int ERROR_CantUsehorrible_cast[sizeof(function_to_bind)==sizeof(u.s)&& sizeof(function_to_bind)==sizeof(u.ProbeFunc)&& sizeof(u2.virtfunc)==sizeof(u2.s) ? 1 : -1];   // Unfortunately, taking the address of a MF prevents it from being inlined, so    // this next line can't be completely optimised away by the compiler.u2.virtfunc = &GenericVirtualClass::GetThis;u.s.codeptr = u2.s.codeptr;return (pthis->*u.ProbeFunc)();}};#if (_MSC_VER <1300)// Nasty hack for Microsoft Visual C++ 6.0// unknown_inheritance classes go here// There is a compiler bug in MSVC6 which generates incorrect code in this case!!template <>struct SimplifyMemFunc<SINGLE_MEMFUNCPTR_SIZE + 3*sizeof(int) >{template <class X, class XFuncType, class GenericMemFuncType>inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, GenericMemFuncType &bound_func) {// There is an apalling but obscure compiler bug in MSVC6 and earlier:// vtable_index and 'vtordisp' are always set to 0 in the // unknown_inheritance case!// This means that an incorrect function could be called!!!// Compiling with the /vmg option leads to potentially incorrect code.// This is probably the reason that the IDE has a user interface for specifying// the /vmg option, but it is disabled -  you can only specify /vmg on // the command line. In VC1.5 and earlier, the compiler would ICE if it ever// encountered this situation.// It is OK to use the /vmg option if /vmm or /vms is specified.// Fortunately, the wrong function is only called in very obscure cases.// It only occurs when a derived class overrides a virtual function declared // in a virtual base class, and the member function // points to the *Derived* version of that function. The problem can be// completely averted in 100% of cases by using the *Base class* for the // member fpointer. Ie, if you use the base class as an interface, you'll// stay out of trouble.// Occasionally, you might want to point directly to a derived class function// that isn't an override of a base class. In this case, both vtable_index // and 'vtordisp' are zero, but a virtual_inheritance pointer will be generated.// We can generate correct code in this case. To prevent an incorrect call from// ever being made, on MSVC6 we generate a warning, and call a function to // make the program crash instantly. typedef char ERROR_VC6CompilerBug[-100];return 0; }};#else // Nasty hack for Microsoft and Intel (IA32 and Itanium)// unknown_inheritance classes go here // This is probably the ugliest bit of code I've ever written. Look at the casts!// There is a compiler bug in MSVC6 which prevents it from using this code.template <>struct SimplifyMemFunc<SINGLE_MEMFUNCPTR_SIZE + 3*sizeof(int) >{template <class X, class XFuncType, class GenericMemFuncType>inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, GenericMemFuncType &bound_func) {// The member function pointer is 16 bytes long. We can't use a normal cast, but// we can use a union to do the conversion.union {XFuncType func;// In VC++ and ICL, an unknown_inheritance member pointer // is internally defined as:struct {GenericMemFuncType m_funcaddress; // points to the actual member functionint delta;// #bytes to be added to the 'this' pointerint vtordisp;// #bytes to add to 'this' to find the vtableint vtable_index; // or 0 if no virtual inheritance} s;} u;// Check that the horrible_cast will worktypedef int ERROR_CantUsehorrible_cast[sizeof(XFuncType)==sizeof(u.s)? 1 : -1];u.func = function_to_bind;bound_func = u.s.funcaddress;int virtual_delta = 0;if (u.s.vtable_index) { // Virtual inheritance is used// First, get to the vtable. // It is 'vtordisp' bytes from the start of the class.const int * vtable = *reinterpret_cast<const int *const*>(reinterpret_cast<const char *>(pthis) + u.s.vtordisp );// 'vtable_index' tells us where in the table we should be looking.virtual_delta = u.s.vtordisp + *reinterpret_cast<const int *>( reinterpret_cast<const char *>(vtable) + u.s.vtable_index);}// The int at 'virtual_delta' gives us the amount to add to 'this'.        // Finally we can add the three components together. Phew!        return reinterpret_cast<GenericClass *>(reinterpret_cast<char *>(pthis) + u.s.delta + virtual_delta);};};#endif // MSVC 7 and greater#endif // MS/Intel hacks}  // namespace detail//////////////////////////////////////////////////////////////////////////////////Fast Delegates, part 2:////Define the delegate storage, and cope with static functions//////////////////////////////////////////////////////////////////////////////////// DelegateMemento -- an opaque structure which can hold an arbitary delegate.// It knows nothing about the calling convention or number of arguments used by// the function pointed to.// It supplies comparison operators so that it can be stored in STL collections.// It cannot be set to anything other than null, nor invoked directly: //   it must be converted to a specific delegate.// Implementation:// There are two possible implementations: the Safe method and the Evil method.//DelegateMemento - Safe version//// This implementation is standard-compliant, but a bit tricky.// A static function pointer is stored inside the class. // Here are the valid values:// +-- Static pointer --+--pThis --+-- pMemFunc-+-- Meaning------+// |   0|  0       |   0        | Empty          |// |   !=0              |(dontcare)|  Invoker   | Static function|// |   0                |  !=0     |  !=0*      | Method call    |// +--------------------+----------+------------+----------------+//  * For Metrowerks, this can be 0. (first virtual function in a //       single_inheritance class).// When stored stored inside a specific delegate, the 'dontcare' entries are replaced// with a reference to the delegate itself. This complicates the = and == operators// for the delegate class.//DelegateMemento - Evil version//// For compilers where data pointers are at least as big as code pointers, it is // possible to store the function pointer in the this pointer, using another // horrible_cast. In this case the DelegateMemento implementation is simple:// +--pThis --+-- pMemFunc-+-- Meaning---------------------+// |    0     |  0         | Empty                         |// |  !=0     |  !=0*      | Static function or method call|// +----------+------------+-------------------------------+//  * For Metrowerks, this can be 0. (first virtual function in a //       single_inheritance class).// Note that the Sun C++ and MSVC documentation explicitly state that they // support static_cast between void * and function pointers.class DelegateMemento {protected: // the data is protected, not private, because many// compilers have problems with template friends.typedef void (detail::GenericClass::*GenericMemFuncType)(); // arbitrary MFP.detail::GenericClass *m_pthis;GenericMemFuncType m_pFunction;#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK)typedef void (*GenericFuncPtr)(); // arbitrary code pointerGenericFuncPtr m_pStaticFunction;#endifpublic:#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK)DelegateMemento() : m_pthis(0), m_pFunction(0), m_pStaticFunction(0) {};void clear() {m_pthis=0; m_pFunction=0; m_pStaticFunction=0;}#elseDelegateMemento() : m_pthis(0), m_pFunction(0) {};void clear() {m_pthis=0; m_pFunction=0;}#endifpublic:#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK)inline bool IsEqual (const DelegateMemento &x) const{    // We have to cope with the static function pointers as a special caseif (m_pFunction!=x.m_pFunction) return false;// the static function ptrs must either both be equal, or both be 0.if (m_pStaticFunction!=x.m_pStaticFunction) return false;if (m_pStaticFunction!=0) return m_pthis==x.m_pthis;else return true;}#else // Evil Methodinline bool IsEqual (const DelegateMemento &x) const{return m_pthis==x.m_pthis && m_pFunction==x.m_pFunction;}#endif// Provide a strict weak ordering for DelegateMementos.inline bool IsLess(const DelegateMemento &right) const {// deal with static function pointers first#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK)if (m_pStaticFunction !=0 || right.m_pStaticFunction!=0) return m_pStaticFunction < right.m_pStaticFunction;#endifif (m_pthis !=right.m_pthis) return m_pthis < right.m_pthis;// There are no ordering operators for member function pointers, // but we can fake one by comparing each byte. The resulting ordering is// arbitrary (and compiler-dependent), but it permits storage in ordered STL containers.return memcmp(&m_pFunction, &right.m_pFunction, sizeof(m_pFunction)) < 0;}// BUGFIX (Mar 2005):// We can't just compare m_pFunction because on Metrowerks,// m_pFunction can be zero even if the delegate is not empty!inline bool operator ! () const// Is it bound to anything?{ return m_pthis==0 && m_pFunction==0; }inline bool empty() const// Is it bound to anything?{ return m_pthis==0 && m_pFunction==0; }public:DelegateMemento & operator = (const DelegateMemento &right)  {SetMementoFrom(right); return *this;}inline bool operator <(const DelegateMemento &right) {return IsLess(right);}inline bool operator >(const DelegateMemento &right) {return right.IsLess(*this);}DelegateMemento (const DelegateMemento &right)  : m_pFunction(right.m_pFunction), m_pthis(right.m_pthis)#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK), m_pStaticFunction (right.m_pStaticFunction)#endif{}protected:void SetMementoFrom(const DelegateMemento &right)  {m_pFunction = right.m_pFunction;m_pthis = right.m_pthis;#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK)m_pStaticFunction = right.m_pStaticFunction;#endif}};//ClosurePtr<>//// A private wrapper class that adds function signatures to DelegateMemento.// It's the class that does most of the actual work.// The signatures are specified by:// GenericMemFunc: must be a type of GenericClass member function pointer. // StaticFuncPtr:  must be a type of function pointer with the same signature //                 as GenericMemFunc.// UnvoidStaticFuncPtr: is the same as StaticFuncPtr, except on VC6//                 where it never returns void (returns DefaultVoid instead).// An outer class, FastDelegateN<>, handles the invoking and creates the// necessary typedefs.// This class does everything else.namespace detail {template < class GenericMemFunc, class StaticFuncPtr, class UnvoidStaticFuncPtr>class ClosurePtr : public DelegateMemento {public:// These functions are for setting the delegate to a member function.// Here's the clever bit: we convert an arbitrary member function into a // standard form. XMemFunc should be a member function of class X, but I can't // enforce that here. It needs to be enforced by the wrapper class.template < class X, class XMemFunc >inline void bindmemfunc(X *pthis, XMemFunc function_to_bind ) {m_pthis = SimplifyMemFunc< sizeof(function_to_bind) >::Convert(pthis, function_to_bind, m_pFunction);#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK)m_pStaticFunction = 0;#endif}// For const member functions, we only need a const class pointer.// Since we know that the member function is const, it's safe to // remove the const qualifier from the 'this' pointer with a const_cast.// VC6 has problems if we just overload 'bindmemfunc', so we give it a different name.template < class X, class XMemFunc>inline void bindconstmemfunc(const X *pthis, XMemFunc function_to_bind) {m_pthis= SimplifyMemFunc< sizeof(function_to_bind) >::Convert(const_cast<X*>(pthis), function_to_bind, m_pFunction);#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK)m_pStaticFunction = 0;#endif}#ifdef FASTDELEGATE_GCC_BUG_8271// At present, GCC doesn't recognize constness of MFPs in templatestemplate < class X, class XMemFunc>inline void bindmemfunc(const X *pthis, XMemFunc function_to_bind) {bindconstmemfunc(pthis, function_to_bind);#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK)m_pStaticFunction = 0;#endif}#endif// These functions are required for invoking the stored functioninline GenericClass *GetClosureThis() const { return m_pthis; }inline GenericMemFunc GetClosureMemPtr() const { return reinterpret_cast<GenericMemFunc>(m_pFunction); }// There are a few ways of dealing with static function pointers.// There's a standard-compliant, but tricky method.// There's also a straightforward hack, that won't work on DOS compilers using the// medium memory model. It's so evil that I can't recommend it, but I've// implemented it anyway because it produces very nice asm code.#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK)//ClosurePtr<> - Safe version//// This implementation is standard-compliant, but a bit tricky.// I store the function pointer inside the class, and the delegate then// points to itself. Whenever the delegate is copied, these self-references// must be transformed, and this complicates the = and == operators.public:// The next two functions are for operator ==, =, and the copy constructor.// We may need to convert the m_pthis pointers, so that// they remain as self-references.template< class DerivedClass >inline void CopyFrom (DerivedClass *pParent, const DelegateMemento &x) {SetMementoFrom(x);if (m_pStaticFunction!=0) {// transform self references...m_pthis=reinterpret_cast<GenericClass *>(pParent);}}// For static functions, the 'static_function_invoker' class in the parent // will be called. The parent then needs to call GetStaticFunction() to find out // the actual function to invoke.template < class DerivedClass, class ParentInvokerSig >inline void bindstaticfunc(DerivedClass *pParent, ParentInvokerSig static_function_invoker, StaticFuncPtr function_to_bind ) {if (function_to_bind==0) { // cope with assignment to 0m_pFunction=0;} else { bindmemfunc(pParent, static_function_invoker);        }m_pStaticFunction=reinterpret_cast<GenericFuncPtr>(function_to_bind);}inline UnvoidStaticFuncPtr GetStaticFunction() const { return reinterpret_cast<UnvoidStaticFuncPtr>(m_pStaticFunction); }#else//ClosurePtr<> - Evil version//// For compilers where data pointers are at least as big as code pointers, it is // possible to store the function pointer in the this pointer, using another // horrible_cast. Invocation isn't any faster, but it saves 4 bytes, and// speeds up comparison and assignment. If C++ provided direct language support// for delegates, they would produce asm code that was almost identical to this.// Note that the Sun C++ and MSVC documentation explicitly state that they // support static_cast between void * and function pointers.template< class DerivedClass >inline void CopyFrom (DerivedClass *pParent, const DelegateMemento &right) {SetMementoFrom(right);}// For static functions, the 'static_function_invoker' class in the parent // will be called. The parent then needs to call GetStaticFunction() to find out // the actual function to invoke.// ******** EVIL, EVIL CODE! *******template < class DerivedClass, class ParentInvokerSig>inline void bindstaticfunc(DerivedClass *pParent, ParentInvokerSig static_function_invoker, StaticFuncPtr function_to_bind) {if (function_to_bind==0) { // cope with assignment to 0m_pFunction=0;} else {    // We'll be ignoring the 'this' pointer, but we need to make sure we pass   // a valid value to bindmemfunc().bindmemfunc(pParent, static_function_invoker);        }// WARNING! Evil hack. We store the function in the 'this' pointer!// Ensure that there's a compilation failure if function pointers // and data pointers have different sizes.// If you get this error, you need to #undef FASTDELEGATE_USESTATICFUNCTIONHACK.typedef int ERROR_CantUseEvilMethod[sizeof(GenericClass *)==sizeof(function_to_bind) ? 1 : -1];m_pthis = horrible_cast<GenericClass *>(function_to_bind);// MSVC, SunC++ and DMC accept the following (non-standard) code://m_pthis = static_cast<GenericClass *>(static_cast<void *>(function_to_bind));// BCC32, Comeau and DMC accept this method. MSVC7.1 needs __int64 instead of long//m_pthis = reinterpret_cast<GenericClass *>(reinterpret_cast<long>(function_to_bind));}// ******** EVIL, EVIL CODE! *******// This function will be called with an invalid 'this' pointer!!// We're just returning the 'this' pointer, converted into// a function pointer!inline UnvoidStaticFuncPtr GetStaticFunction() const {// Ensure that there's a compilation failure if function pointers // and data pointers have different sizes.// If you get this error, you need to #undef FASTDELEGATE_USESTATICFUNCTIONHACK.typedef int ERROR_CantUseEvilMethod[sizeof(UnvoidStaticFuncPtr)==sizeof(this) ? 1 : -1];return horrible_cast<UnvoidStaticFuncPtr>(this);}#endif // !defined(FASTDELEGATE_USESTATICFUNCTIONHACK)// Does the closure contain this static function?inline bool IsEqualToStaticFuncPtr(StaticFuncPtr funcptr){if (funcptr==0) return empty(); // For the Evil method, if it doesn't actually contain a static function, this will return an arbitrary// value that is not equal to any valid function pointer.else return funcptr==reinterpret_cast<StaticFuncPtr>(GetStaticFunction());}};} // namespace detail//////////////////////////////////////////////////////////////////////////////////Fast Delegates, part 3:////Wrapper classes to ensure type safety//////////////////////////////////////////////////////////////////////////////////// Once we have the member function conversion templates, it's easy to make the// wrapper classes. So that they will work with as many compilers as possible, // the classes are of the form//   FastDelegate3<int, char *, double>// They can cope with any combination of parameters. The max number of parameters// allowed is 8, but it is trivial to increase this limit.// Note that we need to treat const member functions seperately.// All this class does is to enforce type safety, and invoke the delegate with// the correct list of parameters.// Because of the weird rule about the class of derived member function pointers,// you sometimes need to apply a downcast to the 'this' pointer.// This is the reason for the use of "implicit_cast<X*>(pthis)" in the code below. // If CDerivedClass is derived from CBaseClass, but doesn't override SimpleVirtualFunction,// without this trick you'd need to write://MyDelegate(static_cast<CBaseClass *>(&d), &CDerivedClass::SimpleVirtualFunction);// but with the trick you can write//MyDelegate(&d, &CDerivedClass::SimpleVirtualFunction);// RetType is the type the compiler uses in compiling the template. For VC6,// it cannot be void. DesiredRetType is the real type which is returned from// all of the functions. It can be void.// Implicit conversion to "bool" is achieved using the safe_bool idiom,// using member data pointers (MDP). This allows "if (dg)..." syntax// Because some compilers (eg codeplay) don't have a unique value for a zero// MDP, an extra padding member is added to the SafeBool struct.// Some compilers (eg VC6) won't implicitly convert from 0 to an MDP, so// in that case the static function constructor is not made explicit; this// allows "if (dg==0) ..." to compile.@VARARGStemplate<@CLASSARGS, class RetType=detail::DefaultVoid>class FastDelegate@NUM {private:typedef typename detail::DefaultVoidToVoid<RetType>::type DesiredRetType;typedef DesiredRetType (*StaticFunctionPtr)(@FUNCARGS);typedef RetType (*UnvoidStaticFunctionPtr)(@FUNCARGS);typedef RetType (detail::GenericClass::*GenericMemFn)(@FUNCARGS);typedef detail::ClosurePtr<GenericMemFn, StaticFunctionPtr, UnvoidStaticFunctionPtr> ClosureType;ClosureType m_Closure;public:// Typedefs to aid generic programmingtypedef FastDelegate@NUM type;// Construction and comparison functionsFastDelegate@NUM() { clear(); }FastDelegate@NUM(const FastDelegate@NUM &x) {m_Closure.CopyFrom(this, x.m_Closure); }void operator = (const FastDelegate@NUM &x)  {m_Closure.CopyFrom(this, x.m_Closure); }bool operator ==(const FastDelegate@NUM &x) const {return m_Closure.IsEqual(x.m_Closure);}bool operator !=(const FastDelegate@NUM &x) const {return !m_Closure.IsEqual(x.m_Closure); }bool operator <(const FastDelegate@NUM &x) const {return m_Closure.IsLess(x.m_Closure);}bool operator >(const FastDelegate@NUM &x) const {return x.m_Closure.IsLess(m_Closure);}// Binding to non-const member functionstemplate < class X, class Y >FastDelegate@NUM(Y *pthis, DesiredRetType (X::* function_to_bind)(@FUNCARGS) ) {m_Closure.bindmemfunc(detail::implicit_cast<X*>(pthis), function_to_bind); }template < class X, class Y >inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(@FUNCARGS)) {m_Closure.bindmemfunc(detail::implicit_cast<X*>(pthis), function_to_bind);}// Binding to const member functions.template < class X, class Y >FastDelegate@NUM(const Y *pthis, DesiredRetType (X::* function_to_bind)(@FUNCARGS) const) {m_Closure.bindconstmemfunc(detail::implicit_cast<const X*>(pthis), function_to_bind);}template < class X, class Y >inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(@FUNCARGS) const) {m_Closure.bindconstmemfunc(detail::implicit_cast<const X *>(pthis), function_to_bind);}// Static functions. We convert them into a member function call.// This constructor also provides implicit conversionFastDelegate@NUM(DesiredRetType (*function_to_bind)(@FUNCARGS) ) {bind(function_to_bind);}// for efficiency, prevent creation of a temporaryvoid operator = (DesiredRetType (*function_to_bind)(@FUNCARGS) ) {bind(function_to_bind);}inline void bind(DesiredRetType (*function_to_bind)(@FUNCARGS)) {m_Closure.bindstaticfunc(this, &FastDelegate@NUM::InvokeStaticFunction, function_to_bind); }// Invoke the delegateRetType operator() (@FUNCARGS) const {return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(@INVOKEARGS); }// Implicit conversion to "bool" using the safe_bool idiomprivate:typedef struct SafeBoolStruct {int a_data_pointer_to_this_is_0_on_buggy_compilers;StaticFunctionPtr m_nonzero;} UselessTypedef;    typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type;public:operator unspecified_bool_type() const {        return empty()? 0: &SafeBoolStruct::m_nonzero;    }// necessary to allow ==0 to work despite the safe_bool idiominline bool operator==(StaticFunctionPtr funcptr) {return m_Closure.IsEqualToStaticFuncPtr(funcptr);}inline bool operator!=(StaticFunctionPtr funcptr) { return !m_Closure.IsEqualToStaticFuncPtr(funcptr);    }inline bool operator ! () const{// Is it bound to anything?return !m_Closure; }inline bool empty() const{return !m_Closure; }void clear() { m_Closure.clear();}// Conversion to and from the DelegateMemento storage classconst DelegateMemento & GetMemento() { return m_Closure; }void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); }private:// Invoker for static functionsRetType InvokeStaticFunction(@FUNCARGS) const {return (*(m_Closure.GetStaticFunction()))(@INVOKEARGS); }};@ENDVAR//////////////////////////////////////////////////////////////////////////////////Fast Delegates, part 4:// //FastDelegate<> class (Original author: Jody Hagins)//Allows boost::function style syntax like://FastDelegate< double (int, long) >// instead of://FastDelegate2< int, long, double >//////////////////////////////////////////////////////////////////////////////////#ifdef FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX// Declare FastDelegate as a class template.  It will be specialized// later for all number of arguments.template <typename Signature>class FastDelegate;@VARARGS// Specialization to allow use of// FastDelegate< R ( @SELARGS ) >// instead of // FastDelegate@NUM < @SELARGS, R >template<typename R, @CLASSARGS>class FastDelegate< R ( @SELARGS ) >  // Inherit from FastDelegate@NUM so that it can be treated just like a FastDelegate@NUM  : public FastDelegate@NUM < @SELARGS, R >{public:  // Make using the base type a bit easier via typedef.  typedef FastDelegate@NUM < @SELARGS, R > BaseType;  // Allow users access to the specific type of this delegate.  typedef FastDelegate SelfType;  // Mimic the base class constructors.  FastDelegate() : BaseType() { }  template < class X, class Y >  FastDelegate(Y * pthis,     R (X::* function_to_bind)( @FUNCARGS ))    : BaseType(pthis, function_to_bind)  { }  template < class X, class Y >  FastDelegate(const Y *pthis,      R (X::* function_to_bind)( @FUNCARGS ) const)    : BaseType(pthis, function_to_bind)  {  }  FastDelegate(R (*function_to_bind)( @FUNCARGS ))    : BaseType(function_to_bind)  { }  void operator = (const BaseType &x)  {  *static_cast<BaseType*>(this) = x; }};@ENDVAR#endif //FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX//////////////////////////////////////////////////////////////////////////////////Fast Delegates, part 5:////MakeDelegate() helper function////MakeDelegate(&x, &X::func) returns a fastdelegate of the type//necessary for calling x.func() with the correct number of arguments.//This makes it possible to eliminate many typedefs from user code.//////////////////////////////////////////////////////////////////////////////////// Also declare overloads of a MakeDelegate() global function to // reduce the need for typedefs.// We need seperate overloads for const and non-const member functions.// Also, because of the weird rule about the class of derived member function pointers,// implicit downcasts may need to be applied later to the 'this' pointer.// That's why two classes (X and Y) appear in the definitions. Y must be implicitly// castable to X.// Workaround for VC6. VC6 needs void return types converted into DefaultVoid.// GCC 3.2 and later won't compile this unless it's preceded by 'typename',// but VC6 doesn't allow 'typename' in this context.// So, I have to use a macro.#ifdef FASTDLGT_VC6#define FASTDLGT_RETTYPE detail::VoidToDefaultVoid<RetType>::type#else #define FASTDLGT_RETTYPE RetType#endif@VARARGStemplate <class X, class Y, @CLASSARGS, class RetType>FastDelegate@NUM<@SELARGS, FASTDLGT_RETTYPE> MakeDelegate(Y* x, RetType (X::*func)(@FUNCARGS)) { return FastDelegate@NUM<@SELARGS, FASTDLGT_RETTYPE>(x, func);}template <class X, class Y, @CLASSARGS, class RetType>FastDelegate@NUM<@SELARGS, FASTDLGT_RETTYPE> MakeDelegate(Y* x, RetType (X::*func)(@FUNCARGS) const) { return FastDelegate@NUM<@SELARGS, FASTDLGT_RETTYPE>(x, func);}@ENDVAR // clean up after ourselves...#undef FASTDLGT_RETTYPE} // namespace fastdelegate#endif // !defined(FASTDELEGATE_H)



FastDelegateBind.hxx

//FastDelegateBind.h //  Helper file for FastDelegates. Provides bind() function, enabling//  FastDelegates to be rapidly compared to programs using boost::function and boost::bind.////  Documentation is found at http://www.codeproject.com/cpp/FastDelegate.asp////Original author: Jody Hagins.// Minor changes by Don Clugston.//// Warning: The arguments to 'bind' are ignored! No actual binding is performed.// The behaviour is equivalent to boost::bind only when the basic placeholder // arguments _1, _2, _3, etc are used in order.//// HISTORY://1.4 Dec 2004. Initial release as part of FastDelegate 1.4.#ifndef FASTDELEGATEBIND_H#define FASTDELEGATEBIND_H#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000//////////////////////////////////////////////////////////////////////////////////FastDelegate bind()////bind() helper function for boost compatibility.//(Original author: Jody Hagins).//// Add another helper, so FastDelegate can be a dropin replacement// for boost::bind (in a fair number of cases).// Note the elipses, because boost::bind() takes place holders// but FastDelegate does not care about them.  Getting the place holder// mechanism to work, and play well with boost is a bit tricky, so// we do the "easy" thing...// Assume we have the following code...//      using boost::bind;//      bind(&Foo:func, &foo, _1, _2);// we should be able to replace the "using" with...//      using fastdelegate::bind;// and everything should work fine...////////////////////////////////////////////////////////////////////////////////#ifdef FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAXnamespace fastdelegate {@VARARGStemplate <class X, class Y, class RetType, @CLASSARGS>FastDelegate< RetType ( @FUNCARGS ) >bind(    RetType (X::*func)( @FUNCARGS ),    Y * y,    ...){   return FastDelegate< RetType ( @FUNCARGS ) >(y, func);}template <class X, class Y, class RetType, @CLASSARGS>FastDelegate< RetType ( @FUNCARGS ) >bind(    RetType (X::*func)( @FUNCARGS ) const,    Y * y,    ...){   return FastDelegate< RetType ( @FUNCARGS ) >(y, func);}@ENDVAR#endif //FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX} // namespace fastdelegate#endif // !defined(FASTDELEGATEBIND_H)



Hopter.cpp

// HOPTER.EXE -- A simple header generator for vararg templates/*  MOTIVATION C++ doesn't have a vararg feature for templates. This can be a nuisance when writing class libraries. There are a few options: (a) manually repeat code. This is tedious and error prone. (b) Use macros. Messy, can't cope with template   arguments, and users get hard-to-interpret error messages. (c) Use the boost preprocessor library. This slows compilation,   and introduces a dependency on boost. (d) Write a program to automatically generate the code.   I've never seen a general program to do this job, so this is    a very simple command-line program to do it. Why is it called Hopter? I wanted a word that started with h, to be reminiscent of header files. At the time, my 2yo son kept talking about 'Hopter Copters' (he couldn't pronounce 'helicopter'). It also reflects the level of sophistication of this program -- it was named by a two year old. IMPLEMENTATION When you analyze the problem, you find that the requirements are very simple. Varargs are needed in two places: templates and functions. You want to use them in two ways: when declaring the template or function, and when invoking the function / selecting the  template. It's a brain-dead implementation, which just does a search-and-replace of specific strings. It could be done with a few lines using sed on a *NIX platform. Because I use Windows, I've done a quick and dirty implementation using CString.In a file, @VARARGS at the start of a line indicates the start of the codeto be expanded. @ENDVAR at the start of a line marks the end of the expansion. declare template  --  template <@CLASSARGS> class SomeClass template<class Param1, class Param2> class SomeClass declare function  -- somefunc(@FUNCARGS) somefunc(Param1 p1, Param2 p2) select template -- SomeClass<@SELARGS> SomeClass<Param1, Param2> invoke function -- somefunc(@INVOKEARGS) somefunc(p1, p2) I also provide @NUM which is the number of arguments. The case where @NUM is zero is a special case: for template declarations, the entire template<> bit needs to disappear. When other arguments are  involved, either at the beginning or end of the list, there a commas  which might need to be removed. I've used .hxx as the extension for these files. This enables C++  I wanted to use an unusual file extension (.HOH) for these files, because they aren't real header files. But I also like to use the C++ syntax highlighting  in Visual Studio. I chose .hxx because MSVC treats it as C++, but it  seems to very rarely used. Installation (for VC6, if you want to use .HOH as the extension instead): Go into the registry and change  HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Text Editor\Tabs/Language Settings\C/C++\FileExtensions from cpp;cxx;c;h;hxx;hpp;inl;tlh;tli;rc;rc2  to cpp;cxx;c;h;hxx;hpp;inl;tlh;tli;rc;rc2;hoh by adding ?hoh?to the end. Then add this as a custom build step to the main file of your project: hopter $(InputDir)\*.hoh $(InputDir)\*.h*/#if defined(_MSC_VER)#  if (_MSC_VER <1300)#    include <afx.h>#  else#    include "atlstr.h"#  endif#else#  include "CString.hpp"#endif/* Example: This is an excerpt from boost::function_template.hpp#define BOOST_FUNCTION_FUNCTION_INVOKER \  BOOST_JOIN(function_invoker,BOOST_FUNCTION_NUM_ARGS)#define BOOST_FUNCTION_VOID_FUNCTION_INVOKER \  BOOST_JOIN(void_function_invoker,BOOST_FUNCTION_NUM_ARGS)      template<        typename FunctionPtr,        typename R BOOST_FUNCTION_COMMA        BOOST_FUNCTION_TEMPLATE_PARMS      >      struct BOOST_FUNCTION_GET_FUNCTION_INVOKER      {        typedef typename ct_if<(is_void<R>::value),                            BOOST_FUNCTION_VOID_FUNCTION_INVOKER<                            FunctionPtr,                            R BOOST_FUNCTION_COMMA                            BOOST_FUNCTION_TEMPLATE_ARGS                          >,                          BOOST_FUNCTION_FUNCTION_INVOKER<                            FunctionPtr,                            R BOOST_FUNCTION_COMMA                            BOOST_FUNCTION_TEMPLATE_ARGS                          >                       >::type type;      };Using HOPTER, this can be written as:    template< typename FunctionPtr, typename R, @CLASSARGS>      struct get_function_invoker@NUM  {        typedef typename ct_if<(is_void<R>::value),void_function_invoker@NUM<FunctionPtr, R, @SELARGS>,function_invoker@NUM<FunctionPtr,R, @SELARGS>                       >::type type;      };*/const int MaxNumArgs=8;void showusage(){printf("Usage: HOPTER infile.hxx outfile.h\n Reads infile.hxx, creates outfile.h\n");}/// Makes the necessary subsititutions in 'bigblock', and writes to fout.void PrintVarArgs(FILE *fout, CString bigblock, int num) {CString numstr;CString invokelist;CString funclist;CString selectlist;CString classlist;CString commastr;numstr.Format("%d", num);if (num==0) {invokelist="";funclist="";selectlist="";commastr="";classlist="";}else {invokelist="p1";funclist="Param1 p1";selectlist = "Param1";classlist = "class Param1";commastr=", ";CString str;for (int k=2; k<=num; k++) {str.Format(", p%d", k);invokelist+=str;str.Format(", Param%d p%d", k, k);funclist+=str;str.Format(", Param%d", k);selectlist += str;str.Format(", class Param%d", k);classlist += str;}}// Simple replacement of number of argumentsbigblock.Replace("@NUM", numstr);// Template declarationsif (num==0) bigblock.Replace("template<@CLASSARGS>", "");else bigblock.Replace("template<@CLASSARGS>", (CString)"template<" + classlist+">");if (num==0) bigblock.Replace("template <@CLASSARGS>", "");else bigblock.Replace("template <@CLASSARGS>", (CString)"template<" + classlist+">");bigblock.Replace(",@CLASSARGS", commastr + classlist);bigblock.Replace(", @CLASSARGS", commastr + classlist);bigblock.Replace("@CLASSARGS, ", classlist + commastr);bigblock.Replace("@CLASSARGS,", classlist + commastr);bigblock.Replace("@CLASSARGS", classlist);// Template selectionsCString selargstr;if (num==0) selargstr = "";else selargstr = (CString)"<"+selectlist+">";bigblock.Replace("<@SELARGS>", selargstr);bigblock.Replace("< @SELARGS >", selargstr);bigblock.Replace(",@SELARGS", commastr + selectlist);bigblock.Replace(", @SELARGS", commastr + selectlist);bigblock.Replace("@SELARGS, ", selectlist + commastr);bigblock.Replace("@SELARGS,", selectlist + commastr);bigblock.Replace("@SELARGS", selectlist);// Function declarationsbigblock.Replace(",@FUNCARGS", commastr + funclist);bigblock.Replace(", @FUNCARGS", commastr + funclist);bigblock.Replace("@FUNCARGS, ", funclist + commastr);bigblock.Replace("@FUNCARGS,", funclist + commastr);bigblock.Replace("@FUNCARGS", funclist);// Function invocationbigblock.Replace(",@INVOKEARGS", commastr + invokelist);bigblock.Replace(", @INVOKEARGS", commastr + invokelist);bigblock.Replace("@INVOKEARGS, ", invokelist + commastr);bigblock.Replace("@INVOKEARGS,", invokelist + commastr);bigblock.Replace("@INVOKEARGS", invokelist);fprintf(fout, bigblock);}int main(int argc, char* argv[]){if (argc<3) { showusage(); return 2; };FILE * fin;FILE *fout;//CString infilename;//infilename=argv[1];fin =fopen(argv[1],"rt");if (!fin) { printf("Error, can't open %s\n", argv[1]); return 1; };fout = fopen (argv[2], "wt");if (!fout) { printf("Error, can't open %s\n", argv[2]); return 1; };char buf[5000];for (;;) { if (!fgets(buf, 5000, fin)) break;if (!strncmp(buf, "@VARARGS", 8)) {// found a match...CString bigblock;for (;;) {if (feof(fin)) { printf("No matching @ENDVAR !!??\n"); return 1; };fgets(buf, 5000, fin);if (!strncmp(buf, "@ENDVAR", 7)) break;bigblock+=buf;}//fprintf(fout, "// Auto-generated code [[[\n");for (int k=0; k<=MaxNumArgs; k++) {fprintf(fout, "//N=%d\n", k);PrintVarArgs(fout, bigblock, k);}//fprintf(fout, "// ]]] End auto-generated code\n");} else {fprintf(fout, "%s", buf);}}fclose(fin);fclose(fout);return 0;}



memfuntest.cpp

class SomeClass;typedef void (SomeClass::*SomePtr)(void);static SomePtr ptr;static SomeClass *pClass;void Invoker(){        (pClass->*ptr)();}class X{public:          void   somefunc();};typedef void (X::*XPtr)(void);X* pX;XPtr Xptr;void XInvoke(){        (pX->*Xptr)();}


0 0
原创粉丝点击