|
Boost : |
From: João Abecasis (jpabecasis_at_[hidden])
Date: 2006-04-12 06:05:34
Hi!
<caveat>
I've followed some discussions on adding Concepts to the language from a
distance, but I'm not completely familiar with all the details. Take my
comments with a grain of salt
</caveat>
Paul Mensonides wrote:
> Well, for a simple scenario:
>
> template<class T> void f(T x) {
> return x;
> }
>
> template<class T> void g(T x) {
> return f(x);
> }
>
> If I add concept requirements to f and g, the set of requirements for g has to
> include the set of requirements of f and f itself. If I add another function:
>
> template<class T> void h(T x) {
> return g(x);
> }
>
> Its set of requirements must include the set of requirements from g and g
> itself. So far, for h, that set includes CopyConstructible, the ability to call
> f, and the ability to call g.
>
> That's what I'm referring to when I say "bubbling of implementation detail."
> Note, in particular, that CopyConstructible is not sufficient as a sole
> requirement of g or h. f is needed in the requirements of g, and both f and g
> are needed in the requirements of h because both are bound during the second
> phase--which could result in (e.g.) an ambiguity. You need to raise that (e.g.)
> ambiguity to the top-level instantiation (or concept check immediately prior to
> instantiation) in order to avoid the two-megabyte error messages (whether they
> be in regular templates or in concept checks).
It is my understanding that requirements are to be maintained by the
developer. This only follows existing practice with, e.g., enable_if,
static_asserts and tag dispatching. It is still up to the developer to
keep those requirements accurate and up-to-date. Otherwise the compiler
will "fallback" to the two megabyte error message.
In my view, the Concepts approach, because it formalizes syntax and
semantics, has the added benefit that it eases automated testing of the
requirements. I'm not sure how much the Concept checks add to what is
already possible in the current language, but having an explicit and
unambiguous syntax to do the same things is better than the hacks being
used today.
> Something like CopyConstructible is fairly innocuous as an implementation
> detail, the f and g are not--particular the requirement for f in h. Such
> bubbling makes it virtual impossible to let those calls be resolved during the
> second phase (without some sort of concept requirement union mechanism either
> explicit or implicit).
From my experience with Spirit, for example, you'll get tons of errors
from fairly small mistakes. I suppose this is what you'd call "bubbling
of implementation details" and it is the current state of affairs. The
situation could be improved today by adding Concept check hacks or it
can be improved tomorrow with proper Concept checks. Either way, the
burden will be on the developers of Spirit to keep those checks accurate
and to avoid ambiguity and corner cases in the library code.
>> Concepts provide separate type-checking, so the error will
>> show up in one of two places:
>>
>> 1) In the definition of __introsort_loop(), before it
>> is instantiated, if there is no suitable operator<. No
>> instantiation stack needed, because there's no instantiation yet.
>>
>> 2) In the user code, where the user actually made the
>> mistake. This error will say that the requirement for, e.g.,
>> LessThanComparable is not satisfied by the types involved. No
>> instantiation stack needed, because we haven't tried to
>> instantiate sort().
>
> What I fail to understand is why this is just LessThanComparable here instead of
> all of LessThanComparable, __introsort_loop, __introsort, __sort, and sort. All
> of those can fail to be bound during the second phase--even if they are
> namespace qualified--because of overloading.
That would be because, as the developer you take great care to ensure
that you respect the stated requirements so your implementation details
don't pop out of the interface. Even though the user could intentionally
(or not) break things by overloading implementation functions or, god
forbid, a library bug could creep in, there will always be plenty of
ways to shoot one self in the foot. But, templates or no templates, the
developer is responsible for hiding and maintaining implementation details.
Taking your example above, if h needs to invoke g(x) generically then,
that could be its Concept requirement. In that case, the compiler would
ensure such invocation is possible and that x meets the requirements for
g. I further suppose all this occurs while deciding if h is a viable
function, and before actually trying to fully instantiate and compile
it. If, on the other hand, you already know (and want to enforce) that
the only effective requirement is that T be CopyConstrutible you could
specify that in the requirements instead.
Best regards,
João Abecasis
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk