2017-06-05 226 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);