C ++:类成员用作事件回调

时间:2011-03-03 12:45:16

标签: c++ oop events event-handling member-function-pointers

我正在尝试向我的项目添加一个简单的消息传递系统,其中事件可以由函数调用,这将导致注册到该事件的所有回调被调用。

现在,执行此操作的逻辑方法是使用函数指针。可以很容易地将指针传递给事件管理器所需的回调函数,以进行注册。事件回调函数总是返回int并以void*为参数。

但是我不想将静态全局函数注册为我的事件回调 - 我想用类成员函数来完成。

  • 是否可以使用C ++完成此操作?存储和调用指向不同类的成员函数的指针,但使用相同的函数标题

  • 如果无法做到这一点,您对我如何解决这个问题有什么建议吗?我真的想直接将事件监听器添加到我的类中。

7 个答案:

答案 0 :(得分:7)

是的,这是可能的。 C++0x has the function class that handles this,正如其他人所指出的那样,Boost也有类似的设施。

你也可以自己动手,但语法不适合胆小的人:

#include <iostream>

class Callable
{
    public:

        virtual ~Callable() {}
        virtual int operator() (void* args) = 0;
};

class CallableFreeFunction  : public Callable
{
    public:

        CallableFreeFunction(int (*func)(void*)) : func_(func) {}

        virtual int operator() (void* args) { return (*func_)(args); }

    private:

        int (*func_)(void*);
};

template <typename tClass>
class ClassMemberCallable : public Callable
{
    public:

        ClassMemberCallable(tClass* instance, int (tClass::*memberfunction)(void*)) : instance_(instance), memberfunc_(memberfunction) {}

        virtual int operator() (void* args) { return (instance_->*memberfunc_)(args); }

    private:

        tClass* instance_;
        int (tClass::*memberfunc_)(void*);
};

class Foo
{
    public:

        int derp(void* args)
        {
            std::cout << args << '\n';
            return 2;
        }
};

int freefunctionfoo(void* args)
{
    std::cout << "free" << args << '\n';
    return 2;
}

int main(int argc, char* argv[])
{
    Foo myfoo;

    Callable* callable = new ClassMemberCallable<Foo>(&myfoo, &Foo::derp);

    (*callable)(0);

    delete callable;

    callable = new CallableFreeFunction(freefunctionfoo);

    (*callable)(0);

    delete callable;

    std::cin.get();

    return 0;
}

这展示了一种以不透明的方式处理自由函数和成员函数的方法。这是一个简单的例子,可以通过多种方式使其更通用和更健壮。我会向您推荐这些页面以获取语法帮助:

http://www.newty.de/fpt/index.html

http://www.parashift.com/c++-faq-lite/pointers-to-members.html

我还建议您查看更多想法:

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

答案 1 :(得分:3)

当然有可能!请查看Boost.Signal2Boost.Bind

Boost.Signal2基本上实现了信号和插槽系统,这正是您所需要的。 然后,您可以使用boost::bind这是std::bind1ststd::bind2nd的一般化来获取函数对象包装器,基本上是您能想到的任何东西(在您的情况下,成员方法)。它真的很强大。

请参阅此官方boost tutorial

答案 2 :(得分:2)

这是我做这样的工作的不那么好的尝试: 首先,您需要一个基本事件处理程序类,现在让我们称之为EvtHandler:

class Event; //implement this yourself, it shall contain general but good info about event

class EvtHandler
{
public:
    virtual void handleEvent (Event & evt);
};

然后,每个应该以某种方式处理事件的类应该派生自这个类,并且只要它们返回相同的数据类型,它们就可以 实现新功能。 void 在这种情况下)并收到相同的参数(在这种情况下事件)。像这样:

class Foo : public EvtHandler
{
public:
    void handleFooEvent (Event & event);
};

然后我为每个特殊事件实现了消息中心,必须在需要时注册监听器和派遣事件:

class ShutdownMessageCenter
{
typedef std::map<EventHandler *, event_func> ListenerMap; 
public:

    void register (EvtHandler * handler, void(EvtHandler::*memFunc)(Event &)) {
        m_lmap[handler] = memFunc;
    }

     void callListeners () {
         Event shutdown_event (EM_SHUTDOWN /*just imagine this can mean something, idk*/);
         ListenerMap::iterator itr = m_lmap.begin ();
         for (; itr != m_lmap.end(); ++itr) {
            EvtHandler * handler = itr->first;
            void (EvtHandler::*func)(Event &) = itr->second;
            (handler->*func)(shutdown_event);
          }
      }

private:
   ListenerMap m_lmap;
};

然后,您可以将EvtHandlers注册到此特定消息中心,例如!

ShutdownMessageCenter message_center;
EvtHandler * some_handler = new EvtHandler ();
Foo * some_foo = new Foo ();
message_center.register (some_handler, &EvtHandler::handleEvent);
message_center.register (some_foo, static_cast<void (EvtHandler::*)(Event &)>(&Foo::handleFooEvent);
message_center.callListeners ();

但是再一次这根本不好,只是想我会分享!抱歉这个烂摊子,哈哈!

答案 3 :(得分:1)

我不完全确定您要存档的内容,但也许您应该查看Boost Signals2

如果你想创建某种信号/插槽机制,那将非常有用。

答案 4 :(得分:0)

不,这是不可能的(除非您使用.net执行c ++ / cli)。

现在,您仍然可以创建静态函数,将对象作为参数传递给他们,他们唯一要做的就是在该对象上调用您的成员函数。 (实际上首先需要演员表。)

答案 5 :(得分:0)

我管理的最接近的是注册静态成员函数作为回调。除了事件处理程序发送的参数之外,静态成员还将object(this)指针作为参数,并使用它来调用成员函数。

class myClass{
public:
  static void callback(void *arg, void *obj)
  {
    if (obj)
      reinterpret_cast<myClass*>(obj)->cb(arg);
  }

private:
  void cb(void *arg);
};

使用您的处理程序注册myClass::callbackthis。如果您在可以返回的内容中受到限制,则可能需要将此包装在arg引用的结构中。

答案 6 :(得分:0)

我在SWIG中使用lukes答案,因为SWIG不支持所有C ++ 11功能...使用Parsa Jamshidis方法甚至可以进一步改善这一点。

我对其进行了修改,以涵盖更多情况(可变数量的参数和可变的返回类型):

contents.beginText();