Generi c C ++ callback map, есть ли лучший способ? - PullRequest
0 голосов
/ 06 марта 2020

Я пытаюсь создать обобщенное сообщение, обрабатывающее мой код. Каждое сообщение идентифицируется целочисленным идентификатором. Поскольку все обработчики сообщений имеют одинаковое замедление, и мне нравится быстро обрабатывать каждое сообщение, я использую std::map для подключения и нахождения соответствующего обработчика сообщений для указанных c идентификаторов сообщений. Затем я вызываю этот обработчик и передаю ему сообщение . Несколько было сделать это, и вот пример:

const std::map<int, void(*)(void*)> g_handlers = {
    {1, h1},
    {2, h2}
};

...
// message
int message_id = 2;
int data = 3;
// handle message
g_handlers[message_id](&data);

Но есть несколько больших ограничений для этого метода:

  1. Поскольку существуют разные сообщения, нам нужно обобщите их, передав их как void* параметр. Таким образом, каждый синтаксис обработчика сообщений будет void (*)(void*), и тогда мы сможем использовать его в качестве значения map.
  2. Нет проверки типа для этого сообщения. Если кто-то неправильно добавил обработчик сообщения с идентификатором сообщения 1 для идентификатора сообщения 2, мы не сможем быстро найти эту ошибку.

Я хотел попробовать что-то новое, поэтому я пытался найти способ решить эти проблемы. проблемы, и я наконец достиг рабочего кода. Вот код:

class handler_base {
    public:
    template <typename U>
    void operator()(U* arg) {
        run(arg, typeid(U));
    }

    private:
    virtual void run(void* arg, const std::type_info& info) {}
};

template<typename T>
class handler : public handler_base {
    public:
    using type = T;
    handler(void (*f)(T*)) :func(f) {
    }

    private:
    void run(void* arg, const std::type_info& info) {
        assert(info.hash_code() == typeid(T).hash_code());
        func(static_cast<T*>(arg));
    }
    void (*func)(T*);
};

int main()
{
    // 2 different types of handlers
    handler h1(+[](double* v){ std::cout << "double called " << *v << "\n"; });
    handler h2(+[](int* v){ std::cout << "int called " << *v << "\n"; });

    const std::map<int, handler_base&> myhandler = {
        {1, h1},
        {2, h2}
    };

    double d = 1.5;
    int i = 3;

    myhandler.at(1)(&d);
    //myhandler.at(1)(&i);  // Error: failed assert due to type check
    //myhandler.at(2)(&d); // Error: failed assert due to type check
    myhandler.at(2)(&i);  
}

Теперь вот мой вопрос:

  1. Используется ли & в качестве значения карты, допустимого, когда карта const? Я знаю, что это не так, когда сама карта не const, но мне интересно, правильно ли она в этом случае или нет.
  2. Есть ли какой-нибудь более простой способ сделать это? предоставление другого синтаксиса обработчика сообщений обратного вызова с использованием одного контейнера с проверкой типа?
  3. Что вы думаете об этой идее в целом? Это хорошая идея, чтобы добавить эту сложность для проверки типов и гетерогенных обратных вызовов? Лично я всегда go для этого правила ", простота - лучший ", и я обычно выбираю первый подход (используя обобщенный void(*)(void*) для обратного вызова), но мне нравится знать, что вы об этом думаете.

1 Ответ

1 голос
/ 06 марта 2020

Я думаю, вы можете полностью пропустить базовый класс. Вы просто сохраняете указатель функции напрямую как указатель на функцию для преобразования туда и обратно. Я также сделал это принять много параметров:

#include <unordered_map>
#include <iostream>
#include <cassert>

struct Handler
{
    template <typename T>
    Handler(T fn)
        : f((void(*)())(fn))
        , info(typeid(T))
    {
    }

    template <typename... Args>
    void operator()(Args&&... args)
    {
        using Fn = void(Args...);
        assert(info.hash_code() == typeid(Fn*).hash_code());
        return ((Fn*)(f))(std::forward<Args>(args)...);
    }
    void (*f)();
    const std::type_info& info;
};


int main()
{
    std::unordered_map<int, Handler> cbmap;
    cbmap.emplace(1, +[](int a, double b){std::cout << "1" << a << " " << b << "\n";});
    cbmap.emplace(2, +[](double a){std::cout << "2" << a << "\n";});
    cbmap.emplace(3, +[](double& a){std::cout << "3 " << a << "\n";});

    double x = 42.0;

    cbmap.at(1)(42,4.2);
    cbmap.at(2)(4.2);
    cbmap.at(3)(x);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...