【问题标题】:Replacing boost-signals2 with C++11 signals用 C++11 信号替换 boost-signals2
【发布时间】:2025-11-27 06:40:01
【问题描述】:

我是一名研究程序员,正在为无人驾驶车辆开发自主系统,我正在修改另一个项目的一些开源代码,以制作用于车辆控制的驱动程序。这个想法是拥有一个模块,该模块通常处理将特定于车辆的驱动程序加载为库的控制,并且现有实现使用 boost::signals2 将模块函数调用绑定到驱动程序。由于这是它目前唯一使用 boost 的东西,我想删除 boost 依赖并使用 C++ 信号,但我很难弄清楚如何做到这一点。

我一直在尝试阅读 boost::signals2 和 C++ 信号的一般知识,看看我是否可以自学原理和语法: - https://en.cppreference.com/w/cpp/utility/program/signal - https://testbit.eu/2013/cpp11-signal-system-performance - https://simmesimme.github.io/tutorials/2015/09/20/signal-slot 我认为我正在了解这些概念,但实施却让我感到困惑。我发布的第一个和第二个资源使它看起来像 C++ 信号是内置在 C++11 中的,但是第三个链接中的教程似乎定义了自己的信号类,并且似乎没有使用第一个中的语法全部。

看起来其他项目以前也这样做过 (https://github.com/cinder/Cinder/issues/619),但我无法找到任何具体示例来显示前后比较 - 看到实现通常是点击理论的内容给我的地方。

来自驱动库头文件:

class InterfaceBase
{
   InterfaceBase( const Config& cfg );
   virtual ~InterfaceBase() { }

   boost::signals2::signal<void (const InterfaceState& state)> signal_state_change;
}

来自驱动程序 cpp 文件:

void InterfaceBase::check_change_state()
{
   if( state_ != previous_state )
   {
      signal_state_change( state_ );
   }
}

来自模块 cpp:

template<typename Signal, typename Slot>
   void connect( Signal* signal, Slot slot )
{ signal->connect( slot ); }

template<typename Signal, typename Obj, typename A1>
   void connect( Signal* signal, Obj* obj, void( Obj::*mem_func )( A1 ))
{ connect( signal, boost::bind( mem_func, obj, _1 )); }

Interface::Interface()
{
   if( cfg_.has_driver_lib_path() )
   {
      driver_library_handle = dlopen( cfg_.driver_lib_path().c_str(), RTLD_LAZY );
      if( !driver_library_handle )
      {
         exit( EXIT FAILURE );
      }
   }
   else
   {
      exit( EXIT_FAILURE );
   }
   driver_ = load_driver( &cfg_ );

   connect( &driver_->signal_state_change, this, &Interface::ProcessStateChange );
}

驱动程序声明一个信号,它可以用来向加载它的模块发送数据,然后该模块将该信号绑定到一个函数,以便当调用该信号并向其提供数据时,数据会发送到那个功能。我只是想弄清楚如何在不使用 Boost 的情况下完成同样的事情,我想这意味着弄清楚如何替换 boost::bind。非常感谢任何指向正确方向的人;我会继续努力,如果我发现任何其他问题,我会发布,以防它对将来走同样道路的人有所帮助。

【问题讨论】:

    标签: c++ boost signals boost-signals2


    【解决方案1】:

    这里的问题是,有两种相关但不同的技术都被命名为信号。

    &lt;signal.h&gt; 和对应的&lt;csignal&gt; 中的信号结构是signals in the POSIX sense,并且与错误处理有关,例如当操作系统终止程序或发生灾难性内部错误时。

    Signals and slots 如 Boost.signals2、Qt 的信号/插槽等是观察者模式的更通用实现,它允许一段代码为另一段代码提供松散耦合的方式来获取通知或事件发生时回调。

    前者实际上是后者的一个子集,因为 POSIX 信号主要与错误和终止事件有关,而信号和槽通常也适用于不那么不祥的事件,例如“网络数据包已到达!”或“用户点击了一个按钮!”

    我不建议尝试将通用信号槽解决方案硬塞到陈旧(和 C 风格)的 POSIX 信号 API 中。

    虽然没有标准的 C++ 信号/槽库,但有各种第三方库,如您的一些链接所示。除非您有令人信服的理由重新发明*,否则我不建议您自己动手。 (如果你必须自己动手,std::function 是显而易见的起点。)Boost 是一个高质量的实现,既然你已经在使用它,我会坚持下去。 (我们公司的经验法则是,在所有条件大致相等的情况下,“std > Boost > Qt > other”,但 YMMV。)

    如果您只是想减少 Boost 文件夹的占用空间,Boost 提供了 BCP 来做到这一点。

    【讨论】:

    • 谢谢 - 我开始认为我在看“信号”这个词的两个不同概念,但这个解释清楚了很多。我肯定在尝试做信号和插槽的事情。这是否意味着 C++11 中没有本地方式来实现这一点,而我需要以某种方式编写自己的实现?
    • 我添加了一段关于选择一个库而不是滚动你自己的。
    • 感谢您的帮助!
    • 虽然 Boost 通常是一个高质量的库,但它的一些模块有一些奇怪的错误,比如使用 6.28318530718 来获得持续多年的 2π 近似值 double,参见例如this
    • @Ruslan Boost 与其说是一个库,不如说是一个库集合,它们具有不同程度的实用性和质量(但它们都经过精心策划,因此没有一个是完全糟糕的)。指出 Boost.Units 中的一个特定错误(这是 Boost 中相对晦涩的部分)并没有说明 Boost.Signals2。
    【解决方案2】:

    现在 C++ 标准已经取得了这样的进步,我比以往任何时候都更不愿意使用 Boost 标头。在浏览 github 时,我发现了 Boost Signals2 的替代方案:

    https://github.com/cpp11nullptr/lsignal

    虽然它的文档可以改进(不是自述文件中的完整使用示例),但它看起来简单而高效。

    【讨论】: