 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Steven T. Hatton Guest
|
Posted: Fri Aug 13, 2004 1:49 pm Post subject: Templates vs. ABC interfaces |
|
|
typedef ABC_interface Abstract_Baseclass_interface
(generally meaning all functions are pure virtual.)
The discussions I've had lately regarding exceptions in C++ have caused me
to question exactly what advantages Templates provide to the language, and
at what cost. Before you draw any wrong conclusions, I am not advocating
abolishing templates. I have _C++_Templates_The_Complete_Guide_ sitting at
the top of my books to read next. Now that my eyes have finally adjusted
to looking at expressions containing things such as E<D > > >,
(looks like Lisp with angle brackets) I am starting to appreciate what
templates do for me.
I do have the sense that there are places where ABC interfaces offer a
comperable solution to the use of templates. They offer certain advantages
over templates such as strong(er) typing, faster compilation, easier to
read code (YMMV), etc. The arguments against a more structured exception
handling mechanism in C++ I've met have to do with problems of abstracting
the exception specification list so that the mechanism can be supported
using templates. I'm really trying to get a handle on understanding this
particular (appearant) limitation. I believe it is a significant compromise
that makes C++ code potentially less robust than it might otherwise be.
This is part of my motivation for discussing this topic, but, by far, not
the only one. I believe the comparison between using ABC interfaces verses
templates is a significant topic worth investigating for many reasons.
Stroustrup tells us in §13.6.1 of TC++PL(SE) "If no hierarchical
relationship is required between these objects, they are best used as
template arguments. If the actual types of these objects cannot be known at
compile-time, they are best represented as classes derived from a common
abstract class. If run-time efficiency is at a premium, that is, if
inlining of operations is essential, a templated should be used."
It appears to me the use of templates often means there will be some lack of
information that would (should) otherwise be available in the interface of
the function or class the timeplate represents in a give context. For
example, the type parameters passed to a template cannot, AFAIK, be
restricted in such a way that tells the programmer what can or can't be
used. The programmer must have some other means of knowing what the
template expects; either through documentation or examining the template
itself. Trial and error is also a candidate.
Arnold Gosling and Holmes have in interesting epigraph at the beginning of
their chapter on collection: "The problem with people who have no vices is
that generally speaking you can be pretty sure they're going to have some
very annoying virtues." - Elizabeth Taylor. I didn't really appreciate
that quote until I started learning about templates. I haven't studied the
recently introduced generic programming support in Java, but, before it was
introduced, the only general means of creating collections - analogous to
C++ containers - was to create containers of type Object, and, either rely
on the correct type object being put in them, and/or use some kind of
introspection and casting to get back what you originally put in. IOW, it
basically circumvents type checking. The advantage over the template
counterpart is that it is easier to learn, and works well for many
situations where the programmer can rely on the correct type being passed
as a parameter, etc. I'm confident a comparable set of container classes
could be created to hold C++ objects which all derive from some generic
baseclass.
The cost of doing this seems to be that the more abstract we make the base
class used for the generic element of a container, the less we can rely on
type checking, and the more work we have to do in order to cast the element
back to the correct type before we use it. Also, since collections of
objects cannot be instantiated with non-zero size unless we know the exact
type of the element, we can't create these collections with default
initialized members.
What other relevant issues are involved in evaluating the comparable value
of using ABC interfaces verses templates?
--
STH
Hatton's Law: "There is only One inviolable Law"
KDevelop: http://www.kdevelop.org SuSE: http://www.suse.com
Mozilla: http://www.mozilla.org
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Jean-Marc Bourguet Guest
|
Posted: Fri Aug 13, 2004 4:06 pm Post subject: Re: Templates vs. ABC interfaces |
|
|
"Steven T. Hatton" <susudata (AT) setidava (DOT) kushan.aa> writes:
| Quote: | typedef ABC_interface Abstract_Baseclass_interface
[...]
What other relevant issues are involved in evaluating the comparable
value of using ABC interfaces verses templates?
|
ABC interfaces can
- make use run-time polymorphism.
- reuse object code better
Templates can
- be used with any classes providing the needed interface, with ABC
interface, you need an adaptator
- be specialized explicitly, and there is a lot of expressing power
and flexibility residing here
- provide opportunities for optimisation
Yours,
--
Jean-Marc
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Hyman Rosen Guest
|
Posted: Sat Aug 14, 2004 4:36 am Post subject: Re: Templates vs. ABC interfaces |
|
|
Steven T. Hatton wrote:
| Quote: | The arguments against a more structured exception handling mechanism in C++
I've met have to do with problems of abstracting the exception specification
list so that the mechanism can be supported using templates.
|
No, that's just the convenient excuse. Experience with Java shows that
specifying which exceptions a function can throw is a terrible idea,
because the ideal of exception handling is to ignore and allow to pass
through any exceptions you don't care about. It is utterly useless to
decorate functions with lists of exceptions that any downstream call
can throw, and it costs stupid amounts of effort to go around modifying
all the callers when a low level function decides it wants to throw a
new exception.
| Quote: | For example, the type parameters passed to a template cannot, AFAIK, be
restricted in such a way that tells the programmer what can or can't be
used. The programmer must have some other means of knowing what the
template expects; either through documentation or examining the template
itself. Trial and error is also a candidate.
|
That is true for all things, not just templates. Unless you read or
experiment, you might be tempted to call sqrt(-7) and asin(1.5).
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dietmar Kuehl Guest
|
Posted: Sat Aug 14, 2004 10:28 am Post subject: Re: Templates vs. ABC interfaces |
|
|
Steven T. Hatton wrote:
| Quote: | I do have the sense that there are places where ABC interfaces offer a
comperable solution to the use of templates.
|
Sometimes you can indeed choose whether you want to use ABC or
templates. More often, only one of the two approaches yields a
reasonable solution. This is mainly due to fairly technical
issues like:
- If you need runtime polymorphism, virtual functions are the
way to go.
- Templates support something similar to multiple dispatch.
- Types can vary more independently when using templates.
| Quote: | They offer certain
advantages over templates such as strong(er) typing, faster compilation,
easier to read code (YMMV), etc.
|
Although I agree for the faster compilation, I disagree with
respect to the other issues. Of course, what is easier to
read is in the eye of the beholder.
| Quote: | The arguments against a more structured exception
handling mechanism in C++ I've met have to do with problems of abstracting
the exception specification list so that the mechanism can be supported
using templates. I'm really trying to get a handle on understanding this
particular (appearant) limitation.
|
Actually, I don't think that this issue is really about templates
at all - it is just more obvious when using templates: the issue is
that the exception specifications are always too tight if the code
supports any form of polymorphism. For ABCs you can simply impose
some exception specification which all derived classes have to
follow - whether the exception specification is appropriate or not.
You can actually do the same for templates, although in this case
the requirement on the exception specification is implicit or in
the documentation and not in the code. For whatever reason people
feel it is appropriate to have exception specifications in an ABC
but not for a template parameter.
My personal take on exception specifications is that the idea is
entirely wrong in the first place: exceptions are a tool to handle
errors at a location quite far away from where the error occured.
Exceptions are entirely inappropriate to handle errors locally.
However, exception specifications are a rather local thing,
especially when it comes to generic code, be it statically or
dynamically generic: if I pass an object into a generic function
(i.e. a member of an ABC or function with a templated parameter),
I know very well what kind of errors are associated with this object
and I can be prepared to handle the associated errors. However,
the generic function is unlikely to have even the remotest idea of
what my errors can be but still insists on nailing down the kinds
of errors! Hence, the idea that an exception specification can ever
restrict the kinds of errors which can be caused is completely
wrong. The exception specification might tell us what kind of
errors are known to occur occasionally but that would not do us
any good either because obviously arbitrary kinds of other errors
might also occur and there is no guarantee at all that the promised
error actually ever occurs!
Exception specifications might be reasonable on functions which
are entirely non-generic. For example, it might be reasonable to
attach an empty throw specification to 'double sin(double)'. Big
deal! Most of the code is generic in some form or the other.
Anyway, this thread is on templates vs. ABCs. However, it is
important to notice that the exception specification issue also
applies to ABCs.
| Quote: | I believe it is a significant
compromise that makes C++ code potentially less robust than it might
otherwise be.
|
Right. C++ would be much more robust if we removed exception
specifications completely :-)
| Quote: | It appears to me the use of templates often means there will be some lack
of information that would (should) otherwise be available in the interface
of the function or class the timeplate represents in a give context.
|
Since the concepts implemented by the template paramters are not
specified, it is correct that there is a lack of information.
However, it is partially this lack of information which gives real
power to templates. For example, in an ABC you have to nail down the
exact signature of a function. There is some leeway with respect to
the return type as it can be covariant and, at least conceptually
although not in C++, the paramters could be contravariant. In any
case, this restriction forces the parameter and return types into
some inheritance hierarchy or actually fixes them entirely. For
templates, the parameter and return types need to implement certain
concepts but this is a much weaker requirement.
| Quote: | For
example, the type parameters passed to a template cannot, AFAIK, be
restricted in such a way that tells the programmer what can or can't be
used. The programmer must have some other means of knowing what the
template expects; either through documentation or examining the template
itself. Trial and error is also a candidate.
|
This is correct in some sense although not entirely. The programmer
of a template might actually use a concept checking facility although
there is no such requirement. Of course, being in the body of the
template you might consider this "examining the template itself".
| Quote: | What other relevant issues are involved in evaluating the comparable value
of using ABC interfaces verses templates?
|
There are several things which interoperate nicely to make templates
the obvious choice for all kinds of algorithmic processing (I will go
into details of each below):
1. The abstraction mechanism for templates is extremely cheap.
2. Associated types may vary independently.
3. It is possible to have hierarchies of abstractions and take
advantage of optional properties.
The typical processing in an algorithm does not differ with the
type (except for different capabilities; see 3. below). That is,
the choice of the algorithm is a static choice which is entirely
independent on the use data structure. What actually happens in
each of the operations provided by the abstraction is subject
to the actual data structure which may reasonably use dynamic
polymorphism. That is, data structures may be implemented as ABC.
Whether this is the right approach strongly depends on the details
of the data structure, however.
Now lets describe what I refered to with the numbers above:
1. As the resulting object code is individually crafted for each
combination of parameter types and may actually use inline
functions, i.e. no function call is required, the abstraction
mechanism is very fast. This allows for tiny operations which
just do a small thing. For example, input iterators go through
at least three configurable operations while iterating:
check (operator==()), access (operator*()), and advance
(operator++()). Since there is no real associated cost with these
calls, this is viable. If you'd use a similar approach with ABCs
performance you would experience a significant perofrmance hit.
Consequently, iterator like entities in systems based on virtual
functions only (e.g. Java or C#) use just one function for all
three operations: "Next()". Having three different operations
allows for easier processing. In abstractions where the
operations are not that directly associated with each others,
there may not be any approach to fold things into just one
function.
In some sense, the abstraction has to fit the mechanism used
for the abstraction: If the mechanism is relatively expensive,
the abstracted operation should do more than if the mechanism
is cheap. The same pattern comes up with entirely different
abstraction mechanisms:
- Templates are cheap and can often be inlined, i.e. the invidual
operations can be really tiny, e.g. a pointer dereference or
compare. It is worth noting the the choice whether an operation
of abstraction is inlined is with the client of the template,
not the template.
- Virtual functions generally cannot be inlined, involve a
function call, and thus limit size of the basic blocks, i.e.
reduce the potential of the optimizer. Thus, they should do
a more substantial operation.
- When calling a function from another programming language
but within the same process (e.g. through JNI), in addition
to a function call, some marshalling may become necessary.
Thus, the operation should be even more substantial.
- When calling a remote service, the data actually also has
to be channeled over some network.
2. If you have an entity with some operation which is part of the
abstraction, you generally have a pretty good idea on the
parameter and return types. The template often does not really
care beyond requiring some minimal properties on the type, e.g.
that they are copyable. That is, it is easily possible to vary
them and the template may still apply useful operations to them!
For example, in a function summing the values of a sequence, it
does not really matter to the template whether the values are
doubles, vectors, or whatever as long as the values can be
copied and added.
3. When using a template with an abstraction, the details of the
abstraction are actually just a minimal set of requirements. If
the templates knows about other operations which may potentially
be available and be benefitial, the template can actually take
advantage of thes additional requirements, e.g. when handing
the entities off to some other algorithm. For example, when
computing the distance of two iterators, a general algorithm
may just iterate over the sequence until the second iterator
is reached. If it is know that the iterators are random access
iterators, the distance can be determined using the difference.
Although things like this can be done with ABCs, too, this
would require use of dynamic_cast<>(), a relatively exansive
operation, at run-time while the template can be configured
appropriately at compile time.
--
<mailto:dietmar_kuehl (AT) yahoo (DOT) com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Glen Low Guest
|
Posted: Sat Aug 14, 2004 10:30 am Post subject: Re: Templates vs. ABC interfaces |
|
|
| Quote: | It appears to me the use of templates often means there will be some lack of
information that would (should) otherwise be available in the interface of
the function or class the timeplate represents in a give context.
|
It could be argued that templates preserve information whereas ABCs
destroy it, if you look from the point of the client. For example, if
I use a tmpl <X> object, I can usually recover the interface of X from
the object directly. OTOH, if I have X inherit from ABC and pass ABC
around, I have lost the interface of X (and can only get it back by
casting etc). Of course, abstraction is a nicer name for this.
Another advantage of templates is generic programming, which is where
it dovetails somewhat with more dynamic languages like Objective-C and
scripts. Instead of a priori requiring my X object to obey a formal
interface contract ABC, I only require it to obey an informal protocol
(or "model" in the old STL parlance) i.e. known or documented
functions working in a certain fashion. This may mean an X object
doesn't need to inherit from 100 different ABCs to obey their
contracts, reducing coupling and making it more flexible. But unlike
scripting, all this is checked during compiling not running.
| Quote: | For
example, the type parameters passed to a template cannot, AFAIK, be
restricted in such a way that tells the programmer what can or can't be
used. The programmer must have some other means of knowing what the
template expects; either through documentation or examining the template
itself. Trial and error is also a candidate.
|
That could be an advantage, see previous para. However there are ways
of getting around this, e.g. partial or full specialization, asserts
(Sutter has a good one in gotw.ca somewhere...) and so on, so that
compiling a template with an unsuitable type results in a compiler
error. (Java 1.5 and C# 2.0 have more direct support for this, because
they can't really support an informal interface, generic programming
model like C++.)
| Quote: | I haven't studied the
recently introduced generic programming support in Java, but, before it was
introduced, the only general means of creating collections - analogous to
C++ containers - was to create containers of type Object, and, either rely
on the correct type object being put in them, and/or use some kind of
introspection and casting to get back what you originally put in. IOW, it
basically circumvents type checking. The advantage over the template
counterpart is that it is easier to learn, and works well for many
situations where the programmer can rely on the correct type being passed
as a parameter, etc. I'm confident a comparable set of container classes
could be created to hold C++ objects which all derive from some generic
baseclass.
|
In general, templates work best when agnostic of the template types
and params. That's why the first application that springs to mind is
containers. You really want a vector to be usable with ints, floats
and spaceships. OTOH, you don't want to put a spaceship in a vector of
ints (Bjarne's analogy?).
The more the construct needs to know or constrain the types and
params, the more a traditional or inheritance-based solution may be
needed.
| Quote: | The cost of doing this seems to be that the more abstract we make the base
class used for the generic element of a container, the less we can rely on
type checking, and the more work we have to do in order to cast the element
back to the correct type before we use it. Also, since collections of
objects cannot be instantiated with non-zero size unless we know the exact
type of the element, we can't create these collections with default
initialized members.
|
Also, a template can often be instantiated with non-class objects, but
a non-class object can't inherit from an ABC.
Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Steven T. Hatton Guest
|
Posted: Sat Aug 14, 2004 6:39 pm Post subject: Exceptions:was:Re: Templates vs. ABC interfaces |
|
|
Hyman Rosen wrote:
| Quote: | Steven T. Hatton wrote:
The arguments against a more structured exception handling mechanism in
C++ I've met have to do with problems of abstracting the exception
specification list so that the mechanism can be supported using
templates.
No, that's just the convenient excuse. Experience with Java shows that
specifying which exceptions a function can throw is a terrible idea,
because the ideal of exception handling is to ignore and allow to pass
through any exceptions you don't care about. It is utterly useless to
decorate functions with lists of exceptions that any downstream call
can throw, and it costs stupid amounts of effort to go around modifying
all the callers when a low level function decides it wants to throw a
new exception.
|
I agree that trying to enforce a strict mapping between exceptions thrown at
lower levels, and the functions that initiate the call stack is an exercise
in absurdity. However, Java doesn't actually require you to declare every
exception you might throw. It merely requires that you declare a
superclass of every exception you might throw. Since all exceptions derive
from java.lang.Exception (or perhaps java.lang.Throwable, I'd have to look
it up to be sure.) all that is required is that you tell the caller that
you throw an exception. If the caller doesn't want to be bothered with
handling it, the exception can be passed up the stack using `throws
Exception'. The advantage to this is that the programmer, the compiler,
and the IDE know there is a potential for an exception to be thrown. They
can also determine that you are lying. That is, if you claim to throw
something that cannot be verified at compile time, your code won't compile.
Exceptions are an effect of calling a function. If they are not part of the
function declaration, they are a hidden side-effect. I consider that to be
a bad feature. When some function deep down in the bowels of a program
does throw an unadvertised exception, the programmer who wrote the call
that initiated the call stack is at a loss to know where it came from
without looking at a backtrace of the inevitable abort. (Yes, I know there
are better approaches such as having a top level catch-all, but that still
means some extra work on the part of the programmer to find where the
exception originated.)
My experience has been that C++ programmers, in general, do not understand
exceptions as well as they should, and don't use them(well). I recently
had a discussion with a person who has years of experience working
with/creating one of the most popular GUI toolkits for X-Windows tell me
that exceptions cause problems with resource cleanup, and the correct
approach to freeing resources when exceptions are throw is very difficult
to understand. That was his primary reasoning for not supporting and using
exceptions in the library.
At the risk of making incorrect assumptions as to why he said this, I will
speculate that he was speaking from the basis of his first encounter with
the exception mechanism in C++. I know my first encounter with C++
exceptions was not encouraging. I still don't understand them as well as I
should. When I encountered exceptions when first learning Java, I assumed
they would be as obscure as C++ exceptions, and avoided them for as long as
I could. When conscience and circumstances finally forced me to learn
about them, I found them quite easy to understand and work with.
The fact that circumstances forced me to learn to use exceptions is
significant. In TC++PL(SE) we are told "One might think that the default
should be that a function throws no exceptions. However, that would
require exception specifications for essentially every function, would be a
significant cause for recompilation, and would inhibit cooperation with
software written in other languages. This would encourage programmers to
subvert the exception-handling mechanisms and to write spurious code to
suppress exceptions. It would provide a false sense of security to people
who failed to notice the subversion."
In TJPL(3E) we are told "Checked exception handling is strictly enforced
because doing so helps avoid bugs that come from not dealing with errors.
Experience has shown that programmers forget to handle errors or defer
writing code to handle them until some future time that never arrives."
Bruce Eckel tells us:
http://www.mindview.net/Etc/Discussions/CheckedExceptions
"My theory is that when someone is trying to do something and you are
constantly prodding them with annoyances, they will use the quickest device
available to make those annoyances go away so they can get their thing
done, perhaps assuming they'll go back and take out the device later. I
discovered I had done this in the first edition of Thinking in Java:
.....
} catch (SomeKindOfException e) {}
And then more or less forgot it until the rewrite. How many people thought
this was a good example and followed it? I began seeing the same kind of
code, and realized people were stubbing out exceptions and then they were
disappearing. The overhead of checked exceptions was having the opposite
effect of what was intended, something that can happen when you experiment
(and I now believe that checked exceptions were an experiment based on what
someone thought was a good idea, and which I believed was a good idea until
recently)."
I don't like pointing out the faults of major projects that I have a
significant investment of time and effort in, but I believe this is worth
mentioning. The default behavior of KDevelop, the IDE used by many KDE
developers over the years, is to disable exception handling (and RTTI) in
the compiler, There is no mechanism or correcting this other than hacking
the macro files, (or the perhaps the code that generates them, whereever
that might be) That tells me that a lot of C++ programmers don't use
exceptions, and exception handling at all.
So, in summary, my experience is that:
*Requiring all exceptions - and not superclasses thereof - to be explicitly
declared whereever they are thrown is unrealistic and silly.
*Requiring all exceptions to be declared by functions that may throw them -
allowing for the use of superclasses (base classes) - can result in people
swallowing exceptions.
*The C++ exception model leads to programmers not using exceptions, and
thinking that exceptions are a bad thing.
Of the three evils mentioned above, the second is the lesser. A programmer
must proactively avoid dealing with the exception correctly. putting
throw(std::exception) in every function declaration that throws an
exception is not that big of a deal. To call it "reams of code" is inane.
Now Eckel's approach is to circumvent the requirement by throwin the checked
exception in an unchecked exception wrapper. I believe that is
called /smuggling/. There are times when we may want/need to call a
function that throws an exception from a stack of calls that know nothing
about it. For example if we are using a library to service one of our
established requests. The alternatives are to swallow the exception ( and,
perhaps, write a message to std::clog). We may be able to handle the
exception at that level. If not, we can pass it up the stack. One way is
to wrap it up in something we do throw up the stack already. If our call
stack does not handle exceptions at all, we have to change all the
functions involved, and any funcitons that also call these same funcitons.
Or we take Eckel's GOTO approach to exception handling.
And that is exactly what it is. I suspect it causes a lot of programs to
crash when people run into exceptions they don't expect. It's probably why
many people don't use exceptions in C++. Their first experience with this
is extremely negative. When they try to understand them they run into
example of how to change the default behavior, rethrow the exception to
another catch statement in the same function and handle the exception
there. A few experiments result in infinite recurstion. They have other
things they need to deal with, so they simply avoid using exceptions
thereafter.
--
Regards,
Steven
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dietmar Kuehl Guest
|
Posted: Sun Aug 15, 2004 10:55 am Post subject: Re: Exceptions:was:Re: Templates vs. ABC interfaces |
|
|
Steven T. Hatton wrote:
| Quote: | *Requiring all exceptions - and not superclasses thereof - to be
explicitly declared whereever they are thrown is unrealistic and silly.
|
That is clearly silly.
| Quote: | *Requiring all exceptions to be declared by functions that may throw them
- allowing for the use of superclasses (base classes) - can result in
people swallowing exceptions.
|
Effectively, your only option for any form of generic code, be it a
template or a function accepting a reference of class, is to use
'throw(std::exception)'. Since most of the code is generic or uses a
generic code, most of the code can only have this exception specification.
Assuming all exceptions are derived from 'std::exception', what is the
advantage of this? I see the disadvantage of having to add extra
boilerplate code to each function - a major annoyance. To warrant this
exercise there has to be a pretty high gain. ... and it also disallows
to use of exception hierarchies which are not rooted in 'std::exception',
e.g. to cope with exceptions thrown by some other language which does not
know of 'std::exception'.
I stick to what I have said before: Exception specifications are a bad
idea.
--
<mailto:dietmar_kuehl (AT) yahoo (DOT) com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Hyman Rosen Guest
|
Posted: Sun Aug 15, 2004 11:01 am Post subject: Re: Exceptions:was:Re: Templates vs. ABC interfaces |
|
|
Steven T. Hatton wrote:
| Quote: | It's probably why many people don't use exceptions in C++.
|
My experience with exception handling in C++ has been that many compilers
fail to implement it properly. On both Sun and Microsoft compilers I have
trouble using them because they just don't work.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Steven T. Hatton Guest
|
Posted: Sun Aug 15, 2004 2:30 pm Post subject: Re: Exceptions:was:Re: Templates vs. ABC interfaces |
|
|
Dietmar Kuehl wrote:
| Quote: | Steven T. Hatton wrote:
*Requiring all exceptions to be declared by functions that may throw
them - allowing for the use of superclasses (base classes) - can result
in people swallowing exceptions.
Effectively, your only option for any form of generic code, be it a
template or a function accepting a reference of class, is to use
'throw(std::exception)'. Since most of the code is generic or uses a
generic code, most of the code can only have this exception specification.
Assuming all exceptions are derived from 'std::exception', what is the
advantage of this?
|
I'm not convinced the situation is as drastic as you have described. You
seem to be implying that there are few (virtually no) situations where
anything but std::exception is appropriate. I will admit that specific
(derived from std::exception, rather than std::exception itself) exceptions
become more trouble than they are worth as they get further from their
origin. So I will agree that my proposal implies there will be a fair
number of `throw (std::exception)' specifications. In the case of Jakarta
I've found that javax.servlet.ServletException; actually leads to better
results than java.lang.Exception.
The advantage is that when you see a function without an exception
specification, you know it will not throw 'normal' exceptions. That is,
exceptions typically thrown by application programmers. In the case of
C++, I'm less sure what that might mean. In the case of Java it means the
jvm generated the (unchecked) exception. (Or Bruce Eckel wrote the code
that threw it.) I would say an exception that is thrown past a function
with no exception specification is one that could not be determined at
compile time.
The way I think about this is by analogy to hardware and connection jacks.
The pins are the analog to the parameter list, return type and exception
specification. One can look at the schematics and find what signals are
carried on which pins. It is very atypical to have two different
components connected through multiple jacks. Usually the signals intended
for components further away than the immediately adjacent component are
passed through the adjacent component from the same jack as the signals
intended for that adjacent component. The reason for this is that the
alternative is unmanageable chaos.
There is probably a way to extract the exception information a function or
type passed to a template with throw. I've suggested something along the
lines of `throw (exception<T>)'.
| Quote: | I see the disadvantage of having to add extra
boilerplate code to each function - a major annoyance. To warrant this
exercise there has to be a pretty high gain. ... and it also disallows
to use of exception hierarchies which are not rooted in 'std::exception',
|
And that is very good! It means I can catch everything as std::exception,
and then query it to find out what it really is, or holds.
| Quote: | e.g. to cope with exceptions thrown by some other language which does not
know of 'std::exception'.
|
If you call a function that throws a 'foreign' exception, then you are
obligated to wrap it up in std::exception before you throw it into the C++
call stack. If you don't, then you deserve the consequence of having your
system crash. I certainly have no desire to try to figure out what some
hitherto unspecified object caught in catch(...) is. That exception is
worse than useless. It is sure to represent a bug. The only reason I can
see for doing such a thing is to the swallow the exception.
| Quote: | I stick to what I have said before: Exception specifications are a bad
idea.
|
int main(){
try {
my::application a;
a.run();
}
catch (...) {
// oh well!
}
--
Regards,
Steven
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Niklas Matthies Guest
|
Posted: Mon Aug 16, 2004 10:48 am Post subject: Re: Exceptions:was:Re: Templates vs. ABC interfaces |
|
|
On 2004-08-15 10:55, Dietmar Kuehl wrote:
| Quote: | Steven T. Hatton wrote:
:
*Requiring all exceptions to be declared by functions that may
throw them - allowing for the use of superclasses (base classes) -
can result in people swallowing exceptions.
Effectively, your only option for any form of generic code, be it a
template or a function accepting a reference of class, is to use
'throw(std::exception)'.
|
No, that's not the only option. You can also wrap exceptions that
occur whithin the code into some specific exception type(s) and
declare those type(s) in the exception specification.
(At least in Java you can, where every exception object has an
optional reference to another exception object which is called the
"cause" of the first one.)
The real question, in my view, is whether it really makes sense to
have a function specify several distinct exception types. I.e. whether
what a function does (according to its specification) can be divided
into different aspects so that it matters to the client code which
aspect failed.
A file-opening function for example could be considered to be
performing two distinct steps: (1) locating the file from the
specifed file path, and (2) acquiring a handle to that file with
certain associated rights. The function could specify two exception
types so that upon failure the caller can determine whether step 1
failed (file not found) or step 2 (access couldn't be obtained).
When implementing the function, it wouldn't matter what exceptions can
occur internally. Any exception would simply be mapped to (wrapped
into) one of the two above types (or subtypes thereof), depending on
where/why it occurred. This also means that changing the function's
implementation in a way which results in different exceptions
occurring internally does not in any way entail changing its exception
specification.
If exception specifications are viable at all, then as just described,
i.e. based upon the function's intent, not on its implementation.
Going back to the file-opening function example: A problem occurs when
the implementation of the function changes to use an infrastructure
layer which doesn't provide the distinction between (failure of) (1)
and (2). Or a third step/aspect could come up which doesn't really fit
with the existing two, so a failure here could not be appropriately
mapped to either of the declared exceptions.
Situations like these have made me cautious about the "exception
specifications based on function's intent" approach, which otherwise
would be fine. Maybe it's better to design code in a way so that
client code only ever has to worry about whether a call fails, but now
about how/why it failed. Then exception specifications would reduce to
at most one exception type in all cases, which would make the type
itself irrelevant. Essentially the only useful specification left
would be whether a functions can or cannot throw.
-- Niklas Matthies
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Steven T. Hatton Guest
|
Posted: Mon Aug 16, 2004 10:53 am Post subject: Re: Exceptions:was:Re: Templates vs. ABC interfaces |
|
|
Hyman Rosen wrote:
| Quote: | Steven T. Hatton wrote:
It's probably why many people don't use exceptions in C++.
My experience with exception handling in C++ has been that many compilers
fail to implement it properly. On both Sun and Microsoft compilers I have
trouble using them because they just don't work.
|
My first exposure to C++ exceptions was in the mid 90s, in my one and only
C++ course. I recall that I found them a bit confusing, but, since I
barely touched C++ again until about a year ago, I don't recall exactly
what was confusing. When I reread Bjarne's discussion of them, and
actually tried to understand them, I realized the reason I found them
confusing is because they are confusing. He tells us "[T]he fundamental aim
of the error-handling techniques is to pass information about an error from
the original point of detection to a point where there is sufficient
information available to revover from the problem, and to do so reliably
and conveniently." He also tells us that not all exceptions are "errors".
One example he shows is the use of exceptions to indicate a change in the
state of a queue. I believe he also suggests this may not be an ideal use
for exceptions. Since I haven't done any concurrent programming in C++, I
don't know how likely it is to use exceptions handling to detect a change
of state in a thread, but I do know that is done in Java. It's been
pointed out that exception based handling of such situations typically
leads to lower performance than the 'traditional' alternatives.
From my experience the most valuable use of exceptions is to provide
troubleshooting information. Though that is certainly not the only use.
They can be used to indicate a timeout on a search request, failure to find
a resource such as a file, etc. Sometimes you want to communicate that
information to the user, other times you want to have the program take an
alternate path of execution at that point.
The reason I'm mentioning all of this is because it is relevant to what
could or should be put into an exception. One argument might be that
exceptions which contain data will require more overhead than empty
classes. That argument seems spurious to me because exceptions are meant
to be exceptional. If they are used so often that the price of allocating
resources becomes a significant consideration, they are probably being used
incorrectly. Or, I might even say, by definition they are being used
incorrectly.
Bjarne tells us he never throws a built in object such a an int. I say that
should not even be an option. For the most part, throwing such an object
seems analogous to standing in a large empty water tank and firing a
pistol.
Some people argue that a requirement that all exceptions be derived from
std::exception precludes the use of 'foreign' exceptions which might be
thrown by calls to funcitons written in other languages, etc. To that I
say there should be a mechanism provided for handling exceptions at the
periphery of a compilation unit. IOW, you can catch it, but you can't
rethrow it into the C++ call stack.
As for requiring a throw(exception spec) on every function that throws an
exception. Yes it can be a hastel. It adds to the size of the code, and
can slightly impact the readability of the other parts. The advantage is
that the compiler, and even the IDE can detect that you do or do not throw
a checked exception. If you try to compile a program that doesn't handle
every checked exception, you will get an error message telling you what
exception you failed to catch. Your program is guaranteed not to abort due
to the failure to catch a checked exception.
When you link different object files together, since they have been built
according to the declarations in their respective interfaces, (my wording
may be off, but I believe the ideas are correct) you know the dll (yes, .so
files are dll's) you load will not throw any checked exceptions that you
don't know about, and handle.
And you should be able to disable the different restrictions on exceptions
so that older code can be used. Ideally you would want to protect the
newer code from the older using the perimiter guard mechanism I suggested
above.
There may even be a way to bracket your compilation/translation unit so that
you can work within it without the requirement to specify everything you
throw, and only be specific at the interface, but I really have no idea how
to do such a thing. Perhaps Eckel's smuggling pattern could be used. Just
don't tell the newbies how to do it!
--
Regards,
Steven
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Eugene Gershnik Guest
|
Posted: Mon Aug 16, 2004 2:56 pm Post subject: Re: Templates vs. ABC interfaces |
|
|
Hyman Rosen wrote:
| Quote: | Experience with Java shows that
specifying which exceptions a function can throw is a terrible idea,
|
I am not sure that this is what experience with Java shows. Some people have
made this claim but it remains controversial. Personally my experience was
quite the opposite.
| Quote: | because the ideal of exception handling is to ignore and allow to pass
through any exceptions you don't care about.
|
No. IMHO the ideal of exception handling is to handle all exceptions for
which the function you write is the best place to handle it in the system.
If you beleive that you are not the best handler for a particular exception
then by all means pass it through. Otherwise you must handle it even if you
do not care about it.
In a multi-layered system each layer serves to hide layers below it. As such
it usually must _not_ propagate exceptions thrown by the lower layer code.
This is because there is usually no way in which a higher layer can handle a
low level exception better than a middle layer. (There are exceptions to
this rule but they are rare). The middle layer must either handle low-level
exceptions or throw one of its own (possibly including the original one as
the 'cause').
To give a specific example if I use a security library I don't want and
cannot handle low-level IOException that it encountered when writing to some
internal stream. Even though the author of the security library may not care
about this exception he should better do something about it rather than
parrot it in his interface. He can handle it or if he cannot he should throw
a SecurityException. My top-level code would of course be prepared to deal
with SecurityExceptions while using security library.
| Quote: | It is utterly useless to
decorate functions with lists of exceptions that any downstream call
can throw, and it costs stupid amounts of effort to go around
modifying
all the callers when a low level function decides it wants to throw a
new exception.
|
True but the problem is not ES but lack of design and understanding how to
use them.
Exceptions in Java are part of the function interface. If I declare my
middle level function to throw only NetworkException I must stick to this
contract. Whatever the layers below me throw now or will throw in the future
will have to be handled or converted to NetworkException.
It is true that if the lower layer changes the exceptions thrown I will have
to go over the middle layer code and fix all the problems that prevent it
from compiling. Which is a _good thing_. The language prevents me from
forgetting to handle new errors that resulted from a change in the
implementation.
Now if one does disregards good design and simply parrots each low-level
exception in each function interface he gets what happens when good design
is disregarded -- unmaintainable code.
--
Eugene
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Nicola Musatti Guest
|
Posted: Mon Aug 16, 2004 3:06 pm Post subject: Re: Exceptions:was:Re: Templates vs. ABC interfaces |
|
|
I don't think that Java's approach to exception can really compared to
C++'s one. Even disregarding the fact that in C++ you can throw
instances of native types, there are a few issues that set the two
worlds apart:
- C++ has templates, which means that it is rather common to use the
same code
on classes that are not related by inheritance. Enforcing compile
time
exception specification checking is much more an hindrance than in
Java.
In Java it may be perceived as a nuisance; in C++ it would actually
force
artificial coupling between unrelated entities.
- C++ classes can clean up after themselves, thanks to the existence
of
deterministic destruction. This means that transparency to
exceptions we
cannot deal with is a desirable property which should be sought. On
the other
hand in Java you can perform cleanup only explicitly, which makes it
necessary
to take exceptions into account by using the finally clause.
As it often happens, what may be good for a language is terrible for
another one.
Cheers,
Nicola Musatti
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Mon Aug 16, 2004 7:56 pm Post subject: Re: Templates vs. ABC interfaces |
|
|
"Steven T. Hatton" <susudata (AT) setidava (DOT) kushan.aa> wrote
| Quote: | typedef ABC_interface Abstract_Baseclass_interface
(generally meaning all functions are pure virtual.)
|
Hmmm. In my abstract base classes, usually none of the public functions
are virtual. The abstract base class defines a contract, and the public
functions enforce that contract (passing on to private pure virtual
functions to do the actual work).
| Quote: | I do have the sense that there are places where ABC interfaces offer a
comperable solution to the use of templates.
|
There are places where they are the only reasonable solution. There are
other places where templates are the only reasonable solution. And there
are a lot of places where a simple class, not templated, and with no
virtual functions, is the only reasonable solution; IMHO, both ABC's and
templates tend to get overused. Both add complexity to the program, and
should only be used when the pay-off is higher than the cost.
| Quote: | They offer certain advantages over templates such as strong(er)
typing, faster compilation, easier to read code (YMMV), etc.
|
Faster compilation, certainly. Generally, more understandable error
messages as well:-). For the rest, it depends. If you try to use the
wrong tool, the results will be less readable than if you use the right
tool. Regardless of the tool. Most of the time, the choice between ABC's
and templates will be made on the basis of design criteria. Using the
tool against the design criteria will result in harder to read code.
With regards to type checking -- one of the main reasons for using
templates is compiler enforcement of predicates over the type system. An
obvious, albeit trivial and a bit stupid example, would be some function
like:
void
doIt( FwdIter begin, FwdIter end ) ;
If FwdIter is a base class (and I pass by reference), it's anybody's
guess as to whether begin or end are the same type or not. If FwdIter is
a template parameter, they are the same type, or the code doesn't
compile.
IMHO, this is one of the most important advantages of templates. And you
don't have to be a meta-programming wizard to take advantage of it.
| Quote: | The arguments against a more structured exception handling mechanism
in C++ I've met have to do with problems of abstracting the exception
specification list so that the mechanism can be supported using
templates. I'm really trying to get a handle on understanding this
particular (appearant) limitation. I believe it is a significant
compromise that makes C++ code potentially less robust than it might
otherwise be. This is part of my motivation for discussing this topic,
but, by far, not the only one. I believe the comparison between using
ABC interfaces verses templates is a significant topic worth
investigating for many reasons.
|
I don't see where ABC's are any different from templates with regards to
this. About the only difference is that an ABC can use an exception
specification clause, and this constrains the derived class
implementation to do likewise. Sometimes, this is what you want. Often,
it isn't.
| Quote: | Stroustrup tells us in §13.6.1 of TC++PL(SE) "If no hierarchical
relationship is required between these objects, they are best used as
template arguments. If the actual types of these objects cannot be
known at compile-time, they are best represented as classes derived
from a common abstract class. If run-time efficiency is at a premium,
that is, if inlining of operations is essential, a templated should be
used."
|
And if there is no hierarchial relationship between objects, but
run-time resolution is required:-)?
Seriously, there is a certain orthogonality in these characteristics.
Generally, runtime resolution is more flexible. But results in errors
being caught later.
Requiring an ABC makes using the function on existing types almost
impossible -- existing types don't know about your ABC, and can't derive
from it, even if they meet the requirements you set for a type. On the
other hand, it allows some pretty strict runtime enforcement of a
contract. If what you require of a class is complex and generally non
standardized, then there is a distinct advantage in requiring the user
to explicitly say that he intends to implement it, by deriving from your
interface. If what you require of a class is just some standard sort of
behavior -- at lowest level, that you can copy and assign it, then
requiring derivation from some specific class is nothing but a pain,
resulting in useless additional complexity.
| Quote: | It appears to me the use of templates often means there will be some
lack of information that would (should) otherwise be available in the
interface of the function or class the timeplate represents in a give
context. For example, the type parameters passed to a template cannot,
AFAIK, be restricted in such a way that tells the programmer what can
or can't be used. The programmer must have some other means of knowing
what the template expects; either through documentation or examining
the template itself. Trial and error is also a candidate.
|
There are ways. There is also, I believe, some discussion going on about
ways to improve them. But this is generally true, even for ABC's. For
better or worse, we'll never reach a state where all of the requirements
can be verified by the compiler. (How would you go about verifying that
the function GUIComponent::draw() actually met its postcondition, that
the object was correctly drawn on the screen? At some point, all you
have is documentation, and the hope that the user reads it.)
| Quote: | Arnold Gosling and Holmes have in interesting epigraph at the
beginning of their chapter on collection: "The problem with people who
have no vices is that generally speaking you can be pretty sure
they're going to have some very annoying virtues." - Elizabeth Taylor.
I didn't really appreciate that quote until I started learning about
templates. I haven't studied the recently introduced generic
programming support in Java, but, before it was introduced, the only
general means of creating collections - analogous to C++ containers -
was to create containers of type Object, and, either rely on the
correct type object being put in them, and/or use some kind of
introspection and casting to get back what you originally put in. IOW,
it basically circumvents type checking.
|
Not at all. It DOES defer it to run-time, so that when the error is
detected, it isn't when you are compiling the code on your machine, but
when your customer is using it on theirs.
Note that some languages, such as Smalltalk or Lisp, are based entirely
on this principle. I forget where, but I once read something along the
lines of: "Most programmers in statically type-checked languages
seriously underestimate the loss of flexibility compared to dynamic
type-checking. And most programmers in dynamically type-checked
languages seriously underestimate the loss of safety and robustness that
static type checking gives."
I'm basically a programmer in statically type-checked languages, but I
did to one application in Java in which in a significant part, we only
dealt with Object, casting as needed to get whatever interface we
needed. And yes, it was amazingly flexible. But I tend to work on
applications where robustness and up-time are critical -- safety and
robustness are more important to me than flexibility.
And where do templates fit into this. Only that they largely give the
flexibility of dynamic typechecking, with the safety of static
typechecking, because the type resolution IS done at compile time. Of
course, if you are in a position where you might really not be able to
know the type until run-time, you can't use it. And from a practical
standpoint... if the code gets too big, you can't use it. How many
functions really use the fact that you don't know the actual type of a
istream until runtime. But do you really want every function which uses
an istream to be a template? Could your compiler actually handle such a
program?
| Quote: | The advantage over the template counterpart is that it is easier to
learn, and works well for many situations where the programmer can
rely on the correct type being passed as a parameter, etc. I'm
confident a comparable set of container classes could be created to
hold C++ objects which all derive from some generic baseclass.
|
It's already been done. NIHCL.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
llewelly Guest
|
Posted: Mon Aug 16, 2004 8:10 pm Post subject: Re: Templates vs. ABC interfaces |
|
|
Dietmar Kuehl <dietmar_kuehl (AT) yahoo (DOT) com> writes:
[snip]
| Quote: | My personal take on exception specifications is that the idea is
entirely wrong in the first place: exceptions are a tool to handle
errors at a location quite far away from where the error occured.
Exceptions are entirely inappropriate to handle errors locally.
However, exception specifications are a rather local thing,
|
But there is one aspect of exceptions which is unavoidably local:
exception-safety. Each function an exception may pass through
must provide some known and useful degree of exception safety.
This is impossible without knowing which operations throw. (However,
C++ exception specifications are any more helpful in this respect
than documentation, and Java exception specifications are only
marginally better.)
| Quote: | especially when it comes to generic code, be it statically or
dynamically generic: if I pass an object into a generic function
(i.e. a member of an ABC or function with a templated parameter),
I know very well what kind of errors are associated with this object
and I can be prepared to handle the associated errors. However,
the generic function is unlikely to have even the remotest idea of
what my errors can be but still insists on nailing down the kinds
of errors! Hence, the idea that an exception specification can ever
restrict the kinds of errors which can be caused is completely
wrong.
[snip] |
This is only half the story. Templates use three kinds of names:
dependent names, non-dependent names, and internal names. The
exceptions thrown by internal and non-dependent operations are no
more difficult to nail down than are ordinary functions called
from a non-template. If exception specifications did not apply to
dependent names, they would be every bit as useful for templates
as they are for non-templates. (Of course,
exception-specifications are so busted that doesn't help much.)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|