Я реализую простую систему событий в 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 в обратном вызове.
если кто-то может дать мне быстрый фрагмент, который показывает, как создать хэш-карту, и один для того, как циклически вызывать функции, это было бы полезно.
РЕДАКТИРОВАТЬ Используя ответ Юсселеса, я теперь получаю следующие ошибки:
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)
HashMap строк для векторов функций …
typedef unordered_map<string, vector<function<void()>>> CallbackMap;
слова были все там. Обратите внимание, что вы могли бы использовать unordered_multimap
вместо хэш-карты векторов, и function
Тип отражает ваш интерфейс обратного вызова (вы можете захотеть function<void(Event*)>
или что-то, например).
Как отметил Рам в комментарии, map
а также multimap
являются эквивалентными ассоциативными контейнерами на основе дерева, если вам конкретно не нужен хеш (или у вас нет 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);
}
(Я позволяю ему создавать запись о событии лениво и по запросу).
Что-то вроде:?
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;
}
Если 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::map
в том, что std::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);
Во-первых, вам нужно определить, что такое обратный вызов. Ваши параметры в основном сводятся к указателю на функцию:
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, ...);
}
}
Есть еще несколько проблем с этой реализацией, которые я оставлю на ваше усмотрение: