Попытка заставить std::function работать с параметром void*

#c

#c

Вопрос:

Я пытаюсь реализовать общий класс обратного вызова Messenger. По сути, это сопоставление строки с вектором общих функций-членов.

 std::map<Message, std::vector<std::function<void()>>> _receivers;
 

Класс имеет статические функции, которые в основном позволяют вам регистрировать сообщение enum для функции-члена следующим образом:

 Messenger::GetInstance()->RegisterObserver(Message::QUIT, std::bind(amp;System::OnQuit, this));
 

Затем всякий раз, когда сообщение передается куда-либо, происходит обратный вызов.

 Messenger::GetInstance()->Broadcast(Message::QUIT);
 

Реализация заключается в следующем.

 class Messenger
{
public:

    template <typename Receiver>
    void RegisterObserver(Message msg, Receiveramp;amp; receiver)
    {
       _receivers[msg].push_back(std::forward<Receiver>(receiver));
    }

    static Messenger* GetInstance();

    void Broadcast(Message msg) const
    {
        for (const autoamp; obs : _receivers.at(msg)) obs();
    }

    void Broadcast(Message msg, void* param) const;
    {
        for (const autoamp; obs : _receivers.at(msg)) obs(param); // Does NOT work
    }   

    ~Messenger();

private:
    Messenger() { };

    std::map<Message, std::vector<std::function<void()>>> _receivers;
    static Messenger* _instance;
};
 

Реализация хорошо работает без каких-либо параметров.

Теперь мне также нужно, чтобы это работало с параметром void *, чтобы я мог также передавать что-либо вместе с трансляцией, если мне нужно, чтобы оно вызывалось при обратном вызове.

Это то, чего я хочу достичь.

 Messenger::GetInstance()->Broadcast(Message::QUIT, *param);
 

Любой совет или решение очень ценятся. Это поможет мне сохранить мой дизайн в здравом уме.

Я попытался изменить map на —

 std::map<Message, std::list<std::function<void(void*)>>> _receivers;
 

а затем изменить подписи вызовов на void CallbackName(void* param). Однако я получаю сообщение об ошибке

 error C2064: term does not evaluate to a function taking 1 arguments
 

Вероятно, из-за этого:

 Messenger::GetInstance()->RegisterObserver(Message::QUIT, std::bind(amp;System::OnQuit, this));
 

Комментарии:

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

2. Вы можете создать 2 std::map , один для std::function<void()> , а другой для std::function<void(void*)> .

3. Mat: Попробовал, что получил ошибку, упомянутую выше.

4. Jarod42: Как в этом случае будет работать функция Register ?

5. Какие параметры System::OnQuit() принимает в том случае, когда он выдает ошибку?

Ответ №1:

Вы можете создать 2 карты и использовать 2 метода регистрации. Если вам нужно то же имя, вы можете использовать SFINAE следующим образом: Живой пример

 class Messenger
{
public:
    template <typename Receiver>
    auto RegisterObserver(Message msg, Receiveramp;amp; receiver) -> decltype(receiver(), void())
    {
        _receivers0[msg].push_back(std::forward<Receiver>(receiver));
    }

    template <typename Receiver>
    auto RegisterObserver(Message msg, Receiveramp;amp; receiver) -> decltype(receiver(nullptr), void())
    {
        _receivers1[msg].push_back(std::forward<Receiver>(receiver));
    }

    // Others methods

private:

    std::map<Message, std::vector<std::function<void()>>> _receivers0;
    std::map<Message, std::vector<std::function<void(void*)>>> _receivers1;
};
 

Комментарии:

1. Фантастический Джарод42. Это абсолютно то, чего я хотел достичь. 🙂 Если вы можете добавить методы для отмены регистрации наблюдателей и для 2 карт, это было бы идеально!