Subject: Re: [boost] [guidelines] why template errors suck
From: Daniel Walker (daniel.j.walker_at_[hidden])
Date: 2010-09-28 18:42:27


On Tue, Sep 28, 2010 at 3:36 PM, Daniel Walker
<daniel.j.walker_at_[hidden]> wrote:
> On Tue, Sep 28, 2010 at 1:48 PM, joel falcou <joel.falcou_at_[hidden]> wrote:
>> On 28/09/10 19:37, David Abrahams wrote:
>>>
>>> This BehaveAsPair thing doesn't identify any real abstraction.  I
>>> promise you're not going to come across any uses for BehaveAsPair
>>> other than in solving the exact implementation problem you're
>>> attacking.
>>>
>>
>> I see
>>>
>>> In short, it lacks "truthiness." :-)
>>>
>>
>> Agreed
>>>
>>> Was there something unsatisfying about the solution I posted in
>>> http://permalink.gmane.org/gmane.comp.lib.boost.devel/209012 ?
>>>
>>
>> Nope, I just missed it.
>
> Oh wait! I think I have another example that's more truthy. :) I'll
> use Boost.ConceptCheck to flesh it out. Let's define the concept of an
> AdditivePair to represent the arguments to binary addition. To model
> the concept a type needs to support first(x), second(x) and add(x) for
> an object x where all three functions have integer values. So, the
> (simplest) concept checking class would be:
>
> template <class T>
> struct AdditivePair {
>    BOOST_CONCEPT_USAGE(AdditivePair)
>    {
>        int a = first(x);
>        int b = second(x);
>        int c = add(x);
>    }
>    T x;
> };
>
> We can now write Eric's function using concept requirements.
>
> template<class T>
> BOOST_CONCEPT_REQUIRES(
>    ((AdditivePair<T>)),
>    (int)
> ) sum_ints( T const & t )
> {
>    return add( t );
> }
>
> We can now adapt std::pair<int,int> to model the AdditivePair concept.
>
> int first(std::pair<int,int> x) { return x.first; }
> int second(std::pair<int,int> x) { return x.second; }
> int add(std::pair<int,int> x) { return first(x) + second(x); }
>
> And we can also adapt int to model the AdditivePair concept; i.e. int
> is an additive pair where the second member is 0.
>
> int first(int x) { return x; }
> int second(int x) { return 0; }
> int add(int x) { return x; }
>
> That seems pretty natural to me.

Crap. I just realized that Eric's original function was recursive!
Doh! Let me try that again. To handle recursive pairs the AdditivePair
concept needs a pair_traits class, which will determine the return
types of first(x) and second(x). The concept checking class is almost
the same as before:

template<class> struct pair_traits;

template <class T>
struct AdditivePair {
    BOOST_CONCEPT_USAGE(AdditivePair)
    {
        typename pair_traits<T>::first_type a = first(x);
        typename pair_traits<T>::second_type b = second(x);
        int c = add(x);
    }
    T x;
};

Eric's function can still use concept requirements. In fact, no
changes are required for the recursive version.

template<class T>
BOOST_CONCEPT_REQUIRES(
    ((AdditivePair<T>)),
    (int)
) sum_ints( T const & t )
{
    return add( t );
}

Adapting pair<int,int> and int to model AdaptivePair is basically the
same, but now we need to specialize the traits class.

// adapt pair
int first(std::pair<int,int> x) { return x.first; }
int second(std::pair<int,int> x) { return x.second; }
int add(std::pair<int,int> x) { return first(x) + second(x); }
template<> struct pair_traits<std::pair<int,int> > {
    typedef int first_type;
    typedef int second_type;
};

// adapt int
int first(int x) { return x; }
int second(int x) { return 0; }
int add(int x) { return x; }
template<> struct pair_traits<int> {
    typedef int first_type;
    typedef int second_type;
};

And now we can adapt recursive pairs to model the AdditivePair concept.

template<class T0, class T1>
T0 first(std::pair<T0,T1> x) { return x.first; }

template<class T0, class T1>
T1 second(std::pair<T0,T1> x) { return x.second; }

template<class T0, class T1>
int add(std::pair<T0,T1> x) { return add(first(x)) + add(second(x)); }

template<class T0, class T1>
struct pair_traits<std::pair<T0,T1> > {
    typedef T0 first_type;
    typedef T1 second_type;
};

Now that works as expected. Sorry for any confusion. (It illustrates
an interesting point, though. I was concerned with the base case in
Eric's recursion where I a saw unary function called sum_ints, which
didn't make sense to me conceptually since addition is a binary
operation. So, I was focused on trying to work out the concept modeled
by the argument to sum_ints, and missed the fact that sum_ints was
recursive. Later, though, having the concept worked out made
implementing the recursive version trivial.)

Daniel Walker