A
Alf P. Steinbach
* Pavel:
Hm, you throw in a number of apparently new arguments and then
effectively say "I'm going, bye".
Most of the problems you note above are difficult to protect against no
matter what general solution is adopted, i.e. apply equally. Like
returning a square root from a logarithm function -- which could perhaps
be protected against with judicial use of Design By Contract, but AFAIK
only Eiffel has proper DBC support[1]. The problem you note with the
Template Pattern (which is what I think you mean by Template Method),
which you say "happens a lot", does however require an example, because
the Template Pattern is essentially a means to impose the restrictions
one deems necessary -- and it seems you're saying that it doesn't.
Anyway, it seems the argument is that there are things one can't protect
against with reasonable effort, and since one can't in practice protect
against all cases, one should not do the easy things to protect against
more likely mishaps either. Therefore, one should not invest effort in
restricting usage to only valid usage, which is what using static type
checking is all about, and what C++'s type safety for construction is
all about. I most emphatically disagree that that effort is wasted.
With proper use of static typing, e.g. as with a "parts constructor",
you have the very strong clue that code that breaks the assumptions &
rules captured by the types, doesn't compile.
With virtual calls down to derived class from a constructor, you have no
clue other than documentation and perhaps a naming convention. The code
blowing up comes as a surprise, as illustrated by the Java bug report
referred to above (top). But much worse, it allows you to build a huge
edifice resting on invalid assumptions, before the blow-up happens.
Cheers,
- Alf
Notes:
[1] D has a little. The C++ DBC proposal stranded on something, I'm not
sure what. I seem to recall it had to do with the difficulty of
definining exactly what constitutes an external interface of a class,
e.g. the problem of callbacks during a temporarily broken class
invariant. Unless Google is tricking me it can be found at <url:
http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2004/n1613.pdf>.
Alf said:* Pavel:Alf P. Steinbach wrote:
In passing, check out[1] <url:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6596304>.
Yes, people make mistakes. JVM Specs is pretty clear about what is
supposed to happen and that is exactly what happens. The relevant
piece of the standard is easy to read and follow:
http://java.sun.com/docs/books/jvms/second_edition/html/Concepts.doc.html#19124Hopefully when the "bug" comes to the developer, they will close it
instead of "fixing" it.
I think it clearly illustrates the dangers of those virtual calls from
constructors.
For the problem is that code is maintained, not just originally
written (about 80% of coding work is maintainance, as I recall the
statistics).
When a maintainance programmer (which might be oneself) comes along
and e.g. adds an initializer to a class member, or falls for the
temptation to let the downcalled function do something with or to the
derived class' members, or adds a call there to a function that
someone else will in turn make do something with or to the derived
class' members, perhaps even in a class derived from the derived one,
where the context of the call is not at all clear, then presto, a new
bug or set of bugs.
I guess it is the problem with any framework, isn't it? A framework (be
it a Template Method, Factory or operator overloading) has to rely on
the maintenance programmer's using rather than abusing it. I am not
aware of a satisfactory protection against the concrete factory's
create() method returning a random integer casted to a pointer, the
semantics of two Template Method's concrete operations being swapped
(which, incidentally, happens a lot) or an overloaded binary operator*
for some Matrix class calculating pair-wise products instead of matrix
product (or other way around).
Certainly a good framework must let its *benevolent* user as clear and
readily available clue of what's expected from him/her as possible. My
opinion is that the name of init() virtual function would give just
right a clue and you disagree, you believe that the Holder/Factory
machinery is less error-prone and reduces maintenance costs and I
disagree. I really doubt we can come to an agreement on this so I
suggest that we disengage.
Hm, you throw in a number of apparently new arguments and then
effectively say "I'm going, bye".
Most of the problems you note above are difficult to protect against no
matter what general solution is adopted, i.e. apply equally. Like
returning a square root from a logarithm function -- which could perhaps
be protected against with judicial use of Design By Contract, but AFAIK
only Eiffel has proper DBC support[1]. The problem you note with the
Template Pattern (which is what I think you mean by Template Method),
which you say "happens a lot", does however require an example, because
the Template Pattern is essentially a means to impose the restrictions
one deems necessary -- and it seems you're saying that it doesn't.
Anyway, it seems the argument is that there are things one can't protect
against with reasonable effort, and since one can't in practice protect
against all cases, one should not do the easy things to protect against
more likely mishaps either. Therefore, one should not invest effort in
restricting usage to only valid usage, which is what using static type
checking is all about, and what C++'s type safety for construction is
all about. I most emphatically disagree that that effort is wasted.
With proper use of static typing, e.g. as with a "parts constructor",
you have the very strong clue that code that breaks the assumptions &
rules captured by the types, doesn't compile.
With virtual calls down to derived class from a constructor, you have no
clue other than documentation and perhaps a naming convention. The code
blowing up comes as a surprise, as illustrated by the Java bug report
referred to above (top). But much worse, it allows you to build a huge
edifice resting on invalid assumptions, before the blow-up happens.
Cheers,
- Alf
Notes:
[1] D has a little. The C++ DBC proposal stranded on something, I'm not
sure what. I seem to recall it had to do with the difficulty of
definining exactly what constitutes an external interface of a class,
e.g. the problem of callbacks during a temporarily broken class
invariant. Unless Google is tricking me it can be found at <url:
http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2004/n1613.pdf>.