Boost logo

Boost :

Subject: Re: [boost] [guidelines] why template errors suck
From: Steven Watanabe (watanabesj_at_[hidden])
Date: 2010-09-28 18:31:42


On 9/28/2010 10:53 AM, David Abrahams wrote:
> At Tue, 28 Sep 2010 10:12:29 -0700,
> Steven Watanabe wrote:
>> This would be true without C++0x concepts. Half of concepts
>> is type checking the bodies of templates when they are defined.
>> These checks are equivalent to ordinary type checking in the
>> runtime world.
> If you say so. I still don't see how. [I think you mean "analogous,"
> not "equivalent," right? They're certainly not substitutable for one
> another.]
>> The problem is that concepts force all checks into this realm.
> I don't know what you're getting at, but it might be related to this:
> the fact that concepts do early checking is problematic for many of
> our current TMP idioms, which rely on having checks only at
> instantiation time. For example, foo<mpl::_1> couldn't be written if
> foo were a constrained template, unless mpl::_1 just happened to
> satisfy its requirements.
> Anyway, yeah, that's a clash. But maybe in a "concepts" world we
> wouldn't want to do that in the first place.

Okay. Let's try this:

How do you define a concept for a fusion sequence?

First of all, this a reasonable thing to do, since there
are many algorithms that can be defined for an arbitrary
fusion sequence.

As an example, let's use the fusion::less algorithm.

template<class S>
bool less(const S& lhs, const S& rhs);

This algorithm requires that S is a FusionSequence,
and all the elements are LessThanComparable

Here's a first attempt (playing fast and loose
with syntax because I don't remember the details):


concept FusionSequence<class S> {
    // What goes here?

template<FusionSequence S>
requires LessThanComparable<???>
bool less(const S& lhs, const S& rhs);


Suppose that we use something like this:

concept FusionSequence<class S> {
     FusionSequence next_type;
     next_type pop_front(const S&);
     front_type front(const S&);
     constexpr bool empty(const S&);

This almost works, but what about the empty
sequence? pop_front for the empty sequence
has to return itself, and front has to return a dummy

auto concept LessThanComparableFusionSequence<S>
   : FusionSequence<S>

Now, how do we write the less algorithm?

template<LessThanComparableFusionSequence S>
requires empty(S)
bool less(const S& lhs, const S& rhs) {
     return false;

template<LessThanComparableFusionSequence S>
requires !empty(S)
bool less(const S& lhs, const S& rhs) {
     if(front(lhs) < front(rhs)) return true;
     if(front(rhs) < front(lhs)) return false;
     return less(pop_front(lhs), pop_front(rhs));


Okay, I've convinced myself that something
like this should actually work. What are the
problems? Well, we've just suppressed errors
when accessing past the end of the sequence.
Currently in fusion such errors would be caught
when the template is instantiated. concepts
have just forced us to suppress the error
entirely, just to make the code compile.

This is going to be even worse for Eric's
Proto-based code. For Fusion, we only
have two cases, empty and non-empty.
With proto expression trees, we have
one case for each kind of node (or more,
if we need to match more complex patterns
in a transform).


Another way to solve this would be
something like
auto concept less_impl<EmptyFusionSequence> { ... };
auto concept less_impl<FusionSequence> { ... };

This has the unfortunate side-effect of exposing
implementation details of function templates
using less.

template<class S>
requires less_impl<S>
void f(const S& arg) {
     S other = /*...*/;
     less(other, arg);

User's of f don't care that it is less internally, but
the fact of its use has to get propagated up to
all calling templates.

In Christ,
Steven Watanabe

Boost list run by bdawes at, gregod at, cpdaniel at, john at