|
Boost : |
Subject: Re: [boost] [guidelines] why template errors suck
From: David Abrahams (dave_at_[hidden])
Date: 2010-09-28 21:04:11
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.
> 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&);
> S::front_type;
> 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
> value.
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(); }
}
#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) }
}
> 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.
I don't see where you get that, at all.
-- 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