2

我正在用 C++ 实现一个简单的事件系统。该系统旨在根据字符串(事件名称)识别事件,然后在事件触发时调用一系列回调函数。这是一个简单的蓝图:

class EventManager:
    public:
        register_event(string name) // creates a new entry in the event table
        register_listener(string name, callback) // adds the callback to name's entry in the event table
        fire_event(string name// executes all functions in the event table entry for name
    private:
        hashmap<string, vector<function>> //the event table

我目前正在努力解决的是如何创建一个字符串到函数向量的哈希图,然后遍历这些函数来执行它们。你可以假设每个回调都知道它自己的函数类型,所以每个回调都会有参数(void* userdata, ...),我将在回调中处理管理 va_list。

如果有人可以给我一个快速的片段,展示如何创建哈希图,以及如何循环调用函数,那将很有用。

编辑使用无用的答案,我现在得到以下错误:

事件管理器.h

#include <string>
#include <unordered_map>
#include <vector>

using namespace std;

typedef unordered_map<string, vector<function<void()>>> CallbackMap;

class EventManager{
public:
    EventManager(){
        callbacks = CallbackMap();
    }

    void EventManager::RegisterEvent(string const& name);
    void EventManager::RegisterListener(string const &name, function<void()> callback);
    void EventManager::FireEvent(string name);
private:
    CallbackMap callbacks;
};

事件管理器.cpp

#include "EventManager.h"
#include <string>

using namespace std;

void EventManager::RegisterEvent(string const& name){
    callbacks[name] = NULL;
}

void EventManager::RegisterListener(string const &name, function<void()> callback)
{
    callbacks[name].push_back(callback);
}

bool EventManager::FireEvent(string name){
    auto event_callbacks = callbacks.find(event_name);
    if (event_callbacks == callbacks.end()){
        return false; // ?
    }

    // or std::for_each
    for (auto cb = event_callbacks->second.begin();
         cb != event_callbacks->second.end(); ++cb)
    {
        (*cb)();
    }
    return true;
}

终端

$ g++ EventManager.cpp -std=c++0x
In file included from EventManager.cpp:1:0:
EventManager.h:7:38: error: ‘function’ was not declared in this scope
EventManager.h:7:52: error: template argument 1 is invalid
EventManager.h:7:52: error: template argument 2 is invalid
EventManager.h:7:53: error: template argument 2 is invalid
EventManager.h:7:53: error: template argument 5 is invalid
EventManager.h:7:55: error: expected unqualified-id before ‘&gt;’ token
EventManager.h:11:5: error: ‘CallbackMap’ does not name a type
EventManager.h:18:47: error: ‘function’ has not been declared
EventManager.h:18:55: error: expected ‘,’ or ‘...’ before ‘&lt;’ token
EventManager.h: In constructor ‘EventManager::EventManager()’:
EventManager.h:14:9: error: ‘callbacks’ was not declared in this scope
EventManager.h:14:33: error: ‘CallbackMap’ was not declared in this scope
EventManager.cpp: In member function ‘void EventManager::RegisterEvent(const string&)’:
EventManager.cpp:7:5: error: ‘callbacks’ was not declared in this scope
EventManager.cpp: At global scope:
EventManager.cpp:10:57: error: ‘function’ has not been declared
EventManager.cpp:10:65: error: expected ‘,’ or ‘...’ before ‘&lt;’ token
EventManager.cpp: In member function ‘void EventManager::RegisterListener(const string&, int)’:
EventManager.cpp:12:5: error: ‘callbacks’ was not declared in this scope
EventManager.cpp:12:31: error: ‘callback’ was not declared in this scope
EventManager.cpp: At global scope:
EventManager.cpp:15:6: error: prototype for ‘bool EventManager::FireEvent(std::string)’ does not match any in class ‘EventManager’
EventManager.h:19:10: error: candidate is: void EventManager::FireEvent(std::string)
4

4 回答 4

3

字符串到函数向量的哈希映射...

typedef unordered_map<string, vector<function<void()>>> CallbackMap;

话都在那里。请注意,您可以使用unordered_multimap而不是向量的哈希图,并且function类型反映了您的回调接口(例如,您可能想要function<void(Event*)>什么)。

正如 Ram 在评论中指出的那样,如果您不需要哈希(或者没有 C++11,尽管您也可以使用 boost::unordered_map),它们是等效的基于树的关联容器mapmultimap

...还有一个关于如何循环调用函数的...

bool EventManager::fire_event(string const& event_name)
{
    auto event_callbacks = callbacks.find(event_name);
    if (event_callbacks == callbacks.end()) return false; // ?

    // or std::for_each
    for (auto cb = event_callbacks->second.begin();
         cb != event_callbacks->second.end(); ++cb)
    {
        (*cb)();
    }
    return true;
}

哦,注册很简单:

void EventManager::register_listener(string const &name,
                                     function<void()> callback)
{
    callbacks[name].push_back(callback);
}

(我让它懒惰地按需创建事件条目)。

于 2012-10-02T18:26:51.143 回答
0

就像是:?

typedef void(*ExampleFunction) (std::string str, int bar);

void foo(std::string str, int bar){
}
void foo2(std::string str, int bar){
}

int main(){
    std::map<std::string,std::vector<ExampleFunction> > f_map;
    std::vector<ExampleFunction> v_func;
v_func.push_back(foo);
v_func.push_back(foo2);

f_map["tiny"] = v_func;
f_map["tiny"][0]("Woo",1);
return 1;
}
于 2012-10-02T18:31:54.373 回答
0

如果 C++ 11 是一个选项(VS2012,gcc 的最新版本),那么您可以通过 C++ 库获取哈希图:

#include <unordered_map>

std::unordered_map<KeyType, ValueType> map {{x,y},{n,m1},{a,b}};
map[x] = y;
ValueType q = map[y];

等等。std::unordered_map和之间的区别在于std::mapstd::map是排序的,因此在下面使用红黑树。这实际上可能足以满足您的需求,在这种情况下,您也可以使用 C++03。

请注意,我发现 MSVC '12 不支持地图的默认分配,如我的示例所示。如果需要,请使用 boost::assign:

#include <boost/assign.hpp>

std::unordered_map<K,V> map = boost::assign::map_list_of<K,V> (x,y)(a,b)(n,q);
于 2012-10-02T18:32:19.223 回答
0

首先,您需要定义什么是回调。您的选择基本上归结为一个函数指针:

typedef void (* EventCallback)(void * userdata, ...);

或者一个接口:

class IEventCallback
{
public:
    virtual void callback(void * userdata, ...) = 0;
};

我更喜欢接口版本——您可以将函数指针包装在实现此接口的类的对象中;走另一条路要困难得多。

在您的EventManager上,定义地图和类型(使迭代更容易):

private:
    typedef std::vector<IEventCallback &> EventCallbackList_t;
    typedef std::map<std::string, EventCallbackList_t> EventMap_t;

    EventMap_t m_eventMap;

接下来,实现相当简单:

void EventManager::register_event(std::string const & name)
{
    m_eventMap.insert(std::make_pair(name, EventCallbackList_t()));
}

void EventManager::register_listener(std::string const & name, IEventCallback & callback)
{
    EventMap_t::iterator event = m_eventMap.find(name);

    if (event == m_eventMap.end()) {
        throw "No such event.";
    }

    event->second.push_back(callback);
}

void EventManager::fire_event(std::string const & name, void * userdata, ...)
{
    EventMap_t::iterator event = m_eventMap.find(name);

    if (event == m_eventMap.end()) {
        throw "No such event.";
    }

    for (EventCallbackList_t i = event->second.begin(); i != event->second.end(); ++i) {
        i->callback(userdata, ...);
    }
}

这个实现仍然存在一些问题,我将由您来解决:

  • 并发。如果它们将从多个线程调用,请确保注册/触发操作是同步的。
  • 回调对象所有权。考虑使用共享指针类型来管理它们的生命周期。
于 2012-10-02T18:35:22.243 回答