Category: Programming (Page 1 of 2)

Revisiting signal handlers once more

Posted on November 14, 2021, under Programming, Whisker Menu

This is a bit of a long post about a purely technical change inside of Whisker Menu. I quite enjoyed solving it, but the change is invisible to end users. All code is under the GNU GPL version 2 or later, same as Whisker Menu.

The adventure begins

I have been reasonably content with how I handle connecting GLib signals to C++ member functions in Whisker Menu. A while ago I even switched back to C++11 and variadic templates as that standard is old enough now that everybody supports it quite well. It makes the code much cleaner behind the scenes.

Recently, though, I was inspired to revisit the signal handlers in Whisker Menu after discovering that the other Xfce plugins written in C++ use lambdas. That was an intriguing idea and got me thinking about how I could do it myself. After all, I like using lambdas in my Qt projects.

Why use lambdas if I am okay with the current code? Well, lambdas let you capture variables, so I don’t need to write extra functions to bind variables. Putting the signal handling code directly next to the signal itself makes it more clear what will happen in response to that signal. And, for the most part, my signal handlers are tiny and having to scroll around in the document to add or modify them is frustrating (especially the settings dialog, which has a ton of one-line signal handlers).

There are still some cases where a lambda will call a separate function, but through the wonders of inlining those calls will frequently be replaced by the compiler with the function they are calling. In fact, looking at the assembly output, the entire lambda is usually inlined in the end, so it is almost like having a C signal handler. But I am getting ahead of myself.

So! How to connect a lambda to a GLib signal?

The first solution

The simplest approach doesn’t require extra shared code!

class Example
{
public:
	Example()
	{
		GtkWidget* widget = gtk_button_new();
		g_signal_connect(G_OBJECT(widget), "button-press-event",
			G_CALLBACK(
				+[](GtkWidget*, GdkEvent*, gpointer user_data) -> gboolean
				{
					Example* example = static_cast<Example*>(user_data);
					… do stuff …
					return GDK_EVENT_PROPAGATE;
				}
			),
			this);
	}
};

Yeah, no. Static casts to get an equivalent "this" pointer" and no binding variables. Yikes. It works, but I don’t think it is very readable. Definitely not something I would want to work with for all the signal handlers in Whisker Menu. This provides nothing over the current approach, and it has definite drawbacks.

Templates to the rescue!

It wasn’t hard to modify my current signal handler helper code to end up with something that stored and called lambdas, but it unfortunately could not deduce the types for me. So I ended up having to write the types out twice. Yuck. It was a good start, but I needed to sit on it for a few days.

enum class Connect
{
	Default = 0,
	After = G_CONNECT_AFTER
};

template<typename R, typename... Args, typename Sender, typename Func>
gulong connect(Sender instance, const gchar* detailed_signal, Func func, Connect flags = Connect::Default)
{
	class Slot
	{
		Func m_func;

	public:
		Slot(Func func) :
			m_func(func)
		{
		}

		static R invoke(Args... args, gpointer user_data)
		{
			return static_cast<Slot*>(user_data)->m_func(args...);
		}

		static void destroy(gpointer data, GClosure*)
		{
			delete static_cast<Slot*>(data);
		}
	};

	return g_signal_connect_data(G_OBJECT(instance),
			detailed_signal,
			G_CALLBACK(&Slot::invoke),
			new Slot(func),
			&Slot::destroy,
			GConnectFlags(flags));
}

class Example()
{
	Example()
	{
		GtkWidget* widget = gtk_button_new();
		connect<gboolean, GtkWidget*, GdkEvent*>(widget, "button-press-event",
			[](GtkWidget*, GdkEvent*) -> gboolean
			{
				… do stuff …
				return GDK_EVENT_PROPAGATE;
			});
	}
};

Solved!

I was certain that the compiler had to be able to find the types itself. Maybe I could make the Slot class a partial specialization? That would allow me to not specify the types in the template of the connect() function. But how to get the compiler to determine the types for the partial specialization?

After some research, I figured it out. Use decltype on the operator() function of the lambda! Since lambdas are basically functors built into the language, they all have an operator() function that is called behind the scenes to make the magic work. And decltype will get me the return type and argument types!

enum class Connect
{
	Default = 0,
	After = G_CONNECT_AFTER
};

template<typename Func, typename T>
class Slot
{
};

