C++ 事件调度程序 - 回调转换问题

C++ Event Dispatcher - callback casting problem

提问人:Donavan Carvalho 提问时间:4/23/2022 最后编辑:Donavan Carvalho 更新时间:4/25/2022 访问量:379

问:

我正在冒险在 Visual Studio 2022 中使用 C++ 17 创建事件调度程序。基本上,我通过 RegisterEvent() 方法在向量中存储一个字符串 (id) 来标识事件和一个回调(lambda、函数或类的方法)。 然后,我需要调用 DispatchEvent() 方法将事件作为参数传递。此事件接收事件 ID 作为参数。在内部,DispatchEvent() 方法循环查找具有相应 id 的事件,并执行先前存储的回调,将事件传递给回调。 使用通用 Event 事件时,一切正常。(只需注释掉 doCustomEvents() 方法中的所有内容;

问题

创建从泛型事件派生的事件时,在此示例中,CustomEvent 无法执行强制转换并发出以下错误:

Error C2440 : <function-style-cast>': cannot convert from 'initializer list' to 'EventProps' : LINE 41

这里 -> m_EventProps.push_back(EventProps(id, callback));

我一直在研究很多关于铸造的知识,但到目前为止,我所看到的任何东西都无法帮助我解决这个问题。

有人可以帮我吗?

谢谢!

请遵循以下代码:

#include <iostream>
#include <string>
#include <vector>
#include <functional>

#define DEBUG(x) std::cout << x << std::endl;
#define BIND(fn) [this](auto...args) -> decltype(auto) { return this->fn(args...); }

class Event
{
public:
    std::string Id;
    Event(std::string id) : Id(id) { }
    std::string ToString() { return "Event(id=" + Id +")"; }
};

class CustomEvent : public Event
{
public:
    std::string Message;
    CustomEvent(std::string id, std::string message) : Event(id), Message(message) { }
    std::string ToString() { return "CustomEvent(id=" + Id + ", message=" + Message + ")"; }
};

struct EventProps
{
public:
    std::string Id;
    std::function<void(Event)> Callback;
    EventProps(std::string id, std::function<void(Event)> callback) : Id(id), Callback(callback) { }
};

class EventDispatcher
{
private:
    std::vector<EventProps> m_EventProps;
public:
    template<typename T>
    void RegisterEvent(std::string id, std::function<void(T)> callback)
    {
        m_EventProps.push_back(EventProps(id, callback));
    }

    void DispatchEvent(Event event)
    {
        for (int i = 0; i < m_EventProps.size(); i++)
        {
            EventProps props = m_EventProps[i];
            if(props.Id == event.Id) props.Callback(event);
        }
    }
};

void globalCallback(Event e) { DEBUG("[Global] " + e.ToString()); }
void customGlobalCallback(CustomEvent ce) { DEBUG("[Global] " + ce.ToString()); }

class Application
{
public:
    EventDispatcher dispatcher;

    Application()
    {   
        doEvents();
        doCustomEvents(); // Nothing works here
    }

    void doEvents()
    {
        dispatcher.RegisterEvent<Event>("event_0", [](Event e) { DEBUG("[Lambda] " + e.ToString()); });
        dispatcher.RegisterEvent<Event>("event_1", globalCallback);
        dispatcher.RegisterEvent<Event>("event_2", BIND(Application::OnEvent));

        dispatcher.DispatchEvent(Event("event_0"));
        dispatcher.DispatchEvent(Event("event_1"));
        dispatcher.DispatchEvent(Event("event_2"));
    }

    void doCustomEvents()
    {
        dispatcher.RegisterEvent<CustomEvent>("custom_0", [](CustomEvent e) { DEBUG("[Lambda] " + e.ToString()); });
        dispatcher.RegisterEvent<CustomEvent>("custom_1", customGlobalCallback);
        dispatcher.RegisterEvent<CustomEvent>("custom_2", BIND(Application::OnCustomEvent));

        dispatcher.DispatchEvent(CustomEvent("custom_0", "Hi custom 0"));
        dispatcher.DispatchEvent(CustomEvent("custom_1", "Hi custom 1"));
        dispatcher.DispatchEvent(CustomEvent("custom_2", "Hi custom 2"));
    }

    void OnEvent(Event e) { DEBUG("[Application::OnEvent] " + e.ToString()); }
    void OnCustomEvent(CustomEvent ce) { DEBUG("[Application::CustomEvent] " + ce.ToString()); }
};

int main()
{
    Application app;
}
C++ 回调 处理 事件调度

评论

0赞 Daniel McLaury 4/23/2022
这里有大量的代码。至少给我们一个提示,哪一行给出了错误。此外,当我将其放入编译器时,它不会给出有关强制转换的错误;它给出了一个错误,因为您正在尝试构造一个 从接受 的回调,而它想要的是一个接受 .您确定此代码与此错误消息匹配吗?EventPropsCustomEventEvent
0赞 Donavan Carvalho 4/23/2022
对不起,行号,我已经添加了它。它位于第 41 行。是的,此代码与生成我刚刚复制和粘贴的错误相同。

答:

0赞 Daniel McLaury 4/25/2022 #1

这个问题可能处于人们通常会关闭的边缘,但 MSVC 通过在此处提供真正无用的错误消息,使您的生活更加艰难。(您可能想尝试升级编译器;godbolt.org 上最近的 MSVC 提供了更多有用的消息。

问题出在

dispatcher.RegisterEvent<CustomEvent>("custom_0", [](CustomEvent e) { DEBUG("[Lambda] " + e.ToString()); });

和以下几行。

dispatcher.RegisterEvent<CustomEvent>采用类型的第二个参数,然后继续使用它来尝试构造一个 .但是,EventProps 的构造函数想要的是一个 .编译器无法将仅接受 s 的函数转换为接受所有 s 的函数。std::function<void(CustomEvent)>EventPropsstd::function<void(Event)>CustomEventEvent

您需要自己进行模板化,或者进行调整以将采用类型事件的函数转换为采用所有 s 的函数。EventPropsRegisterEventTEvent