Internal changes to Whisker Menu
Posted on June 24, 2013, under Programming, Whisker Menu
When I wrote Whisker Menu I was viewing it as a pet project. I wasn’t planning to share it, and once I decided to make it public I was not expecting that many people to use it. However, I was very much mistaken about how many people would like to try it out, and this is going to require me to reconsider some assumptions I made when writing it!
There is one thing I have changed already. I had come up with a simple hack to get GTK+ signals to connect to C++ member functions by using variadic templates to generate functions that could be called by C. I was quite pleased with how straightforward it was:
template <typename T, typename R, typename... Args>
struct SlotArgs
{
T* instance;
R (T::*member)(Args...);
};
template <typename T, typename R, typename... Args>
R invoke_slot_args(Args... args, SlotArgs<T,R,Args...>* slot)
{
return (slot->instance->*slot->member)(args...);
}
template <typename T, typename R, typename... Args>
void delete_slot_args(SlotArgs<T,R,Args...>* slot)
{
delete slot;
}
template<typename T, typename R, typename... Args>
gulong g_signal_connect_slot(gpointer signal_obj, const gchar* detailed_signal, R (T::*member)(Args...), T* instance)
{
return g_signal_connect_data(signal_obj, detailed_signal,
(GCallback)&invoke_slot_args<T,R,Args...>,
new SlotArgs<T,R,Args...>{instance, member},
(GClosureNotify)&delete_slot_args<T,R,Args...>,
GConnectFlags(0));
}
class Example
{
public:
Example();
gboolean on_button_press_event(GtkWidget* widget, GdkEventButton* event);
};
Example::Example()
{
g_signal_connect_slot(widget, "button-press-event", &Test::on_button_press_event, this);
}
This works because the calling convention between C and C++ on Linux is the same by default and the only thing you have to worry about is name mangling. I tested it on both GCC and Clang, and it worked perfectly. On my very up-to-date Arch system, that is. Cue dramatic music. 😛 So of course there is a bug in GCC 4.6 and earlier where it does not properly handle the name mangling of template functions. Oops! I have rewritten things to use macros and static member functions instead:
#define SLOT_CALLBACK(klassmember) G_CALLBACK(klassmember ## _slot)
#define SLOT_2(R, klass, member, A1, A2) \
static R member ## _slot(A1 arg1, A2 arg2, klass* obj) \
{ return obj->member(arg1, arg2); } \
R member(A1 arg1, A2 arg2)
class Example
{
public:
Example();
SLOT_2(gboolean, Test, on_button_press_event, GtkWidget*, GdkEventButton*);
};
Example::Example()
{
g_signal_connect(widget, "button-press-event", SLOT_CALLBACK(Test::on_button_press_event), this);
}
It is not as easy to read, though, because instead of having actual member functions in the header file they are generated by a macro. And it does require a different macro for each number of arguments I want to support. Still, it works out to be the same thing in the end, and it does have the potential to be slightly more efficient.
Obviously, I could write out the code generated by the macro, but that would get repetitive and boring quite fast. And then if I want to change a function’s signature I would have to make sure to update both functions to match. Annoying! So, macros it is.