Revisiting signal handlers

Posted on November 24, 2013, under Programming, Whisker Menu

Ever since I changed how I handle connecting GTK+ signals to C++ members I have never been completely happy with that code. I prefer to not repeat myself, and I like the power and simplicity that templates can bring (and the safety they have over macros). I thought that any GCC before 4.7 would not allow me to write the signal handlers the way I want, but it turns out I was wrong!

Based on the load time error of Whisker Menu 1.0.0 I had surmised that there was a bug in GCC where you can’t take the address of a template function because it did not handle the name mangling properly. However, upon further research I discovered that the bug really is that if you take the address of a template function and cast it to something else before storing it the template is not instantiated. Easy enough to fix: just create a function pointer of the correct type and then cast that:

template <typename T, typename R, typename A1, typename A2>
gulong g_signal_connect_slot(gpointer instance,
		const gchar* detailed_signal,
		R (T::*member)(A1,A2),
		T* obj,
		bool after = false)
{
	class Slot
	{
		T* m_instance;
		R (T::*m_member)(A1,A2);

	public:
		Slot(T* instance, R (T::*member)(A1,A2)) :
			m_instance(instance),
			m_member(member)
		{
		}

		static R invoke(A1 a1, A2 a2, Slot* slot)
		{
			return (slot->m_instance->*slot->m_member)(a1, a2);
		}

		static void destroy(Slot* slot)
		{
			delete slot;
		}
	};
	R (*invoke_slot)(A1,A2,Slot*) = &Slot::invoke;
	void (*destroy_slot)(Slot*) = &Slot::destroy;

	return g_signal_connect_data(instance,
			detailed_signal,
			reinterpret_cast<GCallback>(invoke_slot),
			new Slot(obj, member),
			reinterpret_cast<GClosureNotify>(destroy_slot),
			after ? G_CONNECT_AFTER : GConnectFlags(0));
}

class Example
{
public:
	Example();
	gboolean button_press_event(GtkWidget* widget, GdkEventButton* event);
};

Example::Example()
{
	g_signal_connect_slot(widget, "button-press-event", &Example::button_press_event, this);
}

The code is not quite as simple as the original version, though, because variadic templates are only available in C++11. Instead I have to make make overloads of the function for each amount of parameters I want to support. I did clean it up in other ways by using local classes, which I think makes the new template slot handlers more clear. It even compiles to less space than using class functions for slots.

Categories