This post describes some utilities I’ve recently developed for doing concept checking in C++11. These utilities are part of an ongoing project to reimplement ranges, also for C++11, but I think the concept checking utilities are useful and interesting in their own right.

Concepts, the Saga So Far

(Feel free to skip this section if you already know what concepts are.)

The story of concept checking in C++ is long and quite dramatic. They were added to C++0x, they were hotly debated, they were ripped out (along with lots of greying hair), hands were wrung, chests beaten, sackcloth rent … Biblical stuff, truly. OK, maybe not, but it was dramatic. Anyway, there’s a new proposal to add them back, so it’s clear lots of people want concepts bad.

But let’s back up. What are concepts? In a sense, programmers have been using concepts since 1998 or even earlier, when the Standard Template Library first became a thing. You probably know what an iterator is, and you know there’s a difference between a random-access iterator, like std::vector ‘s iterators, and bidirectional iterators, like std::list ‘s. Things like “random-access iterator” and “bidirectional iterator” are concepts. Types don’t have to inherit from any special base class to be a random-access iterator. They just have to support a certain syntax and semantics. And the random-access iterator concept is a refinement of bidirectional iterator; the former supports all the syntax and semantics of the latter (e.g., increment and decrement), plus some additional stuff (e.g., being able to advance an iterator by n positions in O(1) time).

Concepts make it possible to define polymorphic algorithms: algorithms that work with objects of many different types. And they do it with very loose coupling and high performance. If your algorithm only relies on the syntax and semantics promised by the concept, then it should Just Work. And there’s the rub. Today, there’s no way to say in code that a certain algorithm requires random-access iterators, and if you pass it a bidirectional iterator, you’re sure to find out in the most unpleasant way. Hence the desire to add concept checking to the language proper.

Concepts, A New Hope?

Enough back-story. Show me the code, right? Here’s the full refinement hierarchy for the iterator concepts as defined with my utility.

