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.