6

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.

2 Answers2

12

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.

metal
  • 6,202
  • 1
  • 34
  • 49
  • Thank you - I was starting to think that I was somehow looking at two different notions of the word "signal," but this explanation clears it up a lot. I'm definitely trying to do the signals and slots thing. Does this mean that there is not a native way in C++11 to accomplish that, and I'm going to need to write my own implementation somehow? – Andrew Bouchard Jun 04 '19 at 13:30
  • I added a paragraph about choosing a lib versus rolling your own. – metal Jun 04 '19 at 14:28
  • Thank you for your help! – Andrew Bouchard Jun 04 '19 at 16:29
  • While Boost in general is a high-quality library, some of its modules have strange bugs like using `6.28318530718` for the `double` appriximation of 2π that last for years, see e.g. [this](https://stackoverflow.com/q/30755015/673852) – Ruslan Nov 24 '20 at 20:33
  • 2
    @Ruslan Boost is not really a single library so much as a collection of libraries, which have varying levels of utility and quality (but they're curated so none of them are totally terrible). Pointing to one particular bug in Boost.Units (which is a relatively obscure part of Boost) doesn't say anything about Boost.Signals2. – Arthur Tacca Feb 28 '21 at 10:08
3

Now that the C++ standard has made such progress, I am more reluctant than ever to use Boost headers. While browsing github, I came across this alternative to Boost Signals2 :

https://github.com/cpp11nullptr/lsignal

Although its documentation could be improved (not a full example of use in the README), it seems simple and efficient.

etham
  • 3,158
  • 2
  • 16
  • 13