2011-09-09 32 views
2

我正在處理C++中的事件守護進程,我想使用成員函數回調。基本上,一個事件隊列會收集守護進程連續服務的事件。有一個帶有ID的基類Event結構,所有事件都將從它派生。我希望爲每個事件註冊的方法在其簽名中使用派生的事件類型。事件回調守護進程

struct Event 
{ 
    unsigned int eventId; 
}; 

struct EventA : public Event 
{ 
    unsigned int x; 
    unsigned int y; 
}; 

// and struct EventB, EventC (use your imagination...) 

const unsigned int EVENT_A = 1; 
const unsigned int EVENT_B = 2; 
const unsigned int EVENT_C = 3; 

class Foo 
{ 
public: 
    void handlerMethod_A(const EventA& e); 
    void handlerMethod_B(const EventB& e); 
}; 

class Bar 
{ 
public: 
    void handlerMethod_C(const EventC& e); 
}; 

然後守護進程將允許這些類使用他們的'this'指針來訂閱它們的成員函數。

class EventDaemon 
{ 
public: 

    void serviceEvents(); 

    template <class CallbackClass, class EventType> 
    void subscribe(
     const unsigned int eventId, 
     CallbackClass* classInstancePtr, 
     void (CallbackClass::*funcPtr)(EventType)); 

private: 
    Queue<Event*> eventQueue_; 
}; 

所以這個類,你可以這樣做外:

EventDaemon* ed = new EventDaemon(); 
Foo* foo = new Foo(); 
Bar* bar = new Bar(); 

ed->subscribe(EVENT_A, foo, Foo::handlerMethod_A); 
ed->subscribe(EVENT_B, foo, Foo::handlerMethod_B); 
ed->subscribe(EVENT_C, bar, Bar::handlerMethod_C); 

而且EventDaemon循環會沿着

void EventDaemon::serviceEvents() 
{ 
    while (true) 
    { 
     if (eventQueue_.empty()) 
     { 
      // yield to other threads 
     } 
     else 
     { 
      // pop an event out of the FIFO queue 
      Event e* = eventQueue_.pop(); 
      // somehow look up the callback info and use it 
      classInstancePtr->*funcPtr(reinterpret_cast<?*>(e)); 
     } 
    } 
} 

的線,所以我的問題是我怎麼可以存儲'this'指針和成員函數通過事件ID指向某種數組。這樣我可以通過使用e-> eventId和事件類型來查找'classInstancePtr'和'funcPtr',以及重新解析轉換。

回答

0

好的,所以我完成了我原來所需的界面的實現。我正在通過丹尼斯的回答,但最終得到了導師,我意識到我正在尋找的是一個簡單的多態解決方案。在此之前我無法掌握,我可以創建一個非模板化的基類,用於在向量/數組中存儲模板類。我想這就是mheyman試圖告訴我的......所以我很抱歉我沒有馬上得到它。只是爲了澄清,儘管我真的在爲自己的利益和知識尋找實施解決方案,而不僅僅是第三方庫來完成工作。所以我想我會尋找如何升壓功能的工作,而不僅僅是他們的存在和真棒。

如果有人仍然有興趣在這裏是什麼,我結束了(減去一些多餘的東西,錯誤檢查)的重要組成部分:

  • EventFunctor基本上是一個指向成員函數模板類
  • EventFunctorBase當使用非模板化基類將它們存儲在一個矢量
  • 事件是使用模板類型的動態鑄造被用於調用回調
之前
class EventDaemon 
{ 
public: 

    template <class CallbackClass, class EventType> 
    void subscribe(
     const EventId eventId, 
     CallbackClass* callbackClassInstancePtr, 
     void (CallbackClass::*funcPtr)(const EventType&)); 

private: 
    EventFunctorBase* callbacks_[MAX_NUM_EVENTS]; 
}; 

template <class CallbackClass, class EventType> 
void EventDaemon::subscribe(
    const EventId eventId, 
    CallbackClass* callbackClassInstancePtr, 
    void (CallbackClass::*funcPtr)(const EventType&)) 
{ 
    callbacks_[eventId] = new EventFunctor<CallbackClass,EventType>(callbackClassInstancePtr,funcPtr); 
} 

class EventFunctorBase 
{ 
public: 
    EventFunctorBase(); 
    virtual ~EventFunctorBase(); 
    virtual void operator()(const Event& e)=0; 
}; 

template <class CallbackClass, class EventType> 
class EventFunctor : public EventFunctorBase 
{ 
public: 

    EventFunctor(
     CallbackClass* callbackClassInstancePtr, 
     void (CallbackClass::*funcPtr)(const EventType&)); 

    virtual void operator()(const Event& e); 

private: 
    CallbackClass* callbackClassInstancePtr_; 
    void (CallbackClass::*funcPtr_)(const EventType&); 
}; 

template <class CallbackClass, class EventType> 
EventFunctor<CallbackClass,EventType>::EventFunctor(
    CallbackClass* callbackClassInstancePtr, 
    void (CallbackClass::*funcPtr)(const EventType&)) 
    : 
    callbackClassInstancePtr_(callbackClassInstancePtr), 
    funcPtr_(funcPtr) 
{ 
} 

template <class CallbackClass, class EventType> 
/*virtual*/ void EventFunctor<CallbackClass,EventType>::operator()(const Event& e) 
{ 
    (callbackClassInstancePtr_->*funcPtr_)(dynamic_cast<const EventType&>(e)); 
} 

