Fiz isso com std :: function e std :: bind ..
Eu escrevi esta classe EventManager que armazena um vetor de manipuladores em um unordered_map que mapeia tipos de eventos (que são apenas const unsigned int, eu tenho um grande enum com escopo de namespace deles) para um vetor de manipuladores para esse tipo de evento.
Na minha classe EventManagerTests, configurei um manipulador de eventos, como este:
auto delegate = std::bind(&EventManagerTests::OnKeyDown, this, std::placeholders::_1);
event_manager.AddEventListener(kEventKeyDown, delegate);
Esta é a função AddEventListener:
std::vector<EventHandler>::iterator EventManager::AddEventListener(EventType _event_type, EventHandler _handler)
{
if (listeners_.count(_event_type) == 0)
{
listeners_.emplace(_event_type, new std::vector<EventHandler>());
}
std::vector<EventHandler>::iterator it = listeners_[_event_type]->end();
listeners_[_event_type]->push_back(_handler);
return it;
}
Aqui está a definição do tipo EventHandler:
typedef std::function<void(Event *)> EventHandler;
Então, de volta a EventManagerTests :: RaiseEvent, eu faço o seguinte:
Engine::KeyDownEvent event(39);
event_manager.RaiseEvent(1, (Engine::Event*) & event);
Este é o código para EventManager :: RaiseEvent:
void EventManager::RaiseEvent(EventType _event_type, Event * _event)
{
if (listeners_.count(_event_type) > 0)
{
std::vector<EventHandler> * vec = listeners_[_event_type];
std::for_each(
begin(*vec),
end(*vec),
[_event](EventHandler handler) mutable
{
(handler)(_event);
}
);
}
}
Isso funciona. Eu recebo a chamada em EventManagerTests :: OnKeyDown. Tenho que deletar os vetores na hora de limpar, mas depois que faço isso não há vazamentos. Gerar um evento leva cerca de 5 microssegundos no meu computador, que é cerca de 2008. Não é exatamente super rápido, mas. É justo, desde que eu saiba disso e não o use em código ultra quente.
Eu gostaria de acelerá-lo rolando meu próprio std :: function e std :: bind, e talvez usando um array de matrizes em vez de um unordered_map de vetores, mas ainda não descobri como armazenar uma função de membro ponteiro e chamá-lo do código que não sabe nada sobre a classe que está sendo chamada. A resposta de Eyelash parece muito interessante ..