Boost logo

Boost :

Subject: Re: [boost] [guidelines] why template errors suck
From: John Bytheway (jbytheway+boost_at_[hidden])
Date: 2010-09-27 18:06:11


On 27/09/10 16:05, Eric Niebler wrote:
> On 9/26/2010 2:24 PM, John Bytheway wrote:
>> I decided to have a first stab at something along these lines (with g++
>> 4.4 in C++0x mode). I think my experiment shows promise. I tweaked a
>> cut-down version of the Boost.ConceptCheck InputIterator concept thus:
>>
>> template<typename X>
>> class InputIterator {
>> private:
>> typedef std::iterator_traits<X> t;
>> public:
>> typedef typename t::value_type value_type;
>> ~InputIterator()
>> {
>> verify::check(verify::same_type(*i++, v));
>> }
>> private:
>> verify::example<X> i;
>> verify::example<value_type> v;
>> };
>>
>> Here verify::same_type, i, and v are all Proto terminals, so the
>> expression verify::same_type(*i++, v) is a Proto expression tree.
>
> Wow, I never would have thought to use Proto for this!

I'm glad I could stretch its application domain beyond even your
imagination :).

<snip>

>> Does this sound like a worthwhile thing to attempt? I'd be willing to
>> give it a try.
>
> John, this sounds terrific. I don't think I grok in full what you're
> doing but it sounds worthwhile.

Here's an example of the sort of thing I'm imagining (this is just the
first plausible syntax that springs to mind):

// Some nasty preprocessor stuff to allow use of a traits class
TRAITS_CLASS(
  (std)(iterator_traits), (iterator_category)(value_type)...)

// Define the concept
class InputIterator :
  concept<InputIterator, refines<Assignable, EqualityComparable>>
{
private:
  // Make some sample values to define requirements with
  static model i;
  static std::iterator_traits<model>::value_type v;
public:
  // State the requirements (which will be a big messy Proto type)
  static auto requirements =
    provide_traits<std::iterator_traits<model>>(),
    same_type(*i, v),
    same_type(i++, i),
    meaningful(++i),
    ...;
};

// Use the concept for concept-based overloading
template<typename T>
void my_algorithm(T a, T b,
  boost::enable_if<models_concept<T, InputIterator>>::type*=0)
{
  // Or use it inside the algorithm to verify that
  // requirements are satisfied (compile error otherwise)
  assert_models_concept<T, InputIterator>();
  ...
}

// Or make an archetype of the concept and use it to verify
// that the algorithm really does only use the required syntax
archetype<InputIterator>::type t;
ASSERT_COMPILES(my_algorithm(t, t));

Does that make it any clearer?

> One thing to consider is whether you can
> use SFINAE on expressions to validate concepts without causing hard
> errors. Then we might be able to get some approximation of concept-based
> overloading.

That's roughly what I'm already doing, and exactly what I was proposing
to do. In order to get those nice-ish error messages I had to avoid the
analogous hard errors deep in the guts of the code. The exact same
techniques would allow concept-based overloading.

The last thing I mention above (using the concept to make an archetype)
is something that has more recently occurred to me. I think it is in
principle possible, although I suspect it could get very messy in
practice. Nevertheless, it would be very nice to get all three aspects
from a single definition, because that fills the three primary design
goals of Concepts as I see them:

1. Early detection of syntax issues for those writing generic algorithms
(archetypes provide this).
2. Early detection of syntax issues for those using generic algorithms
(the ability to assert that types model concepts provides this).
3. Concept-based overloading.

Of course, to do number 3 properly we need to cope with refinements
properly; using overloading as I've written above would lead to
ambiguities if types match multiple concepts. You can't do this:

template<typename T>
void my_algorithm(T a,
  boost::enable_if<models_concept<T, InputIterator>>::type*=0);

template<typename T>
void my_algorithm(T a,
  boost::enable_if<models_concept<T, ForwardIterator>>::type*=0);

because if T is an InputIterator then it will match both overloads.
However, at the cost of one indirection we can take advantage of C++'s
existing most-derived-class overloading by constructing a hierarchy of
tag types matching the hierarchy of concept refinements, thus:

template<typename T>
void my_algorithm_impl(T a, tag_of<InputIterator>::type const&);

template<typename T>
void my_algorithm_impl(T a, tag_of<ForwardIterator>::type const&);

template<typename T>
void my_algorithm(T a) {
  my_algorithm_impl(a,
    most_refined_tag_modelled<T, ForwardIterator>::type())
}

which again I think is possible from constructions such as those above.

John Bytheway


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