Jason Gedge

static_assert in C++11

Posted on June 06, 2014

A few days ago I realized that some of my template code wasn’t that safe. In particular, my entity system code is heavily templated and allowing just about anything to be passed in. Of course, most things would generate invalid code and result in a compile-time error, but sometimes these error messages aren’t very useful.

static_assert is a way to make compile-time assertions about, well, anything you can compute at compile time.

So let’s say you have a piece of code that just isn’t going to work unless you’re compiling on a system that has a 32-bit word. You could simply add the following line somewhere in your code:

1
static_assert(sizeof(int) != 4, "32-bit sadface");.

Pretty neat, eh? Well, that example is somewhat contrived. For my purposes, I wanted to make sure that all template parameters in a variadic expansion had a certain base class. For example, when creating a system you can specific all of its dependent components:

1
2
3
4
5
6
template <class Derived, class ...Dependencies>
class System {}

class PhysicsSystem : public System<PhysicsSystem, Transform, Velocity> {
	// initialize/update code for system here
}

So a physics system depends on transform and velocity components and will update all entities that has both (currently I don’t support an optional dependency, but maybe that’s a good thing). The ellipsis (…) in the template declaration defines what we call a parameter pack. In my Dependencies parameter pack I want to make sure everything is a Component. Unfortunately, I had one issue: as far as I know there is no way to ask an all/any style question in static asserts. Template metaprogramming to the rescue! Here’s the all query I whipped up:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// A
template <bool ...Values>
struct all;

// B
template <bool ...Values>
struct all<true, Values...> : all<Values...> {};

// C
template <bool ...Values>
struct all<false, Values...> : std::false_type {};

// D
template <>
struct all<> : std::true_type {};

Let’s break this down into simpler components. Section A defines the templated struct that we will use for the all query. Section B specializes for the first parameter when it is true. In this case we need to keep recursing into the template until we reach the end or a false value. Section C is for the false value. In this case there’s no need to unpack any more values, so we end the recursion and inherit from a false-valued type. Section D is the sentinel case. We only reach this case when we’ve unpacked all true values, in which case we inherit from a true-valued type. A fun exercise would be to implement an any query (hint: short-circuited case gets swapped).

That’s a whole lot of words, so how do we use this to test if all of our dependent component types actually inherit from Component:

1
2
3
4
5
template <class Derived, class ...Dependencies>
class System {
	static_assert(all<std::is_base_of<Component, Dependencies>{}...>{}>,
				  "Every type in Dependencies should inherit from Component");
}

std:is_base_ofis a super useful trait class which you can find in type_traits, along with many other useful trait classes. With that static_assert in place I get a useful compile-time error message instead of either a) giving it a non-component type that happens to have the necessary concepts (not that bad), or b) getting what will likely be an unhelpful compile-time error message.

Have fun peeps!

comments powered by Disqus