Message dispatching in C++ (Part 1 – Type lists)

In the previous post, we’ve seen how not to do message dispatching.

Today we’ll build a type list.

If you remember we had an enum that held some sort of message id’s that can be used to select the proper class to unserialize on the receiving side. But what if you could do this:

and on the receiving side:

Great, but we’re not in Python or some dynamic language that can do this. Well, we’re in luck, we have C++ and templates.

First, we need a way to hold the types of messages that we want to send and receive, so we need a compile time list of some sort.

If you take a look at some functional languages, lists are built from a head and a tail, with the head being the first element in the list and the tail being another list, something like:

Let’s call the :: operator “cons” and you get a basic list (that’s how it’s built in F#, replace :: with | and you get Erlang lists and so on).

Now, let’s do this in C++:

you use it like this:

So we have a list of message types, what’s so great about it? We still need that IndexOf method.

IndexOf could be written like this:

To write our IndexOf we need a type comparator, so we can implement the equality operator:

AreEqual::value will be true if T1 is the same type as T2.

How it works: by default, value is false, but we have the partial specialization for when T1 == T2, and in that case value is true.

Next we need the ?: operator:

Ok, this needs a little more code. What it does: Select<cond, int1, int2>::value will be int1 if cond is true and int2 if cond is false.

How it works: it takes a few lines of code, but it’s not that difficult. We have template specializations again. First start off with a forward declaration of Select, then Select is specialized for the true case, then for the false case, and we’re done, the compiler does the rest.

That’s all we need, having these things lets us write our IndexOf compile time function:

This works by recursively instantiating itself. First we have a forward declaration, then the part that uses our Select and AreEqual function from above is the same thing as Cons::Head == T ? 0 : IndexOf<Cons::Tail, T>::value + 1 (notice the recursive instantiation of IndexOf). The specialization for None is there just to stop the recursion and return an error value in case the type we’re searching for is not in the list.

Let’s rewrite our example to use the new stuff instead of the enum:

In one file we can declare our messages:

Already an improvement, we removed the extra type member from our messages and with that simplified our messages, they don’t need to know anything more than how to serialize and unserialize themselves (and with boost::serialization they don’t even need that).

So when you write your message to the socket you just need:

Looks like a lot of code? It is more code than the first implementation, but you only need to write it once, and keep in mind that this is all executed at compile time, the code above would get compiled down to stream.put(1);.

Also notice that when you need to add a new message you don’t have to remember to add the type member, go change the enum and all that, you just have to add the type to the type list and you’re done. I usually define a Messages.h that includes additional headers for my messages and defines the type list.

Next time we’ll see how to get rid of the switch on the receiver.