【问题标题】:Function Pointers as Event Callbacks in a st函数指针作为事件回调
【发布时间】:2021-02-10 11:56:20
【问题描述】:

我目前正在尝试实现一个事件系统,其中层可以订阅某些事件类型。我正在为回调方法的函数指针而苦苦挣扎。在 layer.h 文件中,我有 OnEvent 函数和一个函数指针,如下所示:

    void OnEvent(const Event& e);
    void(Engine::Layer::*m_EventCallback)(const Event& e);

在层的构造函数中我只是这样做:

m_EventCallback = OnEvent;

为了跟踪哪个层订阅了哪个 EventType,我定义了一个结构作为 folling:

struct CallbackData {
    EventType type;
    void(Engine::Layer::*OnEvent)(const Event& e);
    int layerID;
    bool isActive;
};

我有一个 EventDispatcher,它的调度函数看起来像这样:

bool EventDispatcher::Dispatch(const Event& e)
{
    for (CallbackData& calldata : m_Callbacks) {
        if (calldata.type == e.GetEventType() && calldata.isActive) {
            calldata.OnEvent(e);
        }
    }
    
    //TODO work on this so it only returns true if the event has been properly dispatched
    return true;
}

还有一个 subscribe 函数,它创建一个 CallbackData 实例并将其推送到一个如下所示的向量中:

void EventDispatcher::Subscribe(EventType type, void(Engine::Layer::*OnEvent) 
(const Event& e), int layerID)
{
    CallbackData temp = { type, OnEvent, layerID, true };
    m_CallbackInsert = m_Callbacks.emplace(m_CallbackInsert, temp);
}

因此,如果回调数据的类型与事件引用类型相同,则应通过函数指针调用 OnEvent 函数。 函数指针定义需要有 Engine:: 这只是一个命名空间和 Layer 是 OnEvent 所在的类。我不知道为什么它需要命名空间,因为我在这里所做的一切都是在命名空间中定义的。 但主要问题是如何正确定义函数指针,然后按照此处展示的方式调用它?

确切的错误信息是这样的:

expression preceding parentheses of apparent call must have(pointer-to-)function type

请不要将网站与已实施的事件系统链接。我已经看过那些,并没有真正了解多少。这就是为什么我想自己做,所以我理解它。

提前致谢!

【问题讨论】:

  • 获取成员指针的语法是&ClassName::MemberName。没有像函数指针那样的隐式转换。
  • 你还需要一个Layer 的实例来调用函数。指向成员的指针不属于特定实例,但必须相对于对象取消引用。
  • 在 CallbackData 结构中添加一个 Layer 指针然后接收该层指针的函数指针是否有意义?

标签: c++ events callback function-pointers


【解决方案1】:

假设我明白你在做什么......

您有一个 EventDispatcher,他需要保留一个可能的回调注册表。我将剪切并粘贴我使用的一些代码,然后解释一下。

首先,这是我的 Route 对象的相关部分。

class Route {
public:
    typedef std::function<void(const HTTPServerRequest &, HTTPServerResponse &)> Callback;

    Callback callback;
};

class Router {
public:
    void addRoute(const std::string &method, const std::string &path, Route::Callback callback);

};

这部分适用于您的期望,不需要任何特别的东西。这将创建一个 Route 对象数组,回调方法被赋予传递给 addRoute() 的回调。当我想在这条特定路线上调度时:

route->callback(request, response);

你可能也知道那部分。

对于我的代码,我正在对一个对象进行方法调用。要通过它,您有两个选择。第一个是使用 std::bind() - 我真的不喜欢。

所以我使用 lambdas。

addRoute("GET", "/ping", [=](const HTTPServerRequest &request, HTTPServerResponse &response) { ping(request, response); } );

这绝对是最有效的吗?我不知道。但是性能并不是很糟糕,所以这就是我所做的。

基本上,我保留了 std::function 指针,它们真的很容易使用。只要签名匹配,您就可以将 lambda 作为 std::function 传递。

另一种选择是使用 std::bind ——正如我所说,我不喜欢它,主要是因为我认为它比使用 lambda 更难看。但这绝对是一种观点,而不是事实。使用 std::bind 可能是更好的方法,但我没有任何代码可以告诉你这样做。

我不完全确定这是否真的解决了您的困惑,但如果它很接近,请发表评论,我会尝试澄清。

【讨论】:

  • 我要添加评论。您会注意到我使用 [=] 部分传递值。在这种情况下,唯一通过的是“this”。在使用有效地“稍后调用”的 lambda 时,要非常小心地通过引用传递,因为您的引用可能不再有效。按值传递更安全。是的,我花了一天半的时间才意识到,当我在实际上是未来的电话上做 [&] 时,我犯了一个多么大的错误。
猜你喜欢
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多