【发布时间】:2015-02-15 05:22:56
【问题描述】:
许多函数接受函数指针作为参数。 atexit 和 call_once 就是很好的例子。如果这些更高级别的函数接受 void* 参数,例如 atexit(&myFunction, &argumentForMyFunction),那么我可以通过传递函数指针和数据块来轻松包装任何我喜欢的函子以提供状态性。
在很多情况下,我希望我可以注册一个带有参数的回调,但是注册函数不允许我传递任何参数。 atexit 只接受一个参数:一个接受 0 个参数的函数。我不能注册一个函数来清理我的对象,我必须注册一个清理一个类的所有对象的函数,并强制我的类维护一个需要清理的所有对象的列表。
我一直认为这是一种疏忽,似乎没有正当理由不允许传递一个可怜的 4 或 8 字节指针,除非您使用的是极其有限的微控制器。我一直认为他们根本没有意识到额外的论点有多么重要,直到重新定义规范为时已晚。在call_once 的情况下,posix 版本不接受任何参数,但 C++11 版本接受一个仿函数(这实际上相当于传递一个函数和一个参数,只有编译器会为您完成一些工作)。
是否有任何理由选择不允许这种额外的论点?只接受“带有 0 个参数的 void 函数”有什么好处吗?
【问题讨论】:
-
在像
atexit这样的情况下,您正在使用全局状态,只需编写自己的处理程序来调度您喜欢的任何仿函数或仿函数列表,这并不丢人。您可以使用全局变量来存储该函子或函子列表,它并不会真正使您的程序变得更讨厌。所以,对于atexit,答案是没有意义。 -
std::call_once接受任意函数对象和任意数量的参数。至于atexit(),鉴于它保证支持的回调数量有限,我不确定“为每个创建的对象注册一个函数”是个好主意(尽管您的基本问题仍然正确)。 -
我同意几乎所有采用回调函数的 API 都应该提供一种将上下文传递给该回调的方法(在 C 中,通常的方法是通过
void*)。我不确定你的问题是什么 - 这些功能没有它,所以如果你需要使用这些功能,你会想出一些解决方法。缺少某些上下文参数是否是设计这些接口的人的疏忽?是的,它可能是。在atexit()的特定情况下,您可能需要考虑on_exit(),除非 POSIX 合规性是硬性要求。
标签: c++ posix higher-order-functions