template<typename Func, typename R, typename T, typename... Args>
class Slot<Func, R(T::*)(Args...) const>
{
	Func m_func;

public:
	Slot(Func func) :
		m_func(func)
	{
	}

	static R invoke(Args... args, gpointer user_data)
	{
		return static_cast<Slot*>(user_data)->m_func(args...);
	}

	static void destroy(gpointer data, GClosure*)
	{
		delete static_cast<Slot*>(data);
	}
};

template<typename Sender, typename Func>
gulong connect(Sender instance, const gchar* detailed_signal, Func func, Connect flags = Connect::Default)
{
	typedef Slot<Func, decltype(&Func::operator())> Receiver;

	return g_signal_connect_data(G_OBJECT(instance),
			detailed_signal,
			G_CALLBACK(&Receiver::invoke),
			new Receiver(func),
			&Receiver::destroy,
			GConnectFlags(flags));
}

class Example()
{
	Example()
	{
		GtkWidget* widget = gtk_button_new();
		connect(widget, "button-press-event",
			[](GtkWidget*, GdkEvent*) -> gboolean
			{
				… do stuff …
				return GDK_EVENT_PROPAGATE;
			});
	}
};

Astute readers have probably noticed that I am seemingly unnecessarily specifying the return type of the lambdas. It is true that C++ will happily deduce it for me, but I want to make sure that the function signatures exactly match the signal handlers since GLib casts everything to void*.

This was a lot of fun to figure out, and I am quite happy with it. Much happier than I was with the old signal handler code!

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.

One of the things I had not given a second thought to when writing Whisker Menu was my use of C++11. The new standard makes C++ much cleaner, and any Linux distro that has Xfce 4.8 or 4.10 mostly likely also has a version of GCC at least as recent as 4.6 (which is the earliest version of GCC that can compile the bits of C++11 I used).

However, not everybody is using Linux. There are a lot of BSD users out there who don’t yet have LLVM/Clang, and so in an effort to make my menu usable by as many people as possible I have downgraded to C++98. Some of the things I will miss most from C++11 are rvalue references (no more passing objects by const reference! yay!), range-based for, and the repurposed auto keyword:

C++11:

LauncherModel example(std::map<std::string, Launcher*> sorted_items)
{
	LauncherModel model;
	for (auto i : sorted_items)
	{
		model.append_item(i.second);
	}
	return model;
}

C++98:

void example(const std::map<std::string, Launcher*>& sorted_items, LauncherModel& model)
{
	for (std::map<std::string, Launcher*>::const_iterator i = sorted_items.begin(), end = sorted_items.end(); i != end; ++i)
	{
		model.append_item(i->second);
	}
}

For those who have never seen the new C++11 constructs, auto is a variable whose type is determined at compile time. That makes the code for dealing with STL iterators a lot cleaner. The range-based for is also nice for reducing code, and it even works with anything that provides begin() and end() functions!

Another change I have made is how I handle the signal callbacks. I had previously decided to use macros to hook up the GTK+ signals to C++ classes. However, I have since found the obfuscation of what is really going on to be frustrating at times. Because of that I have taken the macros out and replaced them with what they were actually doing, even if it does bloat the header files somewhat.

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.

Now that I am switching to release branches I am finally going to also tackle something that has always bothered me but I’ve never taken the time to solve: the development source code has the same version number as the most recent release. I have always wanted it to be some sort of automated number, but I wasn’t sure what I wanted it to be. And I didn’t want to have to update it myself with every single commit.

At first I had wondered about a number that gets incremented every time you compile the project, but I quickly realized that was a pointless thing to track. I may build a project 5,000 times and have a huge build number, but someone else might download the source and compile it only twice. Same source code, different version number. That’s frankly pretty silly.

Instead, I got inspired by the idea of using the git revision ID. It is obviously unique for each commit, and it identifies the specific source code for everyone. Of course, you can’t embed the revision ID because it is a hash that is created of the source code that you’re trying to embed it in. A hash that contains itself? Impossible! Of course, all you have to do instead is simply ask git for the revision ID, and pass that as a definition to the compiler:

VERSION = $$system(git rev-parse --short HEAD)
DEFINES += VERSIONSTR=\\\"git.$${VERSION}\\\"

The source code also needs to make use of the new compiler definition:

QCoreApplication::setApplicationVersion(VERSIONSTR);

This means that I finally have an automated version number for the development source code. I’ve only updated Kapow so far, but I am going to make this change to all of my projects.

Categories