Too Much Code Signalled

This was posted over on the ISO C++ blog the other day. It’s an interesting exercise in the use of various new C++ 11/14 features like variadic templates, but overall it left a bad taste in my mouth. There just seems like there’s too much code being slung around for what is being accomplished. And its not just the implementer of the library that is slinging too much code — the user of the library gets roped into the excessive code slinging as well.

This got me thinking back to how we dealt with the thread unsafety of the original boost::signals library. Before boost::signals2 you could get all sorts of undefined behavior if the registering of listeners and the emission of the signal were going on in multiple threads. To get around this we wrapped the boost::signal class in our own class called thread_safe_signal that inserted the locking of a mutex before each connection and emission attempt. We took things a step further and also addressed what I saw a a deficiency in the interface of boost::signal: the lack of a way to separate the concerns of connection and emission. Really, the only concern is making it possible to publicly expose the ability to connect to the signal without also exposing the ability to emit the signal. To do this we created a signal_connector class that could be used like so

class some_class
{
public:
   signal_connector<void (int)> event;
   
private:
   thread_safe_signal<void (int)> m_event;
};

Anyone could use the connect method on event to connect to the signal, but only the some_class object had access to the real signal (m_event) in order to emit it.

The implementation of signal_connector is simple, especially now that we have r-value references and perfect forwarding (only having access to C++98 meant that the original implementation had to jump through a few more hoops):

template <typename Signature>
class signal_connector
{
public:
   explicit signal_connector (boost::signals2::signal<Signature>& s)
   {
      m_signal_p = &s;
   }
   
   template <typename F>
   boost::signals2::connection operator() (F&& f)
   {
      return m_signal_p->connect (std::forward<F> (f));
   }
   
private:
   boost::signals2::signal<Signature>* m_signal_p;
};

And to use it one does

class some_class
{      
public:
   signal_connector<void(int)> connect_to_signal;

   some_class ():
      connect_to_signal (m_signal)
   {}

   void some_method_that_emits_the_signal ()
   {
      //Do some stuff and then emit the signal
      // ...
      m_signal (529);
   }
   
private:
   boost::signals2::signal<void (int)> m_signal;
};

void do_some_connecting (some_class& sc)
{
   auto conn = sc.connect_to_event ([](int i) {std::cout << "got event " << i <<std::endl;});
}

So instead of having to create an observers enumeration/class doohickey and modify your inheritance hierarchy you could instead simply embed a few objects (one for each signal) in the public part of your interface and connect them to the underlying signal objects in your constructor. I guess this could be considered cheating a little bit since instead of an actual method we instead have a function object in our interface and this does require the client of the class to know what a signal_connector is. But given the much smaller amount of code involved I feel like that’s a fair trade-off. And really, if someone knows what a signal is (as they must if they know that they want to connect to it) then it should be relatively obvious what a signal_connector is.

[Update:]  There is one disadvantage of signal_connector over having actual methods as signal_connector is implemented above. If the object that the signal_connector is embedded in is copied then the signal_connector in the new object will be connected to the signal in the old object (the default copy constructor/operator will just copy the m_signal_p member of signal_connector). This is easily fixed by using the usual methods to make signal_connector uncopyable. Doing so makes signal_connector a bit more difficult to use as making a class that has signal_connector members copyable will require some more work (one can’t just use the default copy constructor and operator).

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: