使用函数指针作为 UI-Button 回调的缺点
Disadvantages of using a function pointer as an UI-Button callback
我目前正在创建自己的基于 SFML 的 GUI 库。
目前我正在研究一个按钮。因此,在创建按钮时,您还必须指定一个回调,它是一个函数,在按钮单击时执行。
现在,我来回答我仅使用指向函数的指针作为按钮回调的缺点是什么,因为我不知道有任何流行的 GUI 库也能如此简单地做到这一点。
如果回调函数是一个很长的过程,我会在一个新的线程中执行它,但我现在不确定。
那么,不使用这种简单解决方案的原因是什么,尤其是更好的方法是什么?
这是一个棘手的问题!
函数指针在发送方很容易实现,但在接收方很难使用,因为它们没有任何上下文。
一个问题是函数指针不能指向成员函数。这就是为什么您经常看到(C 风格)框架将任意 void *userData
传递给它们的回调,因此您可以转换 this
指针并以这种方式检索它。这仍然需要你写一个静态包装函数来转换指针并调用成员函数。
更现代的解决方案是使用 std::function
。这可以包含常规函数指针、成员函数指针,也可以包含 lambda 或仿函数。
但是,当您像这样(或以其他方式)添加上下文时,您很快就会 运行 遇到生命周期方面的困难。当接收方 class 在发送方之前被销毁时,应该发生什么?如果您什么都不做,这种情况将导致未定义的行为。一种解决方案是在接收方跟踪接收方订阅了哪些事件,并在接收方被销毁之前解除绑定。而这需要双向完成:当发送者被销毁时,它还需要通知接收者它应该忘记发送者,否则接收者稍后会尝试解除绑定不再存在的事件。
而且我还没有开始考虑多线程...
有图书馆以各种方式解决这些问题,例如eventpp
(只是通过网络搜索找到的,这不是背书)。
另一个要提到的是 Qt 工具包,它甚至编写了自己的 C++ 语言小 signals and slots 扩展(作为代码生成器和一堆宏实现)来解决这个问题以非常符合人体工程学的方式解决问题。
what the disadvantages are of using just a pointer to a function as a button-callback
将一些上下文参数传递给该函数会派上用场。
我的意思是,UI 可能有很多按钮对不同的对象执行相同的操作。想想朋友列表中每个昵称旁边的“发送消息”按钮。
所以您可能希望按钮将一些上下文参数传递给调用。
但是因为我们在谈论 C++,所以最好将其抽象为
struct IButtonAction
{
virtual void OnAttached() = 0;
virtual void OnDetached() = 0;
virtual void OnClick() = 0;
};
并让客户端代码实现此接口,在每个实例对象中存储 Arg1
、Arg2
等。
按钮 class 会在 begins/ends 使用指向此回调接口实例的指针时调用 OnAttached
/OnDetached
。这些呼叫必须配对。如果需要,这些方法的客户端实现可以执行生命周期管理并与 OnClick
同步。
OnClick
方法执行操作。
我认为按钮不应该打扰线程。客户端代码有责任决定是否为冗长的操作生成线程。
我目前正在创建自己的基于 SFML 的 GUI 库。 目前我正在研究一个按钮。因此,在创建按钮时,您还必须指定一个回调,它是一个函数,在按钮单击时执行。
现在,我来回答我仅使用指向函数的指针作为按钮回调的缺点是什么,因为我不知道有任何流行的 GUI 库也能如此简单地做到这一点。 如果回调函数是一个很长的过程,我会在一个新的线程中执行它,但我现在不确定。
那么,不使用这种简单解决方案的原因是什么,尤其是更好的方法是什么?
这是一个棘手的问题!
函数指针在发送方很容易实现,但在接收方很难使用,因为它们没有任何上下文。
一个问题是函数指针不能指向成员函数。这就是为什么您经常看到(C 风格)框架将任意 void *userData
传递给它们的回调,因此您可以转换 this
指针并以这种方式检索它。这仍然需要你写一个静态包装函数来转换指针并调用成员函数。
更现代的解决方案是使用 std::function
。这可以包含常规函数指针、成员函数指针,也可以包含 lambda 或仿函数。
但是,当您像这样(或以其他方式)添加上下文时,您很快就会 运行 遇到生命周期方面的困难。当接收方 class 在发送方之前被销毁时,应该发生什么?如果您什么都不做,这种情况将导致未定义的行为。一种解决方案是在接收方跟踪接收方订阅了哪些事件,并在接收方被销毁之前解除绑定。而这需要双向完成:当发送者被销毁时,它还需要通知接收者它应该忘记发送者,否则接收者稍后会尝试解除绑定不再存在的事件。
而且我还没有开始考虑多线程...
有图书馆以各种方式解决这些问题,例如eventpp
(只是通过网络搜索找到的,这不是背书)。
另一个要提到的是 Qt 工具包,它甚至编写了自己的 C++ 语言小 signals and slots 扩展(作为代码生成器和一堆宏实现)来解决这个问题以非常符合人体工程学的方式解决问题。
what the disadvantages are of using just a pointer to a function as a button-callback
将一些上下文参数传递给该函数会派上用场。 我的意思是,UI 可能有很多按钮对不同的对象执行相同的操作。想想朋友列表中每个昵称旁边的“发送消息”按钮。
所以您可能希望按钮将一些上下文参数传递给调用。 但是因为我们在谈论 C++,所以最好将其抽象为
struct IButtonAction
{
virtual void OnAttached() = 0;
virtual void OnDetached() = 0;
virtual void OnClick() = 0;
};
并让客户端代码实现此接口,在每个实例对象中存储 Arg1
、Arg2
等。
按钮 class 会在 begins/ends 使用指向此回调接口实例的指针时调用 OnAttached
/OnDetached
。这些呼叫必须配对。如果需要,这些方法的客户端实现可以执行生命周期管理并与 OnClick
同步。
OnClick
方法执行操作。
我认为按钮不应该打扰线程。客户端代码有责任决定是否为冗长的操作生成线程。