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.

Categories