在特定对象实例上调用C ++函数指针

时间:2020-03-06 14:54:14  来源:igfitidea点击:

我有一个函数指针,定义为:

typedef void (*EventFunction)(int nEvent);

有没有一种方法可以使用C ++对象的特定实例来处理该功能?

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne() { handler(1); }
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(EventFromA); }  // What do I do here?

    void EventFromA(int nEvent) { // do stuff }
};

编辑:Orion指出了Boost提供的选项,例如:

boost::function<int (int)> f;
X x;
f = std::bind1st(
      std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)

不幸的是,Boost不是我的选择。是否可以使用C ++编写某种"可弯曲"函数,以将指向成员函数的指针包装到普通函数指针中?

解决方案

不幸的是,EventFunction类型不能指向B的函数,因为它不是正确的类型。我们可以将其设置为正确的类型,但这可能并不是我们真正想要的解决方案:

typedef void(* B :: EventFunction)(int nEvent);`

...然后,一旦我们以B的对象调用回调,一切都将工作。但是我们可能希望能够在B做其他事情的类中调用B之外的函数。这就是回调的重点。但是现在,这种类型肯定指向B中的某种东西。更有吸引力的解决方案是:

  • 使B为基类,然后为可能会调用的每个其他类覆盖一个虚函数。然后,A存储指向B的指针,而不是函数指针。干净得多。
  • 如果我们不想将函数绑定到特定的类类型,甚至不希望绑定到基类(我也不会怪我们),那么我建议我们制作一个称为静态函数的函数:"static void EventFrom A (int nEvent);"。然后,我们可以直接调用它,而无需B的对象。但是我们可能希望它调用B的特定实例(除非B是单例)。
  • 因此,如果我们希望能够调用B的特定实例,但也能够调用非B的实例,则需要将其他内容传递给回调函数,以便回调函数可以调用正确的对象。如上所述,使函数为静态函数,并添加一个void *参数,该参数将成为指向B的指针。

在实践中,我们看到此问题的两种解决方案:在其中传递void *和事件的临时系统,以及在基类中具有虚拟功能的层次结构,例如窗口系统

我们可能会发现Marshall Cline撰写的C ++常见问题解答对我们要完成的工作很有帮助。

了解有关成员的指针。
要在派生类上调用方法,必须在基类中将该方法声明为virtual,并在基类中将其重写,并且指针应指向基类方法。有关虚拟成员的指针的更多信息。

目前尚不清楚我们要在这里完成什么。清楚的是函数指针不是方法。

也许我们正在寻找的是方法的指针。

摆脱原始的C ++函数指针,而改用std :: function

如果我们使用的是不支持C ++ 11的旧版编译器(例如Visual Studio 2008),则可以使用boost :: function
" boost:function"和" std :: function"是同一件事,它们将相当多的增强内容引入了C ++ 11的std库中。

注意:我们可能需要阅读boost函数文档而不是Microsoft的文档,因为它更容易理解

我们可以使用函数指针来索引给定对象实例的vtable。这称为成员函数指针。我们需要更改语法以使用"。*"和"&::"运算符:

class A;
class B;
typedef void (B::*EventFunction)(int nEvent)

接着:

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne(B* delegate) { ((*delegate).*handler)(1); } // note: ".*"
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(&B::EventFromA); } // note: "&::"

    void EventFromA(int nEvent) { /* do stuff */ }
};

对于我在c ++框架中使用的确切内容,我有一组类。

http://code.google.com/p/kgui/source/browse/trunk/kgui.h

我如何处理每个可用作回调的类函数,都需要一个将对象类型绑定到它的静态函数。我有一组自动执行的宏。它使静态函数具有相同的名称,但带有前缀" CB_"和一个额外的第一个参数(即类对象指针)。

检出类类型kGUICallBack及其各种模板版本,以处理不同的参数组合。

#define CALLBACKGLUE(classname , func) static void CB_ ## func(void *obj) {static_cast< classname *>(obj)->func();}
#define CALLBACKGLUEPTR(classname , func, type) static void CB_ ## func(void *obj,type *name) {static_cast< classname *>(obj)->func(name);}
#define CALLBACKGLUEPTRPTR(classname , func, type,type2) static void CB_ ## func(void *obj,type *name,type2 *name2) {static_cast< classname *>(obj)->func(name,name2);}
#define CALLBACKGLUEPTRPTRPTR(classname , func, type,type2,type3) static void CB_ ## func(void *obj,type *name,type2 *name2,type3 *name3) {static_cast< classname *>(obj)->func(name,name2,name3);}
#define CALLBACKGLUEVAL(classname , func, type) static void CB_ ## func(void *obj,type val) {static_cast< classname *>(obj)->func(val);}

如果要与C库进行接口连接,则不能使用类成员函数而不使用boost :: bind之类的东西。大多数使用回调函数的C库通常还允许我们传递我们选择的额外参数(通常为void *类型),我们可以使用该参数来引导类,如下所示:

class C
{
public:
  int Method1(void) { return 3; }
  int Method2(void) { return x; }

  int x;
};

// This structure will hold a thunk to
struct CCallback
{
  C *obj;  // Instance to callback on
  int (C::*callback)(void);  // Class callback method, taking no arguments and returning int
};

int CBootstrapper(CCallback *pThunk)
{
  // Call the thunk
  return ((pThunk->obj) ->* (pThunk->callback))( /* args go here */ );
}

void DoIt(C *obj, int (C::*callback)(void))
{
  // foobar() is some C library function that takes a function which takes no arguments and returns int, and it also takes a void*, and we can't change it
  struct CCallback thunk = {obj, callback};
  foobar(&CBootstrapper, &thunk);
}

int main(void)
{
  C c;
  DoIt(&c, &C::Method1);  // Essentially calls foobar() with a callback of C::Method1 on c
  DoIt(&c, &C::Method2);  // Ditto for C::Method2
}

我强烈推荐Don Clugston出色的FastDelegate库。它提供了真正委托所需要的所有内容,并且在大多数情况下可以编译成一些ASM指令。随附的文章也很好地了解了成员函数指针。

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

我们提到增强不是选择,但是我们有可用的TR1吗?

TR1提供基于boost库的function,bind和mem_fn对象,我们可能已经将其与编译器捆绑在一起。它不是标准的,但是我最近使用的至少两个编译器都具有它。

http://en.wikipedia.org/wiki/Technical_Report_1
http://msdn.microsoft.com/en-us/library/bb982702.aspx