← Back to team overview

unity-dev team mailing list archive

[Ayatana-dev] [C++] GObject Signals in Unity

 

Hey,

I've just merged my lp:~njpatel/unity/nicer-glib-signals branch into Unity trunk. With it comes a nice wrapper to help deal with a annoying little problem we have at the moment with how our C++ code works with GObject:

Currently, when we need to connect to a signal on an object inside a class, we end up doing something like this:


and then MyClass looks like this:

class MyClass
{
  // <snip>
   MyClass()
   {
     awesome_happened_id = g_signal_connect (my_object_,
	                                     "awesome-happened",
G_CALLBACK (MyClass::OnMyObjectAwesomeHappened),
                                             this);
     // repeat for every signal connection
   }

  ~MyClass()
  {
    if (G_IS_OBJECT (my_object))
      g_signal_handler_disconnect (my_object_, awesome_happened_id_);
  }

  void MyClass::OnMyObjectAwesomeHappenedReal (int level_of_awesome)
  {
    //do the real work
  }

  static void MyClass::OnMyObjectAwesomeHappened (MyObject *my_object,
                                                  int level_of_awesome,
                                                  MyClass *self)
  {
    // Static function so we can't access 'this', and need to use the
    // passed in parameter instead
    self->OnMyObjectAwesomeHappenedReal (level_of_awesome);
  }

private:
  guint awesome_happened_id;
};

Having to save the connection id, remember to disconnect, only use static members etc etc is annoying. It also is a great way to cause a crash if you forgot to disconnect a handler on destruction.

So, to make things easier and hopefully a lot cleaner and safer, we now have the Signal and SignalManager objects inside UnityCore/GLibSignal.h

The way you use these are as follows:

class MyClass
{
public:
  MyClass()
  {
    // Order of templates is <RETURN, GOBJECT_TYPE, ARGO, ARG1 etc etc>
    // ARG0 etc are optional depending on the signal, of course :)
    signal_manager_.Add(
      new Signal<void, MyObject*, int>(my_object_,
                                       "awesome-happened",
sigc::mem_fun(this, &MyClass::OnMyObjectAwesomeHappened));


  }

  // No need to have a destructor just for the signal, signal-manager
  // will automatically disconnect all the signals it is managing when
  // it destructs

  void MyClass::OnMyObjectAwesomeHappened(MyObject *object,
                                          int level_of_awesome)
  {
    // Non-static, so just do what you want
    awesome_happened.emit (level_of_awesome);
  }

private:
  SignalManager signal_manager_;
}


You can store individual connections as Signal objects inside your class if your doing some manual management of disconnections etc too, have a look at tests/test_glib_signals.cpp for a test suite that covers all the API!


So far, I've only changed UnityCore to use these, but I think we should start switching over the rest of Unity to use them too, so we can do a quick review of the existing code as well as delete a whole bunch of code!

NOTE: Further to Tim's email about gboolean/bool, there is no magic in Signal* to do the conversion, so make sure that if you expect a or return a gboolean, you use that type and not the C++ bool type (again, this can be seen in the tests).

--
Neil Jagdish Patel | Technical Lead
Desktop Experience Team
Canonical
27 Floor, Millbank Tower
London SW1P 4QP
Ubuntu - Linux for Human Beings
www.canonical.com