2011-07-30 50 views
3

我正在嘗試爲我的遊戲編寫一個事件系統。我的活動經理將存儲的回調既可以是普通函數,也可以是函子。我還需要能夠比較函數/函數,所以我知道我需要從事件管理器斷開哪一個。消息系統:回調可以是任何東西

•最初我嘗試使用boost :: function;它完美地處理函數和函子,除了它沒有運算符==,所以我不能刪除回調,如果我想。

class EventManager 
{ 
    typedef boost::function<void (boost::weak_ptr<Event>)> Callback; 
    std::map<Event::Type, std::vector<Callback>> eventHandlerMap_; 
}; 

•我也使用boost ::信號試過了,但也給了我與運營商==編譯問題:

二進制「==」:沒有操作員發現這需要一個左類型「常量函子」的 - 手操作數(或沒有可接受的轉換)

void test(int c) { 
    std::cout << "test(" << c << ")"; 
} 

struct Functor 
{ 
    void operator()(int g) { 
     std::cout << "Functor::operator(" << g << ")"; 
    } 
}; 

int main() 
{ 
    boost::signal<void (int)> sig; 

    Functor f; 

    sig.connect(test); 
    sig.connect(f); 

    sig(7); 

    sig.disconnect(f); // Error 
} 

關於我如何實現這個沒有其他的建議?或者,也許我可以如何使boost :: function或boost :: signal工作? (我寧願使用boost ::功能雖然,因爲我聽說過的信號,而對項目的小集合放緩。)


編輯:這是我想eventmanager進行的,該界面具有。

class EventManager 
{ 
    public: 
    void addEventHandler(Event::Type evType, Callback func); 
    void removeEventHandler(Event::Type evType, Callback func); 

    void queueEvent(boost::shared_ptr<Event> ev); 
    void dispatchNextEvent(); 
}; 
+0

@bdonlan所述一個在所述信號的例子是從[升壓:: function_equal](http://www.boost.org/doc/libs/1_47_0/doc/html/boost/function_equal.html)。 –

+0

八九不離十DUP:http://stackoverflow.com/questions/89488/comparing-stdtr1function-objects – stijn

回答

1

不管,我找到了解決辦法。小模板魔術和事情變得簡單(R):

template<typename F> 
void EventManager::removeEventHandler(Event::Type evType, F func) 
{ 
    auto compare = [func](const Callback& other) -> bool { 
     F const* f = other.target<F>(); 
     if (f == nullptr) return false; 
     return *f == func; 
    }; 

    std::vector<Callback>& callbacks = ...; 
    auto pend = std::remove_if(callbacks.begin(), callbacks.end(), compare); 
    callbacks.erase(pend, callbacks.end()); 
} 


template<typename R, typename F, typename L> 
void EventManager::removeEventHandler(
    Event::Type evType, const boost::_bi::bind_t<R, F, L>& func) 
{ 
    auto compare = [&func](const Callback& other) -> bool { 
     auto const* f = other.target<boost::_bi::bind_t<R, F, L>>(); 
     if (f == nullptr) return false; 
     return func.compare(*f); 
    }; 

    std::vector<Callback>& callbacks = ...; 
    auto pend = std::remove_if(callbacks.begin(), callbacks.end(), compare); 
    callbacks.erase(pend, callbacks.end()); 
} 

我需要處理,因爲operator==實際上並沒有做綁定的對象比較,但產生一個新的算符是結果比較Boost.Bind對象分開另外兩個(read more)。要比較Boost.Bind,您必須使用成員函數compare()

類型boost::_bi::bind_t似乎是一個內部類型的Boost(我想這就是命名空間「_bi」的意思下劃線),但它應該是安全的把它作爲boost::function_equal所有重載也採用這種類型(reference) 。

只要有operator==定義比較,或者如果您使用Boost.Bind,此代碼將適用於所有類型的仿函數。我有一個粗淺的外觀爲std::bind(C++ 0x中),但似乎並不具有可比性,所以不會與我上面貼的代碼工作。

1

你會發現大多數泛型函數包裝不支持函數相等。

這是爲什麼?那麼,就看看你的函子有:

struct Functor 
{ 
    void operator()(int g) { 
     std::cout << "Functor::operator(" << g << ")"; 
    } 
}; 

Functor沒有operator==,因此不能用於相等性比較。所以當你通過值將它傳遞給boost::signal時,會創建一個新實例;這將比較指針相等的false,並且沒有操作符來測試值相等。

大多數仿函數實際上並不具有值相等的謂詞。這沒什麼用處。處理這個問題的通常方法是代之以處理回調。 boost :: signals使用它的connection對象執行此操作。例如,看看這個例子從the documentation

boost::signals::connection c = sig.connect(HelloWorld()); 
if (c.connected()) { 
// c is still connected to the signal 
    sig(); // Prints "Hello, World!" 
} 

c.disconnect(); // Disconnect the HelloWorld object 
assert(!c.connected()); c isn't connected any more 

sig(); // Does nothing: there are no connected slots 

有了這個,HelloWorld並不需要有一個operator==,因爲你直接引用該信號註冊。

+0

如果我做保存連接對象,我還不如用矢量<提振::溫控功能>代替並保存迭代器相反,因爲函數比信號更快。沒有辦法讓boost :: function比較內存地址嗎? –

+0

'boost :: function'也可以按值捕獲,所以地址將永遠不變。我懷疑信號不會比常規的函數向量更糟糕。 – bdonlan

1

你有沒有試過libsigc和libsigC++?我開始在linux中使用它們並且愛上它們。我現在也在我的Windows應用程序中使用它們。我相信它比提升更具可擴展性和靈活性。實施起來也很輕鬆。

1

我強烈建議您考慮Don Clugston的「成員函數指針和最快可能的C++代表」。您可以在文章從這裏下載代碼:

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

在許多其他好處,他的代表提供比較符(==,=,<!)開箱。我目前正在將它們用於實時系統,並以各種方式發現它們非常出色。我似乎記得我們不得不做一些小修改來修復編譯器的可移植性問題;但是,這種體驗會根據平臺等

此外,文章是幾年前的,所以你可能想,如果你遇到任何問題,以谷歌爲左右更新的代碼/討論關於此委託實施。