Boost logo

Boost :

Subject: Re: [boost] [guidelines] why template errors suck
From: David Abrahams (dave_at_[hidden])
Date: 2010-10-31 22:48:01


At Wed, 13 Oct 2010 10:09:39 -0700,
Steven Watanabe wrote:
>
> AMDG
>
> Sorry for the delayed response.

Back atcha! Been looking forward to getting back to this thread.

> >> As an example, let's use the fusion::less algorithm.
>
> I was actually using a simplified version that requires
> both sequences to be the same type. (And thus the
> same length). A full lexicographical comparison is
> fine though.
>
> > Close. What you need is a refined concept for NonEmptyFusionSequence
> > that includes front_type and pop_front. Just riffing here, but this
> > could work:
> >
> > concept FusionSequence<class S>
> > {
> > CompileTimeInt size_type = int_<0>;
> >
> > constexpr bool empty(const S&) { return size_type::value; }
> > constexpr std::size_t size(const S&) { return size_type::value; }
> > }
> >
> > auto concept NonEmptyFusionSequence<class S>
> > : FusionSequence<S>
> > {
> > FusionSequence next_type;
> > next_type pop_front(const S&);
> >
> > typename front_type;
> > front_type front(const S&);
> >
> > CompileTimeInt size_type = int_<next_type::size_t::value+1>;
> > };
> >
> > // A shorter way. Because of refinement, this one will only get
> > // used when either S1 or S2 is empty.
> >
> > template<FusionSequence S1, FusionSequence S2>
> > // a constraint you could add for clarity, but don't need
> > // requires SameType<int_<0>, int_<S1::size_type()*S2::size_type()> >
> > concept_map LessThanComparable<S1,S2>
> > {
> > constexpr operator<(S1 const&, S2 const&)
> > { return S1::size_type()< S2::size_type(); }
> > }
>
> Isn't the extra constraint needed to make sure
> that trying to compare sequences whose elements
> are /not/ LessThanComparable won't silently misbehave?

I see what you mean; I overlooked that issue. Technically the correct
answer is that I guess it depends on your desired semantics; as I
coded it, it would mean

  make_tuple(1, 2) < make_tuple("a", "b", "c")

is that misbehavior? Well, I guess it's not what tuple is currently
specified to do, so maybe it is.

> > template<NonEmptyFusionSequence S1, NonEmptyFusionSequence S2>
> > requires LessThanComparable<S1::front_type,S2::front_type>
> > && LessThanComparable<S1::next_type,S2::next_type>
> > concept_map LessThanComparable<S1,S2>
> > {
> > operator<(S1 const& x, S2 const& y)
> > { return front(x)< front(y) || pop_front(x)< pop_front(y) }
> > }
>
> Okay. Here's why I don't like this.
>
> Suppose that I define a second algorithm with
> exactly the same requirements.

So the first algorithm is fusion::less. If you accept the above
concept_maps, fusion::less would just look like:

     template <FusionSequence S1, FusionSequence S2>
       requires LessThanComparable<S1,S2>
     bool less(S1 const& x, S2 const& y)
     {
         return x < y;
     }

> As an example,
> I'll use what fusion::less actually does. (It doesn't
> quite do a lexicographical comparison. It truncates
> to the length of the shorter sequence.) This may
> be slightly contrived, but I'm sure you'll agree that
> there exist algorithms with identical requirements,
> neither one of which can be easily implemented
> in terms of the other.

Granted.

Okay, so that would be

     template <FusionSequence S1, FusionSequence S2>
       requires LessThanComparable<S1,S2>
     bool what_fusion_less_actually_does(S1 const& x, S2 const& y)
     {
         ...
     }

Right? Same requirements.

> Now, suppose that I want to write a function
> template<FusionSequence S1, FusionSequence S2>
> void foo(S1, S2);
> which calls both of these algorithms. How can I
> write the requirements for foo?

     template <FusionSequence S1, FusionSequence S2>
       requires LessThanComparable<S1,S2>
     bool foo(S1 const& x, S2 const& y)
     {
         return less(x,y) && what_fusion_less_actually_does(x,y);
     }

> a) requires LessThanComparable<S1, S2> &&
> TruncatingLessThanComparable<S1, S2>
> Is every function template that calls foo also going to
> have to list both of these? This is silly and makes foo
> difficult to change, because the concept requirements
> include extraneous information. Remember that the
> two sets of types that model these concepts are the same.
> b) Leave it with just FusionSequence.
> I hope this won't compile unless you have a concept_map
> LessThanComparable<FusionSequence, FusionSequence>
> which is a bad idea, as I explained above.
> c) Somehow define a concept that allows both LessThanComparable
> and TruncatingLessThanComparable to be deduced. This
> would be ideal, but I don't have the least idea how to accomplish it.
> d) Something else I haven't thought of?

I don't understand why you'd go down any of those roads. Am I missing
something?

-- 
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk