|
Re: Well trodden path
|
Posted: Sep 24, 2005 3:48 AM
|
|
> > I understand duck typing to be a pattern to make > calling > > methods that have the same signature (i.e., return the > > same type, take the same number and kind of arguments) > > easy, even if the classes in question are not related > to > > each other. > > > > Perhaps my counterexample was poorly picked. However, > > std::swap relies on templates to do much more than what > > duck typing offers. AND, other uses of policy classes > > permit far more than that > (http://www.boost.org/libs/concept_check/concept_check.htm) > > After reading that, I get the idea that concept checking > is basically a trick to validate some type against a set > of operations at compile time by forcing instantiation of > a type parameterized checking function.
Yes, that's the BCCL library he links to, but that's basically a library to give some of the benefits of support for concepts. Concepts in the language would be very different, and there exists two formal proposals for it (which were presented at the last C++ standards meeting at Lillehammer):
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1782.pdf http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1758.pdf
What you describe above is the workings of the library, not language support for concepts.
> A bit like a unit > test but at compile time. Sort of a poor man's compile > time [object conformsToProtocol: someProtocol] to borrow a > line from ObjectiveC.
Or poor man's support for concepts. :)
> That's fine, but what if the operation I'm performing only > requires part of the protocol? I guess I have to create a > special sub concept checker?
With language support for concepts, it should be much easier to define new concepts, maybe even at the point of use. One way to think of concepts is as "types for types", or perhaps better as a "set of types". Consider:
// This accepts _only_ values of type int, or types convertible to int
void f(int);
// This accepts values of type SomeBase, or any type derived from it
void f(SomeBase);
// This accepts a value of _any_ type... Bjarne Stroustrup has said that templates model the mathematical concept "for all T...". However, in maths, you can typically constrain the values: "for all T, where...". And this is what concept checking is about - giving that "where" clause.
template<class T> void f(T);
// This accepts a pointer to any type
template<class T> void f(T *);
// This accepts a reference to any type
template<class T> void f(T &);
// This accepts an array of any size of any type (variations may be used, such as fixing the type or size)
template<class T, size_t N> void f(T (&)[N]);
And so on. As you can see, we have some "wiggle room"; some way of specifying some of the desired type properties in templates (pointer, reference, array, ...). However, we currently have no way of specifying arbitrary constraints (without "simulating" it with BCCL or the mentioned CTL), such that a type should support the basic arithmetic operations (+, -, *, /). If we had, we could do this (syntax from one of the concept proposals mentioned above):
template<Arithmetic T> void f(T);
You'd be able to call function with any type conforming to the concept "Arithmetic" (or "modelling the concept", in generic programming terminology), such as int, float, double, ArbitraryPrecisionNumber, etc.
As you hopefully can see, support for concepts would fill a "hole" in the type system, as you now can only check for concrete, specific types (like int, etc.), or types related by inheritance, or pointer/reference/array, or allow anything, but not something inbetween "anything" and the mentioned ones.
> Seems like a lot of extra work for dubious benefit. I'd > rather have a system that just runs and when something > goes wrong it helps me diagnose the problem.
Some big advantages with support for concepts are:
1) You get to know about any problem at compile-time; no tests are needed to check for conformance. Also, the compiler can type-check the templates, and the client code _independently_ of each other, a key benefit of "separate compilation" of templates.
2) The errors are reported where they exist, according to the concept specification (such as in the function, or in the function call).
Without this, it can be very hard to determine the actual problem, because then the actual error may be in an entirely different part of the program, than where you get the error message (try calling std::sort() with types not supporting "<", and you'll see what I mean: it "bombs out" somewhere deep into the implementation, rather than saying "Your type doesn't support "<"".
Support for concepts are really about giving "duck typing" the same type checking (and ability to overload) as ordinary function signatures.
> It seems to me that C++ has an over-reliance on compile > time checking because of an unhealthy obsession with > avoiding the cost of maintaining useful meta information.
I think you're way out on a limb, here, and risk making opinions based on erroneous or missing information. It's not about avoiding storing meta information: that may actually be useful, and given your remark, you're probably not aware of the work going on in giving C++ considerable reflection information (such as Bjarne Stroustrup's project, or Daveed Vandevoorde's "Metacode" proposal).
You see, it's not an either-or: Being able to check things, and act on type information, both at compile-time, and at run-time, complements each other (see also the "Design by Contract" proposal, which deals with the run-time checking).
> In the end, C++ ends up being ever more bloated as > s endless instantiation of templates produces dozens of > copies of the same binary. For instance: > > List<A*> > List<B*> > List<C*> > etc, all of which are probably identical binary but have > different copies just because they are different "types".
As someone else pointed out, this problem can be "trivially" avoided with partial specialisation, and hoisting type-invariant code out of the template:
// General version
template<class T> List { // ... };
// Partial specialisation for pointers
template<class T> List<T *> : ListImpl {};
Support for concepts might provide even further ways of doing code sharing.
> The very picture of penny wise and pound foolish.
You may want to reconsider your opinion, given that your argument is based in large part on incorrect or incomplete information, making the conclusion erroneous.
Regards,
Terje
|
|