Revisiting signal handlers

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 though 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 becuase 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.

Advertisements

Comments are closed.