Boost logo

Boost :

Subject: Re: [boost] [guidelines] why template errors suck
From: Steven Watanabe (watanabesj_at_[hidden])
Date: 2010-10-13 13:09:39


  AMDG

Sorry for the delayed response.

On 9/28/2010 6:04 PM, David Abrahams wrote:
> At Tue, 28 Sep 2010 15:31:42 -0700, Steven Watanabe wrote:
>> 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.
> Fantastic example!
>
>> As an example, let's use the fusion::less algorithm.
> That would be great, except that I can't find such an algorithm in
> http://www.boost.org/doc/libs/1_44_0/libs/fusion/doc/html/index.html,
> and how the rest of this conversation goes would depend on its
> semantics. For example, does it do a full lexicographic compare, or
> must the two sequences have the same length? If you mean what's in
> boost/fusion/sequence/comparison/less.hpp, it's the former.

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>;
> };
>
> #if THE_VERBOSE_NON_TRICKY_WAY
>
> auto concept EmptyFusionSequence<class S> : FusionSequence<S>
> requires SameType<S::size_type,int_<0> >
> {};
>
> template<EmptyFusionSequence S1, EmptyFusionSequence S2>
> concept_map LessThanComparable<S1,S2>
> {
> constexpr operator<(S1 const&, S2 const&) { return false; }
> }
>
> template<NonEmptyFusionSequence S1, EmptyFusionSequence S2>
> concept_map LessThanComparable<S1,S2>
> {
> constexpr operator<(S1 const&, S2 const&) { return false; }
> }
>
> template<EmptyFusionSequence S1, NonEmptyFusionSequence S2>
> concept_map LessThanComparable<S1,S2>
> {
> constexpr operator<(S1 const&, S2 const&) { return true; }
> }
>
> #else
>
> // 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?

> #endif
>
>
> 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. 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.

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?

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?

In Christ,
Steven Watanabe


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