2011-03-03 98 views
5

我想添加一个简单的消息传递系统到我的项目,其中事件可以由函数调用,这将导致所有回调注册到该事件被调用。C++:类成员函数作为事件回调

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

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

  • 它甚至有可能用C++来完成这项工作吗?存储和调用指向不同类的成员函数的指针但是与相同的函数头

  • 如果这是不可能的,你有什么可以解决这个问题的建议吗?我真的很想直接将事件监听器添加到我的类中。

+0

尽一切努力避免void *。在C++中通常不需要它,它为更复杂的系统中的奇怪错误打开了可能性:它在没有编译器的帮助下强制发送者和接收者之间的一致性。 – stefaanv 2011-03-03 13:18:25

回答

0

不,这是不可能的(除非你用.net做C++/cli)。

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

3

当然这是可能的!看看Boost.Signal2Boost.Bind

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

看到这个官员boost tutorial

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引用的结构中。

1

我不完全确定你想要的存档,但也许你应该看看Boost Signals2

,如果你想建立某种形式的信号/槽机制,这是非常有帮助的。

6

是的,这是可能的。和其他人指出的那样,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

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

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

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

然后每一个应该处理事件的方法类,应该从这个类派生,他们可以多,因为他们想,只要它们返回相同的数据类型实现新的功能(在这种情况下为)并且接收相同的参数(在这种情况下为事件)。就像这样:

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(); 

但是,再一次,这并不好,只是以为我会分享!对不起,一塌糊涂,哈哈!