Я работаю над алгоритмом для хранения обратных вызовов членов объекта по указанному c идентификатору в std :: multimap, чтобы их можно было извлечь по id и вызвать обратный вызов с аргументом. Это будет использоваться в качестве системы регистрации / вызова обратного вызова для событий приложения. Ограничение используемых методов заключается в том, что интерфейс publi c должен быть базовым c, поскольку программисты, обслуживающие код / добавляющие новые функции, часто являются новичками (управление не может быть отговорено от укомплектования большей части инженерного отдела стажерами). Я знаю, что есть библиотеки обработки событий, но я не хочу кривую издержек / обучения для простой системы событий, которую я запланировал.
Приведенный ниже пример работает, как и ожидалось, для тестов. Одна ловушка, которую я вижу, состоит в том, что этот тип разделен на базовый класс. Если есть два объекта Test_1 t1
и Test_2 t2
, тогда AddCallback( EventId::ONE, &t1 )
можно вызвать, когда программист намеревался передать &t2
. Я не вижу способа обойти это с помощью этого метода.
В программе будут зарегистрированы сотни обратных вызовов с десятками различных типов событий. Существует класс mixin, который обрабатывает хранение / извлечение данных и запускает события и предоставляет данные о событиях.
#include <functional> // for std::bind()
#include <iostream>
#include <chrono>
#include <map>
using namespace std::placeholders; // for _1, _2, _3, ...
using std::cout;
using std::endl;
using std::multimap;
using std::pair;
// Base class for all event handlers
class EventHandlerBase
{
public:
virtual void EventHandler( int num ) = 0;
};
class Test_1 : public EventHandlerBase
{
public:
void EventHandler( int num )
{
cout << "Test_1 Callback: " << num << endl;
}
};
class Test_2 : public EventHandlerBase
{
public:
void EventHandler( int num )
{
cout << "Test_2 Callback: " << num << endl;
}
};
enum class EventId : uint16_t
{
ONE,
TWO,
// Add new items above this line
COUNT,
INVALID
};
class EventManager
{
public:
EventManager() : m_base_ptr_for_type( nullptr ), m_pair( nullptr )
{
m_pair = new pair<EventId, decltype( std::bind( &EventHandlerBase::EventHandler, m_base_ptr_for_type, _1 ) )>(
EventId::ONE, std::bind( &EventHandlerBase::EventHandler, m_base_ptr_for_type, _1 ) );
}
~EventManager()
{
delete m_pair;
}
void AddCallback( EventId id, EventHandlerBase* callback_object_ptr )
{
m_pair->first = id;
m_pair->second = std::bind( &EventHandlerBase::EventHandler, callback_object_ptr, _1 );
m_callbacks.insert( *m_pair );
// Make sure that if the pair is used incorrectly elsewhere that the same callback is not registered
m_pair->first = EventId::INVALID;
}
void FireCallbacks( EventId id, int num )
{
auto iter_pair = m_callbacks.equal_range( id );
for( auto iter = iter_pair.first; iter != iter_pair.second; ++iter )
{
// iter->first is the EventId, iter->second is the bound function to call
iter->second( num );
}
}
private:
// clang-format off
EventHandlerBase* m_base_ptr_for_type;
multimap< EventId, decltype( std::bind( &EventHandlerBase::EventHandler, m_base_ptr_for_type, _1 ) )> m_callbacks;
pair<EventId, decltype( std::bind( &EventHandlerBase::EventHandler, m_base_ptr_for_type, _1 ) )>* m_pair;
// clang-format on
};
int main()
{
EventManager event_manager;
Test_1 test_1;
Test_2 test_2;
event_manager.AddCallback( EventId::ONE, &test_1 );
event_manager.AddCallback( EventId::ONE, &test_1 );
event_manager.AddCallback( EventId::TWO, &test_2 );
event_manager.AddCallback( EventId::TWO, &test_2 );
event_manager.FireCallbacks( EventId::ONE, 1 );
event_manager.FireCallbacks( EventId::TWO, 2 );
}