Структура данных для хранения callbackов

138
07 мая 2019, 11:00

Мне нужна структура данных, которая каждой строке будет ставить в соответствие некоторую callback-функцию. Эта структура будет инициализироваться извне класса с помощью лямбд. Какую структуру данных лучше использовать, если сигнатуры функций могут быть разными?

Нужно такое поведение:

map["gui_ok_button"] = [&](){
   data.apply();
};
map["gui_close_button"] = [&](int close_code){
   data.close(close_code);
};
Answer 1

Новые возможности Си++17 вам помогут. Могу предложить реализация на std::variant:

#include <map>
#include <string>
#include <functional>
#include <variant>
// здесь должны быть перечислены варианты
// всех сигнатур
using Callback = std::variant
<
      std::function<void(void)>
    , std::function<void(int)>
    , std::function<void(const std::string&)>
>;
using CallbackMap = std::map<std::string, Callback>;

Далее главная идея решения, функция вызова:

// Эта перегрузка нужна для обработки неверных вызовов
// статическая проверка пока не приходит на ум.
template<typename F, typename ...A>
void DoCall(F&&, A&&...)
{
    throw std::bad_variant_access();
}
// Нужно для каждой варианта сигнатуры определить перегрузку для проброски 
// аргументов. Выглядит коряво, тут можно автоматизировать.
void DoCall(std::function<void(void)> & f)
{
    f();
}
void DoCall(std::function<void(int)> & f, int arg)
{
    f(arg);
}
void DoCall(std::function<void(const std::string&)> & f, const std::string & arg)
{
    f(arg);
}
// Собственно вызов калбека
template<typename ...A>
void Dispatch(const Callback & callback, A&& ...args)
{        
    // здесь лямбда нужна чтобы захватить args и передать в DoCall, 
    // все из-за того что std::visit не предусматривает проброс
    // аргументов в посетителя (здесь в нашу лямбду)
    std::visit
    (
        [&args...](auto && func) -> void
        {
            DoCall(func, std::forward<A>(args)...);
        }
      , callback
    );
}

Использовать это вот так как-то:

void TestCallbackMap()
{
    auto cm = CallbackMap();
    cm["foo"] = [](void) -> void
    {
        std::cout << "call: foo" << std::endl;
    };
    cm["bar"] = [](int arg) -> void
    {
        std::cout << "call: bar " << arg << std::endl;
    };
    cm["baz"] = [](const std::string & msg) -> void
    {
        std::cout << "call: baz " << msg << std::endl;
    };
    Dispatch(cm["baz"], std::string("hello world"));
}
READ ALSO
Можно ли как-то ещё ускорить данный код?

Можно ли как-то ещё ускорить данный код?

Где subset1 и subset2 - это вектора подмножеств первой и второй половины множества соответственно

134
Как работает этот код по распаковке TGA файла? (unload_rle_data)

Как работает этот код по распаковке TGA файла? (unload_rle_data)

Начал копать библиотеку уроков по тому как работает OpenGL и не могу разобраться с одним методомСсылка на курс: https://github

131
Доступ к элементам std::map по индексу

Доступ к элементам std::map по индексу

Интересует, как можно получить доступ к элементу map'ы с индексом iНо! К примеру, при перебирании map'ы при помощи итерататора она отсортирована...

154