Simpler UI Code With Signals and Slots
Posted by Patrick on Saturday, December 12, 2009Sat, Dec 12, 2009

Most of my game development on the iPhone is done in C++, not Objective C. I feel like this has its benefits, but it also has drawbacks, not the least of which is that programming user interfaces can get really messy. The Cocoa delegates model is wonderfully powerful for that kind of thing. The easiest model I've seen in C++ is the signals and slots model used, most notably, by QT.

My game UI is created from scratch with OpenGL and it's really fast, but I didn't want to have a very complicated or heavy event system. Events need to get to other UI elements, but also the various game objects. Oh, if only I had delegates! How could I have my cake and eat it?

I was delighted to stumble across a very efficient way to do delegates in C++. Essentially the delegate can bind to and call anything with a matching signature. As a rough example:

void GlobalFunction( int x, float y ) {}

class MyClass
{
public:
    void NonVirtualMethod( int x, float y );
    virtual void VirtualMethod( int x, float y );
    static StaticMethod( int x, float y );
}

...

Delegate2 delegate< int, float >;
delegate.Bind( &GlobalFunction );
delegate( 10, 2.5f );

// Similarly, you can bind these:
MyClass obj;
delegate.Bind( &obj, &MyClass::NonVirtualMethod );
delegate.Bind( &obj, &MyClass::VirtualMethod );
delegate.Bind( &MyClass::StaticMethod );

Delegates can only be bound to a single function or method (although a single function can be bound by many delegates). The signals and slots model extends this to a many-to-many relationship. A signal is really just a delegate that can bind to multiple functions or methods at once. A slot is the function being bound to. For simplicity any scope visible function or method can be a slot. Here's a slightly contrived example of how to use it:

class Button
{
    ...
public:
    Signal0< void > onClick;
};

I can then bind that onClick signal to anything I want to call when the button is clicked.

quitButton.onClick.Connect( &gameManager, &GameManager::StopGame );

Of course you can also disconnect a slot at any time. The ability for objects manage their connections on-the-fly is quite powerful and made my (admittedly simple) UI very painless to implement.

I have made my signals implementation available on GitHub. It consists of two header files and the only dependency is std::set.

  • Extremely efficient (based on delegates that generate only two lines of ASM code).
  • No inheritance required.
  • No third-party preprocessor required.
  • Works seamlessly with global functions, object methods, virtual methods and static class methods.
  • Very portable. This should work on any reasonable C++ compiler.
  • Really simple to drop in and use.
  • Implemented fully in templates.

Download it from GitHub. Use it for whatever.