Есть ли более безопасный тип хранения обратных вызовов членов объекта в одном std :: multimap - PullRequest
0 голосов
/ 26 марта 2020

Я работаю над алгоритмом для хранения обратных вызовов членов объекта по указанному 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 );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...