【问题标题】:Is there a reason some functions don't take a void*?是否有某些功能不占用 void* 的原因?
【发布时间】:2015-02-15 05:22:56
【问题描述】:

许多函数接受函数指针作为参数。 atexitcall_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


【解决方案1】:

我认为atexit 只是一个特例,因为你传递给它的任何函数都应该只被调用一次。因此,它需要完成工作的任何状态都可以保存在全局变量中。如果今天正在设计atexit,它可能需要void* 以使您能够避免使用全局变量,但这实际上不会赋予它任何新功能;在某些情况下,它只会让代码稍微干净一些。

不过,对于许多 API,回调可以使用额外的参数,而不允许它们这样做将是一个严重的设计缺陷。例如,pthread_create 确实允许您传递 void*,这是有道理的,因为否则您将需要为每个线程提供一个单独的函数,并且完全不可能编写一个产生可变数量线程的程序。

【讨论】:

    【解决方案2】:

    相当多的接口采用缺少传递参数的函数指针只是来自不同的时间。但是,如果不破坏现有代码,就无法更改它们的签名。这是一种错误的设计,但事后看来很容易说。整体编程风格已经转变为在一般非函数式编程语言中有限地使用函数式编程。此外,在创建这些接口时,即使在“普通”计算机上存储任何额外数据也意味着可观察到的额外成本:除了使用的额外存储之外,即使不使用额外参数也需要传递。当然,atexit() 几乎不会成为性能瓶颈,因为它只被调用一次,但如果你在任何地方传递一个额外的指针,你肯定也会有一个 qsort() 的比较函数。

    特别是对于像atexit() 这样的东西,使用自定义全局对象是相当直接的,在该对象上注册要在退出时调用的函数对象:只需使用atexit() 注册一个函数,调用在所述全局中注册的所有函数目的。另请注意,atexit() 仅保证注册最多 32 个函数,尽管实现可能支持更多注册函数。将其用作对象清理函数的注册表而不是调用对象清理函数的函数似乎是不明智的,因为其他库也可能需要注册函数。

    也就是说,我无法想象为什么atexit() 在 C++ 中特别有用,因为无论如何对象都会在程序终止时自动销毁。当然,这种方法假定所有对象都以某种方式保持,但这通常以某种形式或其他形式无论如何都是必需的,并且通常使用适当的RAII 对象来完成。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-09
      • 2022-07-08
      • 2019-09-29
      • 2018-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-25
      相关资源
      最近更新 更多