创建字符串的hashmap到函数的向量

时间:2012-10-02 18:20:16

标签: c++ events hashmap function-pointers

我正在用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。

如果有人可以给我一个显示如何创建hashmap的快速代码段,以及一个如何循环调用函数的代码,那将非常有用。

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

EventManager.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;
};

EventManager.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 ‘>’ 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 ‘<’ 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 ‘<’ 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 个答案:

答案 0 :(得分:3)

  

字符串到函数向量的散列图...

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

这些话都在那里。请注意,您可以使用unordered_multimap而不是向量的散列图,而function类型会反映您的回调界面(例如,您可能需要function<void(Event*)>或其他内容。)

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

  

...以及如何循环调用函数...

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

(我让它懒洋洋地按需创建活动条目。)

答案 1 :(得分: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;
}

答案 2 :(得分:0)

如果C ++ 11是一个选项(VS2012,最新版本的gcc),那么你可以通过C ++库获得一个hashmap:

#include <unordered_map>

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

等等。 std::unordered_mapstd::map之间的区别在于std::map已排序,因此在下方使用red black trees。这实际上可能足以满足您的需求,在这种情况下,您也可以使用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);

答案 3 :(得分: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, ...);
    }
}

此实施仍存在一些问题,我将由您解决:

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