2011-03-03 61 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(); 

但是,再一次,這並不好,只是以爲我會分享!對不起,一塌糊塗,哈哈!