C ++设计事件处理程序类

时间:2017-06-05 15:37:49

标签: c++

我一直致力于设计图书馆。其中一个方面是EventManager

到目前为止,设计是这样的:

Client client
client.on(string, function);

其中字符串为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 个答案:

答案 0 :(得分:2)

我有一个使用变量模板的解决方案(C ++ 14),但它看起来像一个可怕的黑客。它的一个缺点是变量模板只能是静态的。假设你可以忍受它(虽然它已经很糟糕了)...

对于每种类型的消息(此处为type =传递给处理程序的参数列表),您将拥有一个单独的处理程序列表。这可以这样实现:

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

此处F的示例是void(int) - 一个接收一个int参数的函数。另一个例子是void(int, int) - 一个接收两个int参数的函数。我在这里使用的数据结构为每个消息名称存储vector个处理程序。我也可以使用multimap

当&#34;注册&#34;一个事件处理程序,只需将其添加到处理程序列表中:

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);