struct Iterator : refines<CopyConstructible, CopyAssignable, Destructible> { // Valid expressions template<typename T> auto requires(T && t) -> decltype( concepts::valid_expr( *t, concepts::has_type<T &>(++t) )); }; struct OutputIterator : refines<Iterator(_1)> // OutputIterator<T,U> refines { // Iterator<T> template<typename T, typename O> auto requires(T && t, O && o) -> decltype( concepts::valid_expr( t++, *t = o, *t++ = o )); }; struct InputIterator : refines<Iterator, Comparable> { template<typename T> auto requires(T && t) -> decltype( concepts::valid_expr( t++, concepts::convertible(*t, *t++) )); }; struct ForwardIterator : refines<InputIterator> { template<typename T> auto requires(T && t) -> decltype( concepts::valid_expr( concepts::same_type(*t, *t++) )); }; struct BidirectionalIterator : refines<ForwardIterator> { template<typename T> auto requires(T && t) -> decltype( concepts::valid_expr( concepts::has_type<T &>( --t ), concepts::same_type(*t, *t--) )); }; struct RandomAccessIterator : refines<BidirectionalIterator> { template<typename T> auto requires(T && t) -> decltype( concepts::valid_expr( concepts::model_of<SignedIntegral>(t-t), t = t + (t-t), t = (t-t) + t, t = t - (t-t), t += (t-t), t -= (t-t), concepts::same_type(*t, t[t-t]), concepts::model_of<Orderable>(t) )); };

This might look a little strange at first glance, so let me step you through it. The first two lines…

struct Iterator : refines<CopyConstructible, CopyAssignable, Destructible>

… says that there is a concept called Iterator that refines the concepts CopyConstructible , CopyAssignable , and Destructible . Surely all iterators must support those basic operations. If the concept you want to define doesn’t refine any other concepts, you can leave that part off.

The next few lines describes the so-called valid expressions: valid syntax that all iterators must support:

template<typename T> auto requires(T && t) -> decltype( concepts::valid_expr( *t, concepts::has_type<T &>(++t) ));

You must be able to dereference an iterator and increment it, and the result of the increment operation must have type T & . This is true of all iterators. When you’re defining your concept’s valid expressions, you do so by following the above pattern: a requires member function that takes one or more objects by rvalue ref, and a trailing return type with decltype(concepts::valid_expr(/*...*/)) with your valid expressions. And that’s pretty much it for the concept definitions. There’s some utilities like has_type , same_type , and model_of for concept check-y sorts of things, but those are all details.

Concept Checking

We’ve seen what concept definitions look like, now let’s see how to use them. Imagine all the above definitions are in a concepts namespace. Let’s define some helpers for testing certain types against the concept definitions. They look like this:

template<typename T> constexpr bool Iterator() { return concepts::models<concepts::Iterator, T>(); } template<typename T, typename O> constexpr bool OutputIterator() { return concepts::models<concepts::OutputIterator, T, O>(); } template<typename T> constexpr bool InputIterator() { return concepts::models<concepts::InputIterator, T>(); } template<typename T> constexpr bool ForwardIterator() { return concepts::models<concepts::ForwardIterator, T>(); } template<typename T> constexpr bool BidirectionalIterator() { return concepts::models<concepts::BidirectionalIterator, T>(); } template<typename T> constexpr bool RandomAccessIterator() { return concepts::models<concepts::RandomAccessIterator, T>(); }

Notice how these concept checkers are constexpr Boolean functions. The concepts::models function will return true if the given type(s) model the concept, and false otherwise. Easy. And note that so far, we haven’t used a single macro because I hate macros.

Now when you are wondering whether a certain type models a concept, you can get the answer as a compile-time Boolean. Say, for instance, that you are writing something like the std::advance algorithm. You want to make sure that the two arguments are an input iterator and a integral type, respectively:

template<typename InIt, typename Diff> void advance(InIt & it, Diff d) { static_assert(ranges::Integral<Diff>(), "Diff isn't integral"); static_assert(ranges::InputIterator<InIt>(), "InIt isn't an input iterator"); // ... }

If you’re not allergic to macros, you can also do this:

template<typename InIt, typename Diff> void advance(InIt & it, Diff d) { CONCEPT_ASSERT(ranges::Integral<Diff>()); CONCEPT_ASSERT(ranges::InputIterator<InIt>()); // ... }

(As you can see, in my code all the concept checking function are in the ranges namespace.) This is pretty nice. If someone calls advance with the wrong types, they’ll get a sensible error message. But maybe you want something else. Maybe there are lots of advance functions, and you want this overload to silently go away if the types don’t model the concepts. Then you can do this:

template<typename InIt, typename Diff, typename = concepts::requires_t< ranges::Integral<Diff>() && ranges::InputIterator<InIt>()>> void advance(InIt & it, Diff d) { // ... }

This uses SFINAE to make the advance function disappear when the concept requirements are not satisfied. That works, but it’s getting a bit ugly. Maybe it’s better to hold our noses and use a macro:

template<typename InIt, typename Diff, CONCEPT_REQUIRES(ranges::Integral<Diff>() && ranges::InputIterator<InIt>())> void advance(InIt & it, Diff d) { // ... }

I hate macros, but I can live with that.

Concept-Based Overloading

If you know anything about std::advance , you might know why I picked it as an example. advance advances an iterator by a certain number of positions. Most iterators must be bumped forward n times, which is slow. But if an iterator is random-access, you can just add n to it and be done. How would you achieve that with my new concept-checking utilities?

In C++98, this is accomplished with iterator tag types and tag dispatching. Unfortunately, tag dispatching is still the best we can do in C++11, which is why we really need a language feature. But with my code, becomes quite a bit easier. The concept definitions can themselves be used as tags. Let’s see how.

The first question to answer is, given an iterator type, what is the most refined iterator concept that it models? For a type like int* it should be RandomAccessIterator , but for std::list::iterator it should be BidirectionalIterator . You can get that information with the help of a utility called most_refined_t . Here we use most_refined_t to implement an iterator_concept_t alias that tells you what concept an iterator type models:

template<typename T> using iterator_concept_t = concepts::most_refined_t< concepts::RandomAccessIterator, T>;

most_refined_t does a breadth-first search of the refinement hierarchy rooted at concepts::RandomAccessIterator , looking for the most refined concept modeled by type T . Here’s how we can use it to optimally implement advance :

// Random-access iterators go here template<typename RndIt, typename Diff> void advance_impl(RndIt & it, Diff d, ranges::concepts::RandomAccessIterator) { it += d; } // All other iterator types go here template<typename InIt, typename Diff> void advance_impl(InIt & it, Diff d, ranges::concepts::InputIterator) { for(; d != 0; --d) ++it; } template<typename InIt, typename Diff, CONCEPT_REQUIRES(ranges::InputIterator<InIt>() && ranges::Integral<Diff>())> void advance(InIt it, Diff d) { advance_impl(it, d, ranges::iterator_concept_t<InIt>{}); }

As you can see, concept-based overloading is accomplished by dispatching to the proper implementation based on the concept that a particular type models. This all works just based on the concept definitions which, if you recall, only required you to specify the refinements and the valid expressions declaratively. You didn’t have to define any separate tags or any traits or metafunctions. Not shabby.

What’s Missing?

The big missing piece of this puzzle is the ability to automatically check an algorithm against the requires clauses. It’s all well and good that the advance algorithm says it needs only input iterators. But what if its implementation actually makes some other assumption? You wouldn’t know until you tried to call the algorithm with a type that doesn’t satisfy the assumption. That’s the state of the art, I’m afraid, and there’s nothing I can do about it. Sorry.

Making the Abstract Concrete

My concept-checking library isn’t perfect. It’s really a pale approximation of what true language support would be like. Heck, it isn’t even a library yet. But in my limited experience using this utility in my range code so far, it has real benefits. I can create rich overload sets and tune which overload gets selected by simply declaring what concepts the types must model. And defining the concepts is easy. Fun, even. It gives me more confidence when writing generic code that I’m actually going to get the behavior I expect.

So, if you like, leave me a comment with your thoughts. Would you find this useful? Is there a direction you’d like to see this go? Should I try (in my ample free time ) to turn this into a proper library, possibly as a modern replacement for Boost.Concept_check? Give me your thoughts.

For reference, you can find the (woefully under-commented and undocumented) code here.