2017-06-05 96 views
3

我一直在設計一個庫。其中一個方面將是EventManagerC++設計事件處理程序類

到目前爲止,設計是這樣的:

Client client 
client.on(string, function); 

其中string是std::string和功能是std::function<void()> client.on的定義是這樣的:

void Client::on(const std::string& name, const std::function<void()>& function) { 
    _eventManager->add(string, function); 
} 

而且_eventManager->add(string, function)唯一目的是創造一個名爲Event的結構,並將名稱和函數存儲在該結構中,然後將新的支柱推到一個矢量上。現在

我可以做一些類似的: _eventManager->emit("test"); 然後它會循環的載體陣列和發現任何event.name等於當你叫emit和運行結構中的功能,您使用的名稱。

這一切工作,是非常偉大的,真棒和所有,但它不完全是我所需要的,將無法正常工作,因爲我需要發送第二個參數與_eventManager->emit()。第二個參數是未知的,很可能不是未知數,但它可以是基於您在第一個參數中輸入的字符串的多種數據類型。

用法示例:

Client client(); 

client.on("ready", [](User user) { 
    user.test(); 
}); 

User user; 
_eventManager->emit("ready", user); //I know this does not exists but it is nearly an example. 

client.on("message_created", [](Message message) { 
    std::cout << message << std::endl; //Operator overload here 
} 

Message message("Something to print"); 

_eventManager->emit("message_created", message)); 

我想過使用boost::variant允許多種類型是允許被傳遞,但是你必須通過做一些像這樣的函數中對它們進行檢索。

client.on("ready", [](boost::variant<User, Message> args){ 
    User user = boost::get<User>(args); 
}); 

而且它假設是太簡單,我寧可不要使用強制使用boost::get所有事件中檢索獲得通過該類庫中的人。

這就是說,有了上面所有的精彩信息,我想做什麼又是另一種選擇?關於爲什麼我應該或不應該做我在做什麼的任何建議?

這花了我很長時間才寫出來,所以先進的謝謝,我希望這一切都有意義。

+1

爲什麼不使用像[Qt的(https://www.qt.io)現有的庫,[SFML(https://開頭WWW .sfml-dev.org),[gtk](https://www.gtk.org)或類似的?爲什麼要經歷重塑車輪的痛苦? –

+1

你的'on'函數模板知道回調接受的參數類型。它可以實例化一箇中間thunk,它接受一個變體,提取期望的類型,並且(如果成功)調用原始回調。 – Useless

回答

2

我有一個使用可變模板(C++ 14)的解決方案,但它看起來像一個可怕的黑客。它的一個缺點是變量模板只能是靜態的。假設你可以忍受它(雖然它已經壞了)...

對於每種類型的消息(這裏type =傳遞給處理程序的參數列表),你將有一個單獨的處理程序列表。這可以被實現這樣的:

template<typename F> static map<string, vector<F>> list; 

這裏F一個例子是void(int) - 接收一個int參數的函數。另一個例子是void(int, int) - 接收兩個參數int的函數。我在這裏使用的數據結構爲每個消息名稱存儲一個處理程序的vector。我可以使用multimap

當「註冊」的事件處理程序,只需將其添加到處理程序列表:

template<typename F> static void add_handler(string s, F f) 
{ 
    list<F>[s].push_back(f); 
} 

當尋找了事件處理程序,應明確指定其類型。這是實現類型安全的關鍵部分 - 它只會在與被調用處理程序的類型相對應的數據結構中查找。

template<typename F, typename... A> static void call_handlers(string s, A... args) 
{ 
    for (F f: list<F>[s]) 
     f(args...); 
} 

用法:

// Define event handlers 
// Their type SHOULD be explicit (not lambda), because we will reference it further 
function<void(int)> action1 = [](int k){cout << "notify user " << k << '\n';}; 
function<void(int)> action2 = [](int k){cout << "alert user " << k << '\n';}; 
function<void(int, int)> action3 = [](int a, int b){cout << "give $" << a << " to user " << b << '\n';}; 

// Register the handlers 
add_handler("good_event", action1); 
add_handler("good_event", action2); 
add_handler("bad_event", action2); 
add_handler("money_event", action3); 

// Generate events, which will call the handlers 
call_handlers<function<void(int)>>("good_event", 7); 
call_handlers<function<void(int)>>("bad_event", 8); 
call_handlers<function<void(int, int)>>("money_event", 100, 9); 

// Wrong call, but no compilation error - the handler is just not found 
call_handlers<function<void(int)>>("money_event", 99);