EventDaemon環

while (true_) 
{ 
    if (eventQueue_->empty()) 
    { 
     // yield to other threads 
    } 
    else 
    { 
     Event* e = eventQueue_.pop(); 
     (*(callbacks_[e->ID]))(*e); 
    } 
} 

我的鰭這裏的步驟是嘗試去除開發者爲每個事件定義ID的需要......當然這可能會在本週晚些時候結束一個新帖子。

3

你正在努力工作。使用升壓功能:

http://www.boost.org/doc/libs/1_47_0/doc/html/function.html

這些工作是否有對象或沒有。他們會增加你的編譯時間。

請注意,無論何時遇到這些類型的問題,您知道很多人都必須遇到同樣的問題,可能有一個簡單的選擇,如果它不在標準庫中,則可能會提升。

爲了迴應尼克,我經常將助推功能對象放入向量中,而不是。

我發現,雖然boost函數對象可以容納對象引用,但是讓它們這樣做會導致對象生存期的錯誤,並且最好讓它們保存類對象的副本(您遇到相同的錯誤但是你試着持有一個對象實例的引用,你不一定控制它的生命週期)。圖案:

class Foo 
{ 
    struct Member 
    { 
    // member variable definitions 
    }; 
    shared_ptr<Member> m_; // the only real member variable 
public: 
    // etc. including the all-important copy 
    // constructor and assignment operator and 
    // don't forget the member function that gets stuck into 
    // the boost function as a callback! 
}; 

,所有的成員變量在一個shared_ptr舉行得到允許良好的性能,你不必擔心通過函數對象持有對象的壽命,因爲你可以通過值複製它們。線程代碼(我現在總是寫的東西)需要額外的東西,比如Member中的至少一個boost互斥元素或其他方式來確保值不會被忽略。

+0

看起來像boost函數也是模板化的,當我試圖根據任意訂閱存儲它們的數組時,我認爲它們不會起作用。我對成員函數指針有相當好的理解並使用它們,只是沒有嘗試允許從Event派生的任何參數。 – Nick

+0

我明白你的意思了,現在我想......我使用我想象的類似於Boost函數的工作原理髮布了我的解決方案。 – Nick

1

boost::function [或者,如果你的系統支持的話,std::function]會照顧好指針,並且不需要實際的對象,如果沒有必要的話,這個附加的好處就是不需要實際的對象。因此,代替void (SomeType::*)(EventA),您有std::function<void(EventA)>,並且您可以根據情況調用std::bind

subscribe(EVENT_A, std::bind(&foo::handleEventA, &foo, std::placeholders::_1)); 

一個簡單的包裝函數可以用來提供相同的簽名,你最初提出並隱藏討厭的佔位符。

當然,您確實仍然有每個事件類型都有自己的簽名問題,並且需要確保您使用正確的事件ID代碼。在這兩種情況下,您的基本事件類型都可以提供幫助。您的回撥不需要接受EventA&;它可以在運行時接受Event&dynamic_castEventA。對於ID,直接查詢類型。

struct Event { 
    virtual void ~Event() { } 
    virtual int ID() =0; 
}; 

template<typename E> 
struct EventHelper : Event { 
    virtual int ID() { return E::EventID; } 
}; 

struct EventA : EventHelper<EventA> { 
    static const int EventID = 89; 
}; 

現在,如果你有一個Event*對象[當你去派遣您的活動],你可以做p->ID()才能獲得相應的ID,如果你有一個EventA型[當你註冊你的回調]你可以做EventA::EventID

所以,現在,您所有必須存儲的值是std::function<void(const Event&)>和相關聯的int值,無論您的事件類型如何,您的每個回調值都是如此。

void subscribe(int id, std::function<void(const Event&)> f) { 
    callbacks.insert(std::make_pair(id, f)); 
} 

template<typename E> 
void subscribe(std::function<void(const Event&)> f) { 
    subscribe(E::EventID, f); 
} 

template<typename O, typename E> 
void subscribe(O* p, void (O::*f)(const Event&)) { 
    subscribe<E>(std::bind(f, p, std::placeholders::_1)); 
} 

您仍然有問題,即訂閱可導致一種功能,當用戶錯誤被稱爲不正確。如果您在回調中正確使用了dynamic_cast,這將在運行時被捕獲,但編譯時檢查會很好。那麼如果我們自動化這個dynamic_cast呢?對於這一步,我將使用C++ 11 lambda表達式,但它也可以使用各種方法在C++ 03中實現。

template <class CallbackClass, class EventType> 
void subscribe(CallbackClass* classInstancePtr, void (CallbackClass::*funcPtr)(EventType)) { 
    subscribe<EventType::EventID>([&](const Event& e) { 
     (classInstancePtr->*funcPtr)(dynamic_cast<const EventType&>(e)); 
    }); 
} 

現在,我們已經走了一圈又回到原來的界面,你的回調接受,他們將要工作的實際類型,但在內部已將其全部擠進一個共同特徵。

+0

謝謝!這看起來正是我正在尋找的東西......一旦我在週末回家後,我會給予執行一個鏡頭。看起來我會閱讀[Lambda表達式](http://en.wikipedia.org/wiki/C%2B%2B0x#Lambda_functions_and_expressionions),因爲這是我第一次聽說過它們。 – Nick