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

时间:2008-09-30 01:29:47

标签: c++ pointers function

我有一个由:

定义的函数指针
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对我来说不是一个选择。是否有某种“currying”函数可以用C ++编写,它会将指向成员函数的指针包装到普通函数指针中?

10 个答案:

答案 0 :(得分:11)

远离原始C ++函数指针,改为使用std::function

如果您使用的是旧版编译器,例如不支持C ++ 11的visual studio 2008,则可以使用boost::function
boost:functionstd::function是相同的东西 - 他们在C ++ 11的std库中提取了相当多的提升内容。

注意:您可能希望阅读boost函数文档而不是microsoft文档,因为它更容易理解

答案 1 :(得分:11)

您可以使用函数指针索引到给定对象实例的vtable。这称为member function pointer。您的语法需要更改为使用“。*”和“&amp; ::”运算符:

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 */ }
};

答案 2 :(得分:9)

我强烈推荐Don Clugston出色的FastDelegate库。它提供了您对真实代表所期望的所有内容,并在大多数情况下编译为一些ASM指令。随附的文章也很好地阅读了成员函数指针。

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

答案 3 :(得分:4)

您可能会发现C++ FAQ by Marshall Cline对您要完成的任务有所帮助。

答案 4 :(得分:2)

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

答案 5 :(得分:2)

如果您正在与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
}

答案 6 :(得分:1)

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

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

...然后一旦你用B的obhect调用回调,一切都能正常工作。但是你可能希望能够在其他做其他事情的类中调用B之外的函数。这有点像回调。但是现在这种类型在B中肯定存在一些东西。更有吸引力的解决方案是:

  • 使B成为基类,然后为可能被调用的每个其他类重写虚函数。然后A存储指向B的指针而不是函数指针。更清洁。
  • 如果你不想将函数绑定到特定的类类型,甚至是基类(我不会责怪你),那么我建议你创建一个被称为静态函数的函数:“{{ 1}}”。然后你可以直接调用它,而不需要B的对象。但你可能希望它调用B的特定实例(除非B是单例)。
  • 因此,如果您希望能够调用B的特定实例,但也能够调用非B,那么您需要将其他内容传递给回调函数,以便回调函数可以调用正确的对象。如上所述,使您的函数成为静态函数,并添加一个void *参数,您将指向B。

在实践中,您会看到两个解决此问题的方法:ad hoc系统,其中传递void *和事件,以及带有虚函数的层次结构,如基类,如窗口系统

答案 7 :(得分:1)

你提到升力不适合你,但你有TR1吗?

TR1提供基于boost库的函数,绑定和mem_fn对象,您可能已将它与编译器捆绑在一起。它还不是标准的,但我最近使用过的至少两个编译器都有它。

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

答案 8 :(得分:0)

目前还不清楚你要在这里完成什么。很明显,函数指针不是这样的。

也许您正在寻找的是指向方法的指针。

答案 9 :(得分:0)

我有一组用于我在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);}