Boost logo

Boost :

Subject: Re: [boost] dramatically improved template error messages
From: Eric Niebler (eric_at_[hidden])
Date: 2012-08-14 17:40:52


On 8/14/2012 2:19 PM, Andrew Sutton wrote:
>>> IMO, this is now approaching something of real value. My personal
>>> experience using this with Proto-11 is that it dramatically improves
>>> error messages. I can also easily imagine how this can be used to
>>> implement a very nice concept-checking library for C++11 that
>>> approximates auto-concepts. Andrew, any thoughts on this? You've done
>>> far more work in this space than I have.
>>
>> I want to say yes, but I'm not sure. What's really needed is a
>> combination of this and static_assert. I want compilation to stop
>> *and* I want brief errors. Hmm... Let me get back to you on this.
>
> I tinkered for a little bit, and couldn't get what I wanted. The
> problem, from the perspective of general concept checking, is that I
> want compilation to fail when a substitution failure would happen. For
> example:
>
> template <typename I, typename T>
> I find(I first, I last, const T& value) {
> static_assert (Input_iterator<I>(), "");
> while (first != last && *first != value) ++first;
> }
>
> If a substitution of I triggers the assertion, then (ideally), we
> would stop instantiating the body and emit an appropriate message.
> What actually happens is that a diagnostic is reported, then
> instantiation continues, generating whatever errors I can expect from
> the expressions in the algorithm (e.g., no operator*). So we get
> redundancy. If the errors occur in nested instantiations of those
> expressions, we get template spew.

To avoid redundant errors, you must dispatch to an empty implementation
on concept check failure:

template <typename I, typename T>
  I find_impl(std::true_type, I first, I last, const T& value) {
    while (first != last && *first != value) ++first;
  }

template <typename I, typename T>
  I find_impl(std::false_type, I first, I last, const T& value) {
    static_assert (Input_iterator<I>(), "");
  }

template <typename I, typename T>
  I find(I first, I last, const T& value) {
    find_impl(Input_iterator<I>(), first, last, value);
  }

> The sfinae_error technique is somewhat different. My characterization
> is that it is useful for shallow logging of substitution errors, but
> it won't help here. You could probably force a "logged failure" in
> place of the static assert, but you'd still get all of the other
> errors from body of the template.

Not if you use the trick above.

> You could make this technique work if you were willing to write all of
> your algorithms as function objects,

If you're like me, you write function objects anyway because you prefer
first-class functions. :-)

> and guard all of your calls, but
> that seems a little intrusive.

You only have the guard the calls that add constraints. Other calls will
simply propagate sfinae_error and don't need to be guarded. (In my
example, S2 doesn't need to guard the call to S1, but S1 must guard S0
because it adds an Addable constraint.) Also, you can guard your
function object once by defining it with try_call_wrapper, so it doesn't
need to be guarded everywhere.

> It's a good trick, but I don't think it scales.

Just apply more force. :-)

-- 
Eric Niebler
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