Search code examples
c++boostsignalsboost-signals2

Replacing boost-signals2 with C++11 signals


I'm a research programmer developing autonomy systems for unmanned vehicles and I'm in the middle of modifying some open source code from another project to make a driver for vehicle control. The idea is to have a module that generally handles control that loads a vehicle-specific driver as a library, and the existing implementation uses boost::signals2 to bind the module function calls to the driver. Since this is the only thing that it's currently using boost for, I'd like to remove the boost dependency and use C++ signals, but I'm having difficulty figuring out how to do that.

I've been trying to read up on boost::signals2 and C++ signals in general to see if I can just teach myself the principles and syntax: - 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 I think that I'm getting the concepts, but the implementation is confusing the tar out of me. The first and second resources that I posted make it look like C++ signals are built in to C++11, but the tutorial in the third link seems to define its own signal class and doesn't seem to use the syntax in the first at all.

It looks like other projects have done this before (https://github.com/cinder/Cinder/issues/619), but I haven't been able to find any specific examples that show the before-and-after to compare - seeing an implementation is typically what clicks theory into place for me.

From the driver library header file:

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

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

From the driver cpp file:

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

From the module 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 );
}

The driver declares a signal that it can use to send data up to the module that loads it, and that module then binds that signal to a function so that when that signal is called and data provided to it, the data goes to that function. I'm just trying to figure out how to accomplish the same thing without using Boost, which I guess means figuring out how to replace the boost::bind as well. Any pointing in the right direction is much appreciated; I'm going to keep working on it and will post if I figure anything else out in case it's helpful to anyone going down the same road in the future.


Solution

  • The problem here is that there are two related but different technologies that are both named signals.

    The signal constructs in <signal.h> and the corresponding <csignal> are signals in the POSIX sense and are related to error handling such as when the OS kills the program or a catastrophic internal error occurs.

    Signals and slots such as Boost.signals2, Qt's signals/slots, etc. are a more general implementation of the observer pattern which allow one piece of code to offer a loosely coupled way for another piece of code to get a notification or callback when some event happens.

    The former is really a subset of the latter since POSIX signals are mostly related to error and termination events, whereas signals and slots in general also work for less ominous events like "a network packet has arrived!" or "the user clicked a button!"

    I would not recommend trying to shoehorn a general signal-slot solution into the aged (and C-style) POSIX signals API.

    While there is no standard C++ signal/slot library, there is a variety of third-party libraries, as some of your links show. I wouldn't recommend rolling your own unless you have a compelling reason to reinvent the wheel. (If you must roll your own, std::function is the obvious starting point.) Boost's is a high-quality implementation, and since you're already using it, I would stick with that. (Our rule of thumb at my company is, with all things being approximately equal, "std > Boost > Qt > other", but YMMV.)

    If you just want to reduce the footprint of your Boost folder, Boost supplies BCP to do just that.