
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