Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Custom Exception Class

11 views
Skip to first unread message

eed...@filenet.com

unread,
May 18, 1999, 3:00:00 AM5/18/99
to
Hello

I am new to exception handling in C++ and am thinking of creating a
custom exception class. Is it wise and or desirable to create it using
the exception class defined by the C++ standard library as its base
class?

Thanks,

E. Edeen
eed...@filenet.com


--== Sent via Deja.com http://www.deja.com/ ==--
---Share what you know. Learn what you don't.---

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Ross Smith

unread,
May 18, 1999, 3:00:00 AM5/18/99
to
eed...@filenet.com wrote in message <7hs4ui$f4e$1...@nnrp1.deja.com>...

>
>I am new to exception handling in C++ and am thinking of creating a
>custom exception class. Is it wise and or desirable to create it using
>the exception class defined by the C++ standard library as its base
>class?

Yes, this is generally considered a good idea. It's especially good if
your code is likely to be re-used in other people's programs -- they
know that, if all else fails, they can always catch std::exception and
be sure of getting any exception you throw, without having to arrange
their code to fit the details of your exception scheme.

(I usually derive my exceptions from std::runtime_error, but that's
just to simplify initialising the what() string -- I agree with Dr
Stroustrup (C++PL3, ch.14) that the logic_error/runtime_error
distinction isn't terribly useful.)

My main() functions tend to look like this:

int main() {
try {
// ... code goes here ...
return 0;
}
catch (std::exception& ex) {
std::cerr << "*** " << ex.what() << std::endl;
return EXIT_FAILURE;
}
}

--
Ross Smith ................................... mailto:ros...@ihug.co.nz
.............. The Internet Group, Auckland, New Zealand ..............
"Perl is the Unix way. 500 million ways of doing the same thing,
and 500 million monster egos all insisting on their way being
the Proper way of doing it." -- David Parsons

Dominic Williams

unread,
May 19, 1999, 3:00:00 AM5/19/99
to
Ross Smith wrote:
>
> eed...@filenet.com wrote in message <7hs4ui$f4e$1...@nnrp1.deja.com>...
> >
> >I am new to exception handling in C++ and am thinking of creating a
> >custom exception class. Is it wise and or desirable to create it using
> >the exception class defined by the C++ standard library as its base
> >class?
>
> Yes, this is generally considered a good idea. It's especially good if
> your code is likely to be re-used in other people's programs -- they
> know that, if all else fails, they can always catch std::exception and
> be sure of getting any exception you throw, without having to arrange
> their code to fit the details of your exception scheme.

Personally, I don't think this is such a good idea. I see the
std::exception hierarchy as being the exceptions thrown by the std
library. If I start throwing some myself I am not helping users of my
code know that the exception was thrown
by my own library, not from the std library.

If your code is likely to be re-used, use exception-specifications
properly.
They tell people *exactly* what each function they call may throw.

Matt Seitz

unread,
May 20, 1999, 3:00:00 AM5/20/99
to

Dominic Williams <d.wil...@csee-transport.fr> wrote in message
news:374275BE...@csee-transport.fr...

> Personally, I don't think this is such a good idea. I see the
> std::exception hierarchy as being the exceptions thrown by the std
> library. If I start throwing some myself I am not helping users of my
> code know that the exception was thrown
> by my own library, not from the std library.

Your position makes sense to me. However, in section 14.10 of THE C++
PROGRAMMING LANGUAGE, Dr. Stroustrup writes:

"Similarly, it would be a mistake to assume that every exception derived
from 'exception' is a standard library exception: programmers can add their
own exceptions to the 'exception' hierarchy."

It is not clear to me which method Dr. Stroustrup actually prefers. He
seems to simply be saying that both methods (deriving and not deriving from
'exception') are in use, so one should not make any assumptions about how a
particular programmer will define their exceptions.

Matt Seitz

unread,
May 20, 1999, 3:00:00 AM5/20/99
to

Ross Smith <ros...@ihug.co.nz> wrote in message
news:7hsjuo$637$1...@newsource.ihug.co.nz...

> eed...@filenet.com wrote in message <7hs4ui$f4e$1...@nnrp1.deja.com>...
> >
> >I am new to exception handling in C++ and am thinking of creating a
> >custom exception class. Is it wise and or desirable to create it using
> >the exception class defined by the C++ standard library as its base
> >class?
>
> Yes, this is generally considered a good idea. It's especially good if
> your code is likely to be re-used in other people's programs -- they
> know that, if all else fails, they can always catch std::exception and
> be sure of getting any exception you throw, without having to arrange
> their code to fit the details of your exception scheme.

In section 14.10 of THE C++ PROGRAMMING LANGUAGE, Dr. Stroustrup writes:

"The standard exceptions are derived from 'exception'. However, not every
exception is, so it would be a mistake to try to catch every exception by
catching 'exception'."

The correct way to "be sure of getting any exception you throw" is to use
the "catch(...)" exception handler.

Dave Abrahams

unread,
May 20, 1999, 3:00:00 AM5/20/99
to
> If your code is likely to be re-used, use exception-specifications
> properly.
> They tell people *exactly* what each function they call may throw.

Yes, but they enforce that guarantee by calling terminate() to avoid
violating it.

If your code is likely to be maintained and needs to run on a system where
termination is not an acceptable recovery action, avoid all exception
specifications except possibly the empty one (throw()), and then only use
that if you know your compiler can use it to help with optimization.

Ross Smith

unread,
May 21, 1999, 3:00:00 AM5/21/99
to
Matt Seitz wrote in message ...

>
>Ross Smith <ros...@ihug.co.nz> wrote in message
>news:7hsjuo$637$1...@newsource.ihug.co.nz...
>> eed...@filenet.com wrote in message <7hs4ui$f4e$1...@nnrp1.deja.com>...
>> >
>> >I am new to exception handling in C++ and am thinking of creating a
>> >custom exception class. Is it wise and or desirable to create it using
>> >the exception class defined by the C++ standard library as its base
>> >class?
>>
>> Yes, this is generally considered a good idea. It's especially good if
>> your code is likely to be re-used in other people's programs -- they
>> know that, if all else fails, they can always catch std::exception and
>> be sure of getting any exception you throw, without having to arrange
>> their code to fit the details of your exception scheme.
>
>In section 14.10 of THE C++ PROGRAMMING LANGUAGE, Dr. Stroustrup writes:
>
>"The standard exceptions are derived from 'exception'. However, not every
>exception is, so it would be a mistake to try to catch every exception by
>catching 'exception'."
>
>The correct way to "be sure of getting any exception you throw" is to use
>the "catch(...)" exception handler.

You didn't read what I wrote. The question was whether deriving all
your exceptions from std::exception was a good idea, and I said that
*if you do* then users of your code can be sure of catching them all
with catch(std::exception&). Catch(...) is fairly useless, since
there's no way of getting any information out of the exception.

If somebody throws an exception that isn't derived from a known type,
there's no point in trying to catch it. You might as well let it
propagate out of main() and kill the program, since at least the
runtime library's shutdown code, unlike the programmer, has a non-zero
chance of being able to come up with a meaningful error message.

--
Ross Smith ................................... mailto:ros...@ihug.co.nz
.............. The Internet Group, Auckland, New Zealand ..............
"Perl is the Unix way. 500 million ways of doing the same thing,
and 500 million monster egos all insisting on their way being
the Proper way of doing it." -- David Parsons

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Esa Pulkkinen

unread,
May 22, 1999, 3:00:00 AM5/22/99
to

"Ross Smith" <ros...@ihug.co.nz> writes:
> You didn't read what I wrote. The question was whether deriving all
> your exceptions from std::exception was a good idea, and I said that
> *if you do* then users of your code can be sure of catching them all
> with catch(std::exception&).

This is not very useful, if your compiler didn't support
std::exception. Some code still needs to be portable to such
compilers.

> Catch(...) is fairly useless, since there's no way of getting any
> information out of the exception.

This is a common misconception; here's how to get information from
the exception in the catch(...) handler:

try { /* ... */ }
catch (...)
{
try { throw; }
catch (std::exception &e)
{
cout << "Exception:" << e.what() << endl;
}
}

It's even possible to move the code in the handler to another function
and reuse it. This is very useful in translating exceptions from 3rd
party libraries to exceptions that your application can properly
handle.

AND it's also possible to _change_ the thrown object within a
catch(...) handler, i.e. to add new information to the existing object
before you rethrow it.

So, I would even assert that catch(...) is the most useful form of
exception handler, and should be preferred over catching specific
exceptions _except_ in the reused handler function(s).

> If somebody throws an exception that isn't derived from a known type,
> there's no point in trying to catch it.

Sure there is ; you can do cleanup in the catch handler. But sure, if
you don't know (any bases of) the type of the exception, you can only
rethrow it (and THEN catch it with a proper handler!).
--
Esa Pulkkinen | C++ programmers do it virtually
E-Mail: es...@cs.tut.fi | everywhere with class, resulting
WWW : http://www.cs.tut.fi/~esap/ | in multiple inheritance.

Dave Abrahams

unread,
May 22, 1999, 3:00:00 AM5/22/99
to
In article <7i2fjk$co9$1...@newsource.ihug.co.nz> , "Ross Smith"
<ros...@ihug.co.nz> wrote:

> If somebody throws an exception that isn't derived from a known type,

> there's no point in trying to catch it. You might as well let it
> propagate out of main() and kill the program, since at least the
> runtime library's shutdown code, unlike the programmer, has a non-zero
> chance of being able to come up with a meaningful error message.

That all depends on whether you value a meaningful error message more than
recovery.

Ross Smith

unread,
May 23, 1999, 3:00:00 AM5/23/99
to
Esa Pulkkinen wrote in message <87btfe5...@c156a.w2.ton.tut.fi>...

>
>"Ross Smith" <ros...@ihug.co.nz> writes:
>> You didn't read what I wrote. The question was whether deriving all
>> your exceptions from std::exception was a good idea, and I said that
>> *if you do* then users of your code can be sure of catching them all
>> with catch(std::exception&).
>
>This is not very useful, if your compiler didn't support
>std::exception. Some code still needs to be portable to such
>compilers.

Personally, if I was stuck with tools that far out of date, I'd start
looking for a new employer.

>This is a common misconception; here's how to get information from
>the exception in the catch(...) handler:
>
>try { /* ... */ }
>catch (...)
>{
> try { throw; }
> catch (std::exception &e)
> {
> cout << "Exception:" << e.what() << endl;
> }
>}

How is this different from plain "catch (std::exception&)"? If the
exception isn't derived from std::exception, you just catch it and
immediately rethrow it. Pointless.

>> If somebody throws an exception that isn't derived from a known type,
>> there's no point in trying to catch it.
>

>Sure there is ; you can do cleanup in the catch handler.

Far better to use the "resource acquisition is initialisation" idiom.
Wrap every resource in a class that cleans up in its destructor, and
forget about cleanup. The ability to do this is one of the things I
like most about C++ (and the inability to do this is one of the things
I hate most about Java, Delphi, and any number of other pseudo-OO
languages).

--
Ross Smith ................................... mailto:ros...@ihug.co.nz
.............. The Internet Group, Auckland, New Zealand ..............
"Perl is the Unix way. 500 million ways of doing the same thing,
and 500 million monster egos all insisting on their way being
the Proper way of doing it." -- David Parsons

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Ross Smith

unread,
May 23, 1999, 3:00:00 AM5/23/99
to
Dave Abrahams wrote in message
<_bn13.6338$AL5....@ndnws01.ne.mediaone.net>...

>In article <7i2fjk$co9$1...@newsource.ihug.co.nz> , "Ross Smith"
><ros...@ihug.co.nz> wrote:
>
>> If somebody throws an exception that isn't derived from a known type,
>> there's no point in trying to catch it. You might as well let it
>> propagate out of main() and kill the program, since at least the
>> runtime library's shutdown code, unlike the programmer, has a non-zero
>> chance of being able to come up with a meaningful error message.
>
>That all depends on whether you value a meaningful error message more than
>recovery.

There's a wise old saying: "Never test for an error condition you don't
know how to handle". If you don't know what went wrong, how can you
possibly recover from it?

If the situation is safety-critical and the program *must* recover from
any error, the only way to do it is to be absolutely certain that no
code you call will ever throw an exception not derived from a known
type.

Matt Seitz

unread,
May 24, 1999, 3:00:00 AM5/24/99
to

Ross Smith <ros...@ihug.co.nz> wrote in message
news:7i2fjk$co9$1...@newsource.ihug.co.nz...

> Matt Seitz wrote in message ...
> >
> >Ross Smith <ros...@ihug.co.nz> wrote in message
> >> Yes, this is generally considered a good idea. It's especially good if
> >> your code is likely to be re-used in other people's programs -- they
> >> know that, if all else fails, they can always catch std::exception and
> >> be sure of getting any exception you throw, without having to arrange
> >> their code to fit the details of your exception scheme.
> >
> >In section 14.10 of THE C++ PROGRAMMING LANGUAGE, Dr. Stroustrup writes:
> >
> >"The standard exceptions are derived from 'exception'. However, not
every
> >exception is, so it would be a mistake to try to catch every exception by
> >catching 'exception'."
> >
> >The correct way to "be sure of getting any exception you throw" is to use
> >the "catch(...)" exception handler.
>
> You didn't read what I wrote. The question was whether deriving all
> your exceptions from std::exception was a good idea, and I said that
> *if you do* then users of your code can be sure of catching them all
> with catch(std::exception&). Catch(...) is fairly useless, since

> there's no way of getting any information out of the exception.

I did read what you wrote, but I misread it. I missed that you said that a
user could catch all of the exceptions from "your code", not all code. I
apologize for my error.

Paul D. DeRocco

unread,
May 25, 1999, 3:00:00 AM5/25/99
to
Esa Pulkkinen wrote:

>
> "Ross Smith" <ros...@ihug.co.nz> writes:
>
> > Catch(...) is fairly useless, since there's no way of getting any
> > information out of the exception.
>
> This is a common misconception; here's how to get information from
> the exception in the catch(...) handler:
>
> try { /* ... */ }
> catch (...)
> {
> try { throw; }
> catch (std::exception &e)
> {
> cout << "Exception:" << e.what() << endl;
> }
> }

I think what Ross meant was that if the only way you have of catching an
exception is by "...", because you don't know anything about the type of
the exception, then you can't get any information about the exception,
other than that it occurred, which is "fairly useless."

--

Ciao, Paul D. DeRocco
Paul mailto:pder...@ix.netcom.com

Paul D. DeRocco

unread,
May 25, 1999, 3:00:00 AM5/25/99
to
Dominic Williams wrote:
>
> Personally, I don't think this is such a good idea. I see the
> std::exception hierarchy as being the exceptions thrown by the std
> library. If I start throwing some myself I am not helping users of my
> code know that the exception was thrown
> by my own library, not from the std library.

Why do you care whether an exception came from the library or not? If
you care specifically what the exception is, then catch that exception.
If you don't care specifically what it is, I can't see why the fact that
it came from the library or some other code would be interesting.

The semantics of std::exception are generally useful, because of the
what() member. If you invent another exception that isn't derived from
std::exception, you should probably give it the equivalent
functionality. This is because one of the common responses to
exceptions, especially during debug, is to display what() and either
terminate or reenter the main processing loop. Any time you have the
same semantic requirement as an existing base class, it makes sense to
derive from that class, so you don't wind up with two catch clauses
saying exactly the same thing.

If you have some reason which escapes me for needing to know whether an
exception is standard or not, you can derive all your exceptions from
std::exception and a dummy class called my_exception. If you can't
dynamic_cast the exception to a my_exception, then it's a standard
exception.

Dominic Williams

unread,
May 25, 1999, 3:00:00 AM5/25/99
to
Matt Seitz wrote:
> It is not clear to me which method Dr. Stroustrup actually prefers. He
> seems to simply be saying that both methods (deriving and not deriving from
> 'exception') are in use, so one should not make any assumptions about how a
> particular programmer will define their exceptions.

I would not like to speak for him, but sentences (all in 14.10) like

"This seems rather elaborate for organizing the eight standard
exceptions. This hierarchy attempts to provide a framework for
exceptions [...] Some people view this as a useful framework for all
errors and exceptions. I don't."

seem fairly clear to me.

Anyway, I agree with both of you about not making assumptions about what
other people have done. I have simply made it a rule in our coding
guidelines not to derive from the std::exception hierarchy, nor to throw
objects of its classes (although they may be propagated, of course...).

Dominic Williams

unread,
May 25, 1999, 3:00:00 AM5/25/99
to
Dave Abrahams wrote:
>
> > If your code is likely to be re-used, use exception-specifications
> > properly.
> > They tell people *exactly* what each function they call may throw.
>
> Yes, but they enforce that guarantee by calling terminate() to avoid
> violating it.

Actually, they call unexpected(), for which you can provide your own
handler and avoid terminate()ing.

This can be done rather elegantly (see Stroustrup C++ Prog. Lang.
14.6.3.1)
using a store and reset class whose objects reset the handler as long as
they
are in scope, and set it back when they go out of scope. Unexpected
exception
can thus be mapped to exceptions that were in the throw specification.

> If your code is likely to be maintained and needs to run on a system where
> termination is not an acceptable recovery action, avoid all exception
> specifications except possibly the empty one (throw()), and then only use
> that if you know your compiler can use it to help with optimization.

One could argue that if termination is not an acceptable recovery
action, it
is IMPERATIVE to provide complete throw specifications... programmers
need to know everything that might be thrown, because uncaught
exceptions
also terminate by default, just like unexpected ones !

Dave Abrahams

unread,
May 25, 1999, 3:00:00 AM5/25/99
to
In article <7i9t37$qr7$1...@newsource.ihug.co.nz> , "Ross Smith"
<ros...@ihug.co.nz> wrote:

> There's a wise old saying: "Never test for an error condition you don't
> know how to handle". If you don't know what went wrong, how can you
> possibly recover from it?

In many situations, it's simple. For example, imagine a database server
which finds out that one step in an atomic transaction failed (but not why
it failed). The recovery is straightforward: roll back the other steps,
report a failure to the client, and continue running.

> If the situation is safety-critical and the program *must* recover from
> any error, the only way to do it is to be absolutely certain that no
> code you call will ever throw an exception not derived from a known
> type.

Not at all. Most error recovery has nothing to do with the information
content of the error message (type of exception), only where in the control
flow it occurred. That info usually only matters for good error _reporting_.

Dave Abrahams

unread,
May 27, 1999, 3:00:00 AM5/27/99
to
In article <374A39F6...@csee-transport.fr> , Dominic Williams
<d.wil...@csee-transport.fr> wrote:

>>
>> Yes, but they enforce that guarantee by calling terminate() to avoid
>> violating it.
>
> Actually, they call unexpected(), for which you can provide your own
> handler and avoid terminate()ing.

That's just a technicality. unexpected isn't allowed to return normally. It
has to either throw a new exception or terminate. If that exception doesn't
match the exception specification, it terminates anyway. Using
exception-specifications means you are forced to know every exception that
can be thrown to avoid termination.

> This can be done rather elegantly (see Stroustrup C++ Prog. Lang.
> 14.6.3.1)
> using a store and reset class whose objects reset the handler as long as
> they
> are in scope, and set it back when they go out of scope. Unexpected
> exception
> can thus be mapped to exceptions that were in the throw specification.

It is unlikely that you'll succeed in doing this correctly through many
revisions of the libraries on which your code depends. These maintenance
problems are described by Stroustrup in his explanations of why
exception-specifications are not checked across compilation units (D&E, I
think). Even if you do succeed, you are almost certain to lose valuable
error-reporting information when new subclasses of these libraries'
exceptions are mapped to some other type. Often, this mapping is premature
because your clients are actually prepared to report errors based on those
exceptions.

> One could argue that if termination is not an acceptable recovery
> action, it
> is IMPERATIVE to provide complete throw specifications... programmers
> need to know everything that might be thrown, because uncaught
> exceptions
> also terminate by default, just like unexpected ones !

Not at all:

int main()
{
for(;;) {
try {
// body of program here
// poll inputs and process, for example.
} catch(...) {
report_error();
}
}
}

This handles all termination due to unexpected exceptions... unless of
course, you've used exception-specifications. In that case there is no hope.
One of the big problems with exception-specifications is that they take
control far too early.

-Dave

Lisa Lippincott

unread,
May 28, 1999, 3:00:00 AM5/28/99
to
Paul D. DeRocco <pder...@ix.netcom.com> wrote:

> I think what Ross meant was that if the only way you have of catching an
> exception is by "...", because you don't know anything about the type of
> the exception, then you can't get any information about the exception,
> other than that it occurred, which is "fairly useless."

In my experience, the sentiment expressed here is dead wrong. The fact
that an exception occured is the most useful piece of information.

The second most useful piece of information is which block of code
failed. This information is also available at the catch site: it's
the preceding try block.

To this wealth of information, std::exception adds what(),
"an implementation defined NTBS." To clarify, the standard points out
that this may or may not be a multibyte string. Where I come from, such
information is at best useful for debugging.

My advice is that if you want to display an error message from an
exception object, then get it from some method of your own design --
what() just isn't up to the task. My better advice is to construct
messages for the user after the exception is caught, because there's
not enough information available at the throw point.

As for deriving from std::excption: perhaps it will help your debugging.
But it also seems to encourage people to write "catch (std::exception&)"
where they should write "catch(...)."

--Lisa Lippincott

Phlip

unread,
May 29, 1999, 3:00:00 AM5/29/99
to
Alan Bellingham wrote:

> Lisa Lippincott <lisa_li...@advisories.com> wrote:
>
> >The second most useful piece of information is which block of code
> >failed. This information is also available at the catch site: it's
> >the preceding try block.
>
> ... or something called from within that block, possibly multiple call
> levels down. Building your won exceptions allow you to add far more
> precise information as to the locality, and this may be picked up from
a
> suitable catch block.

I just had a sadistic, but useful, idea. When running in Debug mode
(under the wild assumption that an exception might be something worth
debugging), put this at the top of every function:

void
func () {
Tracer t_ (__FUNC__);
...

Tracer has a static 'list< string >' that remembers all the functions on
the current stack.

Now pass 't_' into your custom exception, copy its function stack, and
throw your exception. The catcher can now report all the functions
called between 'main()' and the excepting function. Now in addition to
'what ()' you also have a powerful 'where ()' prop on your exception
object.

New question: Might the catch use this info - even in Release mode - to
pass judgement on the exception recovery? Or would that be bad style
(coupling to function names that should have been encapsulated)?

--
Phlip at politizen dot com (address munged)
======= http://users.deltanet.com/~tegan/home.html =======

Alf P. Steinbach

unread,
May 29, 1999, 3:00:00 AM5/29/99
to
In article <270519991233155844%lisa_li...@advisories.com>,

Lisa Lippincott <lisa_li...@advisories.com> wrote:
> Paul D. DeRocco <pder...@ix.netcom.com> wrote:
>
> > I think what Ross meant was that if the only way you have of
> > catching an exception is by "...", because you don't know
> > anything about the type of the exception, then you can't
> > get any information about the exception, other than that it
> > occurred, which is "fairly useless."
>
> In my experience, the sentiment expressed here is dead wrong.

Practical matter:

There's a big difference between "..." and any other exception
catcher, namely that in some implementations (notably Visual C++)
the "..." catcher may catch LOW-LEVEL EXCEPTIONS such as division
by zero or dereferencing an invalid pointer.

Low level exceptions are different from standard C++ exceptions
not only in their basic nature (e.g., asynchronous, machine-
dependent), but also in their severity: it's not a good idea to treat
them as just ordinary exceptions. Thus, the lack of information about
the kind of exception in a "..." catcher is very important. Without
such information a "..." catcher should, IMO, not be used.


> The fact
> that an exception occured is the most useful piece of information.
>

> The second most useful piece of information is which block of code
> failed. This information is also available at the catch site: it's
> the preceding try block.
>

> To this wealth of information, std::exception adds what(),
> "an implementation defined NTBS." To clarify, the standard points out
> that this may or may not be a multibyte string. Where I come from,
> such information is at best useful for debugging.

Fully agree, as regards catching standard C++ exceptions.

> My better advice is to construct
> messages for the user after the exception is caught, because there's
> not enough information available at the throw point.

Fully agree. Hear, hear! Why do so few programmers realize this?

> As for deriving from std::excption: perhaps it will help your
> debugging. But it also seems to encourage people to write "catch
> (std::exception&)" where they should write "catch(...)."

Again, I'd strongly advice *against* writing "catch(...)", due to
the possibility of catching low-level exceptions. If the compiler
supports exception specifications (stating which exceptions a
function may throw), then an alternative to "..." might be to
rethrow as a standard exception from the unexpected-exception
handler. But then, haven't used a conforming compiler so cannot
vouch for practicality of the idea. In the meantime, I consider
it much better if a bug is exposed through a crash de-luxe than
low-level exceptions being silently ignored in a "..." trap.


Cheers,

- Alf


--
alf_DOT_steinbach_AT_ac_DOT_com (clean the address before replying)


Sent via Deja.com http://www.deja.com/

Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Dave Abrahams

unread,
May 30, 1999, 3:00:00 AM5/30/99
to
{ This thread is drifting into the platform-specific; followups should
probably redirect to a more appropriate group. -vdv }

In article <7ipfie$hkb$1...@nnrp1.deja.com> , "Alf P. Steinbach"
<alf_st...@my-deja.com> wrote:

> Practical matter:
>
> There's a big difference between "..." and any other exception
> catcher, namely that in some implementations (notably Visual C++)
> the "..." catcher may catch LOW-LEVEL EXCEPTIONS such as division
> by zero or dereferencing an invalid pointer.

There's an easy way around this problem, as far as I know. I'm no VC++
expert, but my windows compiler (Metrowerks) supports this:

1. Turn off "structured EH-compatible exception handling"
2. Catch low-level ("structured") exceptions by using the same special
keywords that one uses to catch them from straight 'C' code (__try,
__except, __finally, __leave).

This avoids winding together two very different notions of exceptions as
though they were really the same thing.

I still have to do some emprical tests with VC++, but I a good compiler
ought to give you better run-time performance if you turn off structured EH,
because overheads can be eliminated:

< http://www.borland.com/borlandcpp/papers/cppexcp/cppexcp1.html> says of
structured EH:

The OS maintains a singly linked list of active exception handlers. Usually
each stack frame that wants to catch exceptions will establish a handler,
and hook it into the OS chain upon entry and remove it upon exit (please
note that all exit paths must do this, including return and longjmp).

Lisa Lippincott

unread,
May 30, 1999, 3:00:00 AM5/30/99
to
{ Environment specific replies should probably be redirected to a
more specialized group. -vdv }

Alf P. Steinbach <alf_st...@my-deja.com> wrote:

> There's a big difference between "..." and any other exception
> catcher, namely that in some implementations (notably Visual C++)
> the "..." catcher may catch LOW-LEVEL EXCEPTIONS such as division
> by zero or dereferencing an invalid pointer.

In many ways, I come from a sheltered environment. Can you elaborate
on these quirks of Visual C++, and why they make catch(...) impractical?

At first glance, throwing such exceptions seems like a reasonable
choice of "undefined behavior" for an implementation, provided that
they are delivered to the appropriate catch blocks. (Your use of the
word "asynchronous" troubles me in the latter respect.) But I haven't
thought deeply on the matter.

Other posters in this thread have taken the position that some conditions
are so horrible as to preclude recovery. (Including bad_alloc -- even
my environment isn't that sheltered!) I'm more optimistic: as long as
the implementation continues to faithfully execute my program, I can
hope to recover.

--Lisa Lippincott

Ivan J Johnson

unread,
May 30, 1999, 3:00:00 AM5/30/99
to
In article <7ipfie$hkb$1...@nnrp1.deja.com>,

"Alf P. Steinbach" <alf_st...@my-deja.com> wrote:
> [. . .]

> There's a big difference between "..." and any other exception
> catcher, namely that in some implementations (notably Visual C++)
> the "..." catcher may catch LOW-LEVEL EXCEPTIONS such as division
> by zero or dereferencing an invalid pointer.
>
> Low level exceptions are different from standard C++ exceptions
> not only in their basic nature (e.g., asynchronous, machine-
> dependent), but also in their severity: it's not a good idea to treat
> them as just ordinary exceptions. Thus, the lack of information about
> the kind of exception in a "..." catcher is very important. Without
> such information a "..." catcher should, IMO, not be used.
> [. . .]

>
> Again, I'd strongly advice *against* writing "catch(...)", due to
> the possibility of catching low-level exceptions. If the compiler
> supports exception specifications (stating which exceptions a
> function may throw), then an alternative to "..." might be to
> rethrow as a standard exception from the unexpected-exception
> handler. But then, haven't used a conforming compiler so cannot
> vouch for practicality of the idea. In the meantime, I consider
> it much better if a bug is exposed through a crash de-luxe than
> low-level exceptions being silently ignored in a "..." trap.

Are you saying that (under MSVC at least) the following would be a bad
idea?

void f()
{
try {
do_something(); // might throw
}
catch(...)
{
report_error();
}
}

void report_error()
{
try {
throw; // will be re-caught immediately
}
catch (std::exception& e) {
cerr << ex.what() << '\n';
}
catch (CException* pe) {
// . . .
}
catch (...) {
cerr << "unknown error\n";
}
}

--
Regards,
Ivan


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Alf P. Steinbach

unread,
May 31, 1999, 3:00:00 AM5/31/99
to
In article <290519991945358040%lisa_li...@advisories.com>,

Lisa Lippincott <lisa_li...@advisories.com> wrote:
> { Environment specific replies should probably be redirected to a
> more specialized group. -vdv }
>
> Alf P. Steinbach <alf_st...@my-deja.com> wrote:
>
> > There's a big difference between "..." and any other exception
> > catcher, namely that in some implementations (notably Visual C++)
> > the "..." catcher may catch LOW-LEVEL EXCEPTIONS such as division
> > by zero or dereferencing an invalid pointer.
>
> In many ways, I come from a sheltered environment. Can you elaborate
> on these quirks of Visual C++, and why they make catch(...)
impractical?
>
> At first glance, throwing such exceptions seems like a reasonable
> choice of "undefined behavior" for an implementation, provided that
> they are delivered to the appropriate catch blocks. (Your use of the
> word "asynchronous" troubles me in the latter respect.) But I haven't
> thought deeply on the matter.

Standard C++ exceptions are synchronous because they're explicitly
thrown, that is, will always occur *between* statements. A low-level
exception typically occurs as a result of a bad memory reference or an
arithmetic error, but it can also be an external signal, e.g.
someone killing the process. These things typically happen
asynchronously, at some arbitrary place *within* some statement.
For example, might happen in the middle of the call to a
constructor, which might foul up things so that recovery becomes
impossible.


> Other posters in this thread have taken the position that some
> conditions are so horrible as to preclude recovery.

That is self-evident. E.g., if the stack is corrupted the
only thing to do is to terminate. Not that I've experienced
that lately (in the last ten years or so), and I've no idea how to
make the program itself detect such a situation with confidence.


> (Including bad_alloc -- even
> my environment isn't that sheltered!)

Agreed regarding bad_alloc, but it depends on the context.


> I'm more optimistic: as long as
> the implementation continues to faithfully execute my program, I can
> hope to recover.

That, I think, is over on the philosophical plane. There's no way
to determine whether the program is still stable, except to let it
run and catch any exceptions. So a gut-feeling decision is made:
this exception is too bad, terminate, but this one is not too bad...


Cheers,

- Alf

--
alf_DOT_steinbach_AT_ac_DOT_com (clean the address before replying)

Alf P. Steinbach

unread,
May 31, 1999, 3:00:00 AM5/31/99
to
In article <7iqogd$as2$1...@nnrp1.deja.com>,
Ivan J Johnson <iva...@my-deja.com> wrote:
> In article <7ipfie$hkb$1...@nnrp1.deja.com>,

> "Alf P. Steinbach" <alf_st...@my-deja.com> wrote:
> > [. . .]

> > There's a big difference between "..." and any other exception
> > catcher, namely that in some implementations (notably Visual C++)
> > the "..." catcher may catch LOW-LEVEL EXCEPTIONS such as division
> > by zero or dereferencing an invalid pointer.
> >
>
> Are you saying that (under MSVC at least) the following would be a bad
> idea?
>
> void f()
> {
> try {
> do_something(); // might throw
> }
> catch(...)
> {
> report_error();
> }
> }
>
> void report_error()
> {
> try {
> throw; // will be re-caught immediately

I'm assuming above is meant to exemplify code that might
throw.

> }
> catch (std::exception& e) {
> cerr << ex.what() << '\n';
> }
> catch (CException* pe) {
> // . . .
> }
> catch (...) {
> cerr << "unknown error\n";
> }
> }
>

Yes, it is IMO a bad idea.

The design of function f would be reasonable for a user interface
level function, if (...) were replaced by ( exception const& x ).
So I'll assume f is meant as a very high level function. If it's
anywhere under the user interface level it has a duty to either
succeed or throw an exception -- but at the highest level "success"
might incorporate simply reporting a failure to the user, in the
ultimate case of passing the buck.

Assume do_something() dereferences a null pointer, divides by
zero, or some equally disastrous thing. This generates a low level
exception, caught by (...). An *unspecified* error is reported to
the user, or logged somewhere. Worse, the program will keep on
going in a possibly quite unstable state -- e.g., the math
coprocessor state might be less than ideal, or memory might be
really tight, or whatever.

The golden rule of exception handling is: "If you can't handle it,
pass the buck." (Steinbach's rule; the name similarity is just a
coincidence.) And that also applies at the user interface level.
It's better to crash (where the operating system will at least give
some indication of what went wrong, and typically also exactly
where it happened) than to eat the error and continue on. Imagine
someone in your organization doing that -- handling the easy,
known problems competently but collecting all unknown problems,
small as large, into nondescriptive notes he sends to you. After
getting 50 such notes one hour you stop looking at them. All
seems to be well, and the guy can't be fired, for some reason.

Hth.,

- Alf

(Actually my standpoint on this is a bit stronger than what I've
written: one should always use the *least general* catch possible.)

Dave Abrahams

unread,
May 31, 1999, 3:00:00 AM5/31/99
to
In article <7itmir$7ad$1...@nnrp1.deja.com> , "Alf P. Steinbach"
<alf_st...@my-deja.com> wrote:

>> void report_error()
>> {
>> try {
>> throw; // will be re-caught immediately
>
> I'm assuming above is meant to exemplify code that might
> throw.

No, that's a common and very useful technique. The code above is re-throwing
whichever exception is currently being handled.

Alf P. Steinbach

unread,
Jun 1, 1999, 3:00:00 AM6/1/99
to
In article <9FC43.10983$AL5....@ndnws01.ne.mediaone.net>,

"Dave Abrahams" <abra...@mediaone.net> wrote:
> In article <7itmir$7ad$1...@nnrp1.deja.com> , "Alf P. Steinbach"
> <alf_st...@my-deja.com> wrote:
>
> >> void report_error()
> >> {
> >> try {
> >> throw; // will be re-caught immediately
> >
> > I'm assuming above is meant to exemplify code that might
> > throw.
>
> No, that's a common and very useful technique. The code above is re
> -throwing whichever exception is currently being handled.

Hm. Throw without operands will call terminate() if there is
currently no exception being handled. For that reason alone it's
a bad idea: it precludes using report_error without throwing an
exception. Second, using an exception to pass parameter information
is akin to using a global variable: there's no control, and the
report_error signature doesn't communicate what is handled
gracefully and what is not.

Since the function is handling different types of exception,
including nonstandard and unknown types (where no information is
available), a better design would be to define a class T
encapsulating retrieval of information from exceptions, and pass
"T const&" to the function. The case of no information available
can then be handled gracefully by the T object.

Using an explicit parameter also allows passing information about
where the exception was caught (or failure detected).

Hth.,

- Alf


--
alf_DOT_steinbach_AT_ac_DOT_com (clean the address before replying)


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Dave Abrahams

unread,
Jun 2, 1999, 3:00:00 AM6/2/99
to
In article <7j0jv3$46h$1...@nnrp1.deja.com> , "Alf P. Steinbach"
<alf_st...@my-deja.com> wrote:

> Hm. Throw without operands will call terminate() if there is
> currently no exception being handled. For that reason alone it's
> a bad idea: it precludes using report_error without throwing an
> exception.

Of course. Report_error is only supposed to report errors related to
exceptions.

> Second, using an exception to pass parameter information
> is akin to using a global variable: there's no control, and the
> report_error signature doesn't communicate what is handled
> gracefully and what is not.

Huh? Who's passing parameter information?
If you're going to handle exceptions, using report_error and maintaining it
once beats having to rewrite it for every place where you need to catch and
report errors.

> Since the function is handling different types of exception,
> including nonstandard and unknown types (where no information is
> available), a better design would be to define a class T
> encapsulating retrieval of information from exceptions, and pass
> "T const&" to the function. The case of no information available
> can then be handled gracefully by the T object.

And how do you propose to get the information into your class T? I obviously
don't understand your idea. Care to post some concrete code?

-Dave

Ivan J Johnson

unread,
Jun 3, 1999, 3:00:00 AM6/3/99
to
In article <7j0jv3$46h$1...@nnrp1.deja.com>,
"Alf P. Steinbach" <alf_st...@my-deja.com> wrote:
> In article <9FC43.10983$AL5....@ndnws01.ne.mediaone.net>,
> "Dave Abrahams" <abra...@mediaone.net> wrote:
> > In article <7itmir$7ad$1...@nnrp1.deja.com> , "Alf P. Steinbach"

> > <alf_st...@my-deja.com> wrote:
> >
> > >> void report_error()
> > >> {
> > >> try {
> > >> throw; // will be re-caught immediately
> > >
> > > I'm assuming above is meant to exemplify code that might
> > > throw.
> >
> > No, that's a common and very useful technique. The code above is re
> > -throwing whichever exception is currently being handled.

Yes. Exactly. The idea is to take all the exceptions that might be
thrown from the try block in f() (in my previous posting, not in
report_error() above) and gather them in one place, where they can be
conveniently fed to a common handler. The alternative is to allow the
main path of execution to become littered with multiple catch-blocks,
which probably will contain a lot of replicated code from other catch-
blocks in other functions.

>
> Hm. Throw without operands will call terminate() if there is
> currently no exception being handled. For that reason alone it's
> a bad idea: it precludes using report_error without throwing an

> exception. Second, using an exception to pass parameter information


> is akin to using a global variable: there's no control, and the
> report_error signature doesn't communicate what is handled
> gracefully and what is not.

I'm not quite understanding this. Are you concerned that
report_error() might be called "accidentally," leading to abrupt
program termination? I don't see that as a problem, because I think
it's an unlikely mistake for a programmer to make and because
termination is difficult for a tester to ignore. However, if it
concerns you, you could do two things: (1) Change the name to
report_exception(). This name, plus suitable documentation, plus the
fact that the function does not take any exception as an argument,
should together make it unmistakably clear that the function requires
an exception to be already active. (2) Before the try-block in
report_exception, query std::uncaught_exception() to determine if an
exception is active. If it is not, throw std::logic_error or some
exception that you know that your framework will catch.

To me, exception-handling functions like this are the surest way of
making sure *all* errors get handled gracefully. Every error is
reported to the user, and no error is allowed to abruptly terminate the
program and possibly cause the user to lose data. If a program is
found to be throwing errors of a previously unknown type, that new type
can be accommodated in a few handler functions, rather than in catch-
blocks strewn throughout the program.

>
> Since the function is handling different types of exception,
> including nonstandard and unknown types (where no information is
> available), a better design would be to define a class T
> encapsulating retrieval of information from exceptions, and pass
> "T const&" to the function. The case of no information available
> can then be handled gracefully by the T object.

But how would you construct such an object? Either you need multiple
catch-blocks where the exception is first caught, or you need to hand
the exception off to a handler function, which re-throws and re-catches
the exception to determine its actual type and extract information from
it. Such a function is not limited just to reporting errors; it could
also modify the exception with additional context information and re-
throw it, or it could construct a T object and throw that.

>
> Using an explicit parameter also allows passing information about
> where the exception was caught (or failure detected).

That's a good point. It can be accommodated by changing the signature
of report_exception() to permit this:

catch {
report_exception(__FILE__, __LINE__);
}

>
> Hth.,
>
> - Alf

--
Regards,
Ivan


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Jerry Leichter

unread,
Jun 3, 1999, 3:00:00 AM6/3/99
to
| Standard C++ exceptions are synchronous because they're explicitly
| thrown, that is, will always occur *between* statements....

Neither part of this statement is true: The first because throws can
occur from within certain built-in operators - in particular, new; the
second because "throw x" is an *expression*. The second because there
are expressions not containing full statements that can throw - e.g.,
dynamic_cast. For that matter, "throw <expr>" is itself an expression
(with void type), and can occur within a ?: operator.

In any case, even ignoring the ways in which throws can *originate*
within expressions, if you're interested in the *effects* of exceptions,
there is really no useful distinction between a throw and a function
call from which an exception is thrown - and of course such a function
can be called from an arbitrary point in an expression.

I really don't see the difference between an exception raised in the
built-in new operator, and an exception raised in the built-in floating
divide operator (or for that matter a *user-defined* divide operator) -
except that of course the standard happens not to have defined any
exceptions that the built-in floating divide operator is allowed to
throw, so any such exception would be a non-conforming extension. But
it would hardly be contrary to the spirit of C++.

Now, if you're taking true asynchronous events, unconnected with the
flow of the program - e.g., external interrupts - and turning them into
exceptions, the story is different. For such things, you usually want a
way to continue execution from the point of interruption - something the
C++ exception facility doesn't provide. So it would not be a good
match. (On the other hand, consider the Posix thread cancellation
facility. It's not well matched to C++ either - when you cancel a
thread, you probably want all the destructors for the thread's locals to
run before the thread vanishes. Having a binding that turns
cancellation into an exception would be much cleaner - and is in fact
the way other languages (Modula-3, Java) have handled this issue.)

-- Jerry

djel...@teletrader.com

unread,
Jun 3, 1999, 3:00:00 AM6/3/99
to

> There's a big difference between "..." and any other exception
> catcher, namely that in some implementations (notably Visual C++)
> the "..." catcher may catch LOW-LEVEL EXCEPTIONS such as division
> by zero or dereferencing an invalid pointer.

That troubles me too. Is there any way to turn that feature off in the
debug builds so that an access violation can be pinpointed in the
debugger?

Using _set_se_translator doesn't help because the exact location of the
access violation is lost.


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Jerry Leichter

unread,
Jun 4, 1999, 3:00:00 AM6/4/99
to
| > >> void report_error()
| > >> {
| > >> try {
| > >> throw; // will be re-caught immediately
| > >
| > > ... that's a common and very useful technique. The code above is
| > > re-throwing whichever exception is currently being handled.

|
| Hm. Throw without operands will call terminate() if there is
| currently no exception being handled. For that reason alone it's
| a bad idea: it precludes using report_error without throwing an
| exception. Second, using an exception to pass parameter information
| is akin to using a global variable: there's no control, and the
| report_error signature doesn't communicate what is handled
| gracefully and what is not.

I've rarely seen anyone so thoroughly miss the point.

A function like report_error() is used in a situation where (a) there
are a variety of different exception formats around - e.g., because
you're using multiple 3rd-party libraries that have defined their own
exception hierarchies; (b) you want to be able to report meaningful
errors no what what exception was thrown; (c) there are multiple places
where you might catch an exception and need to report what it contains.

The way you accomplish (b) in the presense of (a) is to have a number of
catch clauses, each specific to one of the exception formats that you
program must deal with. A group of such catch clauses can be long; I
have one with 8 catch clauses. About half are for exceptions I define
which have different semantics - e.g., different severity levels that,
for example, may or may not log an error, may cause only the current
thread to exit, or may cause the entire application to exit. There are
a couple for Rogue Wave-defined exceptions, one for exceptions defined
by the C++ standard - depending on what you wanted to do, there could
easily be several of these - a clause to catch thrown int's (to deal
with a common programming error, in which an integer error code is
thrown directly, rather than used to construct one of my exceptions),
and a fine catch(...) to deal with other random stuff. Given (c), I
have two choices: Copy this large block of code everywhere that I might
need to analyze and report an exception; or use the technique described
as follows:

try
{ ... }
catch (...)
{ report_error(); }

report_error() can only be meaningfully called when there is an
exception for it to handle. Calling it at any other time is a
programming error.

report_error() *cannot* be passed the exception for it to analyze as an
argument, since what would the type of that argument be? (And don't
suggest overloading report_error(), since then deciding which one to
call requires pretty much the same analysis code that we were trying to
package up in one place to begin with.)
-- Jerry

Kevin J. Hopps

unread,
Jun 4, 1999, 3:00:00 AM6/4/99
to

Alf P. Steinbach <alf_st...@my-deja.com> wrote in message
news:7ipfie$hkb$1...@nnrp1.deja.com...
> In article <270519991233155844%lisa_li...@advisories.com>,

> Lisa Lippincott <lisa_li...@advisories.com> wrote:
> > Paul D. DeRocco <pder...@ix.netcom.com> wrote:

[snip]

Alf Says:
> There's a big difference between "..." and any other exception
> catcher, namely that in some implementations (notably Visual C++)
> the "..." catcher may catch LOW-LEVEL EXCEPTIONS such as division
> by zero or dereferencing an invalid pointer.
>

> Low level exceptions are different from standard C++ exceptions
> not only in their basic nature (e.g., asynchronous, machine-
> dependent), but also in their severity: it's not a good idea to treat
> them as just ordinary exceptions. Thus, the lack of information about
> the kind of exception in a "..." catcher is very important. Without
> such information a "..." catcher should, IMO, not be used.

It is dangerous to make generalizations about the severity of certain types
of exceptions. It is possible that the code was written to not check before
dividing, relying on an exception to catch the mishap.

There are also times when it is important to perform some clean up and then
pass the exception along, even if you don't know anything about the
exception.

[snip]

Kevin Hopps
Adobe Systems, Incorporated.

Alf P. Steinbach

unread,
Jun 7, 1999, 3:00:00 AM6/7/99
to
In article <3756B4...@smarts.com>,

Jerry Leichter <jerrold....@smarts.com> wrote:
> | Standard C++ exceptions are synchronous because they're explicitly
> | thrown, that is, will always occur *between* statements....
>
> Neither part of this statement is true: The first because throws can
> occur from within certain built-in operators - in particular, new;

C++ exceptions *are* always explicitly thrown, by the program, either
by a throw statement or by code inserted by the compiler. This is
at the machine code level. At the source code level, of course, not
every C++ exception necessarily originates in a "throw" statement.

In contrast, low-level exceptions can occur anywhere in
the machine code instruction sequence, even in the middle of a
machine code instruction. Visual C++, which was my example, has
explicit support for these exceptions. I gather that other
compilers have similar support.

The compiler has full control over where a C++ exception can be
thrown. It has no control whatsoever over where a low level
exception may occur. This is the most important difference
between synchronous (C++) and asynchronous (low-level).


> the
> second because "throw x" is an *expression*. The second because there
> are expressions not containing full statements that can throw - e.g.,
> dynamic_cast. For that matter, "throw <expr>" is itself an expression
> (with void type), and can occur within a ?: operator.
>

You're right; I was thinking as a compiler and projecting my
compiler-thoughts, + using an incorrect word. The important point,
though, is that the compiler has full control: a C++ exception is
not thrown anywhere unless there is explicit compiler-generated machine
code that throws it, and where the only purpose of that machine
code is to throw. This holds also for operator new.

> I really don't see the difference between an exception raised in the
> built-in new operator, and an exception raised in the built-in
> floating divide operator

On most modern computers floating point instructions execute in
parallel with the main code stream. Thus, a floating point exception
(which is just one example of a low-level exception) may occur
anywhere in the main code stream, even in the middle of a
machine code instruction.


> (or for that matter a *user-defined* divide operator) -

That's a very different case.

> except that of course the standard happens not to have defined any
> exceptions that the built-in floating divide operator is allowed to
> throw, so any such exception would be a non-conforming extension. But
> it would hardly be contrary to the spirit of C++.

My original comments were regarding certain C++ implementations, with
Visual C++ as an example. The C++ standard is another matter.


>
> Now, if you're taking true asynchronous events, unconnected with the
> flow of the program - e.g., external interrupts - and turning them
> into exceptions, the story is different.

Floating point instructions are such asynchronous events, on most
computers.


> For such things, you usually want a
> way to continue execution from the point of interruption

I'm not sure I agree that that's what I want.


Again, hth.,

- Alf


--
alf_DOT_steinbach_AT_ac_DOT_com (clean the address before replying)

Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Alf P. Steinbach

unread,
Jun 7, 1999, 3:00:00 AM6/7/99
to
In article <3756C8...@smarts.com>,

Jerry Leichter <jerrold....@smarts.com> wrote:
> | > >> void report_error()
> | > >> {
> | > >> try {
> | > >> throw; // will be re-caught immediately
> | > >
> | > > ... that's a common and very useful technique. The code above is
> | > > re-throwing whichever exception is currently being handled.

(For appearances sake I should perhaps try to find some earlier
article, years ago, where I've helped somebody with "throw;" -- from
the comment it might seem as if fundamental C++ is unknown to me.)


> |
> | Hm. Throw without operands will call terminate() if there is
> | currently no exception being handled. For that reason alone it's
> | a bad idea: it precludes using report_error without throwing an
> | exception. Second, using an exception to pass parameter information
> | is akin to using a global variable: there's no control, and the
> | report_error signature doesn't communicate what is handled
> | gracefully and what is not.
>
> I've rarely seen anyone so thoroughly miss the point.
>
> A function like report_error() is used in a situation where (a) there
> are a variety of different exception formats around - e.g., because
> you're using multiple 3rd-party libraries that have defined their own
> exception hierarchies; (b) you want to be able to report meaningful
> errors no what what exception was thrown; (c) there are multiple
> places where you might catch an exception and need to report what it
> contains.

(a) and (c) were obvious and what I assumed. Concerning (b), we
will probably have to agree to disagree on "meaningful report" in the
presence of (1) "catch(...)" and (2) context-independent global
error reporting. To me it seems like a contradiction in terms.

> The way you accomplish (b) in the presense of (a) is to have a number
> of catch clauses, each specific to one of the exception formats that
> you program must deal with. A group of such catch clauses can be

> long; I have one with 8 catch clauses. [elided details]

I'm not unfamiliar with this catch-clause proliferation: it is often
a consequence of trying to handle exceptions globally. And that,
IMOSHO, is not sound design. Still, the design may not be of one's
own choosing, and then there is little alternative but to catch
every odd exception that may come tumbling through.

As long as the work involved in writing wrapper functions is
prohibitive, I'd prefer to SIN! Namely, to employ a *macro* (or
macros) which would just catch all known types and package them
into a common format. Reason: this avoids using "catch( ... )",
and thus the problems with low-level exceptions (which could
otherwise leave the application in an unstable state).

In other words, I'd *separate the concerns* of (1) catching a huge
number of different exception types and (2) doing something sensible
when an exception occurs. Point (1) can be and, I agree for the
situation where one has no control over possible types, should
be centralized, but point (2) should only have standard support
(e.g., possibility of standard error logging). This approach allows
local, context-sensitive handling without impractical bloated code.


> Given (c), I have two choices: Copy this large block of code
> everywhere that I might need to analyze and report an exception; or
> use the technique described as follows:
>
> try
> { ... }
> catch (...)
> { report_error(); }

Well, you do have a third choice, as described above!


> report_error() *cannot* be passed the exception for it to analyze as

> an argument, since what would the type of that argument b? (And


> don't suggest overloading report_error(), since then deciding which
> one to call requires pretty much the same analysis code that we were
> trying to package up in one place to begin with.)

I don't think we disagree here. If I recall correctly (and you may
check that out) I think I wrote that a better design would be to
pass an object of a type T, where type T supports extracting
the information necessary for error reporting (or logging, whatever
the case may be). If, for some reason, the code absolutely must
incorporate "catch( ... )" somewhere, and in a context where the
exception should be reported/logged, then the answer to what info
T should provide is simple: the same as the information your
current "report_error" function needs and assumes in this case.

Hth.,

Alf P. Steinbach

unread,
Jun 7, 1999, 3:00:00 AM6/7/99
to
In article <7j2t30$fbb$1...@nnrp1.deja.com>,

Ivan J Johnson <iva...@my-deja.com> wrote:
> In article <7j0jv3$46h$1...@nnrp1.deja.com>,
> "Alf P. Steinbach" <alf_st...@my-deja.com> wrote:
> > In article <9FC43.10983$AL5....@ndnws01.ne.mediaone.net>,
> > "Dave Abrahams" <abra...@mediaone.net> wrote:
> > > In article <7itmir$7ad$1...@nnrp1.deja.com> , "Alf P. Steinbach"
> > > <alf_st...@my-deja.com> wrote:
> > >
> > > >> void report_error()
> > > >> {
> > > >> try {
> > > >> throw; // will be re-caught immediately
> > > >
[elided]

> > Hm. Throw without operands will call terminate() if there is
> > currently no exception being handled. For that reason alone it's
> > a bad idea: it precludes using report_error without throwing an
> > exception. Second, using an exception to pass parameter information
> > is akin to using a global variable: there's no control, and the
> > report_error signature doesn't communicate what is handled
> > gracefully and what is not.
>
> I'm not quite understanding this. Are you concerned that
> report_error() might be called "accidentally," leading to abrupt
> program termination?

Nope. My main concern is, to quote the quote of myself, that it
precludes usage of report_error without throwing an exception.
I can imagine many situations where you know you've failed and
you need to report this, without passing an exception upwards.
E.g. due to the interface you're working in using a non-exception
based error reporting scheme, or because report_error handles
logging of warnings (locally recoverable situations) as well as
logging of errors, or because you're using the logging as a trace
for automated testing/diagnostics, or because...

My secondary concern is, to again quote the quote of myself, that


the report_error signature doesn't communicate what is handled

gracefully or not. Thus it's difficult to use correctly, e.g.
in a module that defines a wrapper for the function in order
to report some errors another way (or whatever).


> I don't see that [accidental call] as a problem, because I think


> it's an unlikely mistake for a programmer to make and because
> termination is difficult for a tester to ignore. However, if it
> concerns you, you could do two things: (1) Change the name to
> report_exception(). This name, plus suitable documentation, plus the
> fact that the function does not take any exception as an argument,
> should together make it unmistakably clear that the function requires
> an exception to be already active. (2) Before the try-block in
> report_exception, query std::uncaught_exception() to determine if an
> exception is active. If it is not, throw std::logic_error or some
> exception that you know that your framework will catch.

I agree with both suggestions, although I'm not familiar with
uncaught_exception. Ok, I'll upgrade to new compiler!

> To me, exception-handling functions like this are the surest way of
> making sure *all* errors get handled gracefully. Every error is
> reported to the user

Here's where I totally disagree. The handling of an exception is
IMO best done locally, in context. And there are only two sound
outcomes: either (1) fulfill the function's contract, perhaps using
plan B or plan C, or (2) else pass the buck by throwing/rethrowing.
By the principle of side-effect free functions, in the case of (1)
no error should be reported to the user unless the function is at the
user interface level and error reporting is part of its contract. In
the case of (2) I cannot think of any situation warranting a user-
interface level error report, but maybe they exist (e.g., in Windows
there is the darned drive not ready "Abort/Cancel/Ignore" automatic
dialog, popped up by low-level file API, which IMO is best turned off!).

> and no error is allowed to abruptly terminate the program and
> possibly cause the user to lose data.

Here I totally disagree. If only Microsoft Word had the good
sense to crash instead of corrupting my files! However, might
hurt sales more to crash than to innocently refuse to read the
program's own files (after all, blame might lie elsewhere...).

Admittedly have not had this particular problem since version 8,
but illustrates the case: after an error you don't know how to
handle (at any level) the program is most likely unstable, and may
do much more harm by continuing than by terminating. (Unless, of
course, it's the single control program for a badly designed nuclear
reactor which will immediately blow up in the absence of active
control.)

> If a program is found to be throwing errors of a previously unknown
> type, that new type can be accommodated in a few handler functions,

> rather than in catch blocks strewn throughout the program.

Agreed with regard to *catching* them. Not agreed with regard
to *handling* them, if you imply global function should do that?

> >
> > Since the function is handling different types of exception,
> > including nonstandard and unknown types (where no information is
> > available), a better design would be to define a class T
> > encapsulating retrieval of information from exceptions, and pass
> > "T const&" to the function. The case of no information available
> > can then be handled gracefully by the T object.
>
> But how would you construct such an object? Either you need multiple
> catch-blocks where the exception is first caught, or you need to hand
> the exception off to a handler function

Yes to both. And I'd do that via an ugly-as-sin macro, with one
catch per known type, to avoid dangerous "catch( ... )". But note
that this does not include *handling*, only catching and xform
to common format T (which need not be designed as exception).


> which re-throws and re- catches the exception to determine its actual


> type and extract information from it.

No need for that.


> Such a function is not limited just to reporting errors; it could
> also modify the exception with additional context information and re-
> throw it, or it could construct a T object and throw that.

Yes.


>
> >
> > Using an explicit parameter also allows passing information about
> > where the exception was caught (or failure detected).
>
> That's a good point. It can be accommodated by changing the signature
> of report_exception() to permit this:
>
> catch {
> report_exception(__FILE__, __LINE__);
> }

Well, that's additional reason for using a catchy macro (not that I
like macros, but they're terribly convenient sometimes!)

Alf P. Steinbach

unread,
Jun 7, 1999, 3:00:00 AM6/7/99
to
[To mod: this is a perhaps a bit platform-specific, but answers
specific questions & exemplifies the main topic, namely low-level
exceptions in the context of a "catch(...)".]


In article <7j6did$koc$1...@nnrp1.deja.com>,


djel...@teletrader.com wrote:
>
> > There's a big difference between "..." and any other exception
> > catcher, namely that in some implementations (notably Visual C++)
> > the "..." catcher may catch LOW-LEVEL EXCEPTIONS such as division
> > by zero or dereferencing an invalid pointer.
>

> That troubles me too. Is there any way to turn that feature off in the
> debug builds so that an access violation can be pinpointed in the
> debugger?

Not exactly. But you can turn on first-chance exception catching in
the debugger. This lets you have a go at looking at the exceptions
before they're passed on to the program's exception handlers.

To turn on fcec in DevStudio, use "Step into" to start debugging,
then menu [Debug | Exceptions].


> Using _set_se_translator doesn't help because the exact location
> of the access violation is lost.

Well no, address isn't lost -- but it depends, of course, on your
low-level exception to C++ exception translator function. One of
the paramers is an EXCEPTION_POINTERS structure, wherein you'll
find the address of the exception's origin. Simply send that
address (as a string) to the debugger, via OutputDebugString, then
a DebugBreak, then in the debugger go to that address in a machine
code window, then ask for a source view, and you're there. However,
*dynamic context* is lost, since the translator function is called
from somewhere in the middle of the stack unwinding.


Btw., you may find this function useful since DebugBreak may
break your application when it's *not* being debugged:

--------------------------------------
// Windows NT only -- hasn't been tested exclusively.
// Simply because I couldn't find any binding to this function.

bool IAmSurelyBeingDebugged()
{
typedef BOOL WINAPI IDBFunc();
static HMODULE const kernel = GetModuleHandle("kernel32" );
static void* const pIsDebuggerPresent =
GetProcAddress( kernel, (char*)395 );

return
pIsDebuggerPresent && // Have Ok pointer &&
((IDBFunc*)pIsDebuggerPresent)(); // kernel says debugging.
} // IAmSurelyBeingDebugged
--------------------------------------

Alf P. Steinbach

unread,
Jun 7, 1999, 3:00:00 AM6/7/99
to
In article <aC153.11264$AL5....@ndnws01.ne.mediaone.net>,

"Dave Abrahams" <abra...@mediaone.net> wrote:
> If you're going to handle exceptions, using report_error and
> maintaining it once beats having to rewrite it for every place
> where you need to catch and report errors.

You'll probably consider this a really dumb question, but as a
wise person once remarked, there are no dumb questions, only dumb
answers. So: exactly why?


>
> > Since the function is handling different types of exception,
> > including nonstandard and unknown types (where no information is
> > available), a better design would be to define a class T
> > encapsulating retrieval of information from exceptions, and pass
> > "T const&" to the function. The case of no information available
> > can then be handled gracefully by the T object.
>

> And how do you propose to get the information into your class T? I
> obviously don't understand your idea. Care to post some concrete
> code?

Concrete code, no, this is easy enough. To get info into T, use
a series of *specific* catches, every place. This code-bloat can
be hidden away in a simple macro.

Note: Handling of exception (e.g., retry, cleaning up,
logging, throwing/rethrowing) is a separate issue and is highly
context dependent. Should not be mixed up with catching scheme.

Solves problem with "catch( ... )" (refer back in thread for problem
description). That is the main benefit, and it is IMO *extremely
important*. Also solves problem with mixup of exception and
reporting / logging -- e.g. logging functionality clean, can be
used in other contexts.

Can think of one problem (from other's point of view) with macro
approach, namely requirement of total recompilation versus just
total relinking when a new, unrelated exception type is introduced
in such a way that it requires extension of the standard catch-
list. However, in that case *all* current code becomes suspect
and must be re-tested at all levels, including system test.

Alf P. Steinbach

unread,
Jun 7, 1999, 3:00:00 AM6/7/99
to
In article <7j8u2u$i...@enquirer.corp.adobe.com>,

"Kevin J. Hopps" <kho...@Adobe.COM> wrote:
> It is dangerous to make generalizations about the severity of certain
> types of exceptions. It is possible that the code was written to not
> check before dividing, relying on an exception to catch the mishap.

Agree in general about severity -- in my view an exception can not
have an intrinsic severity, because it depends entirely on context
(which is not necessarily known to the exception thrower). However,
low-level exceptions are in general really nasty ones; they're so
nasty that C++ as a language has no facility for dealing with them.

Specific implementations of C++, however, may provide mechanisms,
and "catch(...)" is one such possible mechanism, and a very natural
one (e.g., it's used this way in Visual C++).

So, agreed in general, but I think access violations, arithmetic
exceptions, kill signals etc. are in special nasty class, very
likely to leave the app in an *unstable state*, and so
simply lumping them together with whatever else in a common
"catch( ... )" is, IMO, not a good design.


> There are also times when it is important to perform some clean up
> and then pass the exception along, even if you don't know anything
> about the exception.

Agreed, under the assumption that higher levels *do* know about
the exception. E.g., a "filter" function/object might be installed
into a call chain it can not know anything about. Yes?

However, passing along an exception is very different from reporting
"an unknown error occured" and then just carrying on.


Cheers,

Stanley Friesen [Contractor]

unread,
Jun 7, 1999, 3:00:00 AM6/7/99
to
In article <7jga6l$l92$1...@nnrp1.deja.com>,

Alf P. Steinbach <alf_st...@my-deja.com> wrote:
>> To me, exception-handling functions like this are the surest way of
>> making sure *all* errors get handled gracefully. Every error is
>> reported to the user
>
>Here's where I totally disagree. The handling of an exception is
>IMO best done locally, in context.

Umm, I am not sure I understand you here.

If you mean that a *problem* or *error* should be handled locally, if
possible, I agree.

But in that case it is far better to use non-exception methods for
handling the error. For local error handling, if's and switch's are
easier to write, and *far* more efficient than throwing exceptions.

Exceptions are really only justified when error handling must be in a
different function than error detection. Only when crossing function
boundaries is the expense of throwing an exception a reasonable choice.

>do much more harm by continuing than by terminating. (Unless, of
>course, it's the single control program for a badly designed nuclear
>reactor which will immediately blow up in the absence of active
>control.)
>

Well - strictly speaking, even a badly designed reactor cannot explode
(unless it is a breeder reactor). The word case scenario for a power-
generating reactor is a meltdown (as happened at Chernobyl), not
an explosion.

[A breeder reactor is one designed to create nuclear fuel from less
useful fissionables, and is not useful for generating power].


>
>Yes to both. And I'd do that via an ugly-as-sin macro, with one
>catch per known type, to avoid dangerous "catch( ... )".

The problem with this is that it doesn't deal with an unknown type of
exeption. There *are* situations where terminating is not acceptible.
This is what "catch(...)" is for.

Well, one use of it.

Another is generic cleanup prior to a re-throw. In *most* cases this
use can be avoided by using the resource-acquisition-is-construction
paradigm. But where this is not viable, say due to a requirement to
be compatible with an older interface, a catch-all catch clause is
necessary (and safe, as no pretention of actually *handling* the exception
is involved).

Ivan J Johnson

unread,
Jun 8, 1999, 3:00:00 AM6/8/99
to
In article <7jg30b$j96$1...@nnrp1.deja.com>,

"Alf P. Steinbach" <alf_st...@my-deja.com> wrote:
> In article <3756B4...@smarts.com>,
> Jerry Leichter <jerrold....@smarts.com> wrote:
>[...]

> In contrast, low-level exceptions can occur anywhere in
> the machine code instruction sequence, even in the middle of a
> machine code instruction. Visual C++, which was my example, has
> explicit support for these exceptions. I gather that other
> compilers have similar support.
>
> The compiler has full control over where a C++ exception can be
> thrown. It has no control whatsoever over where a low level
> exception may occur. This is the most important difference
> between synchronous (C++) and asynchronous (low-level).
>[...]

> > I really don't see the difference between an exception raised in the
> > built-in new operator, and an exception raised in the built-in
> > floating divide operator
>
> On most modern computers floating point instructions execute in
> parallel with the main code stream. Thus, a floating point exception
> (which is just one example of a low-level exception) may occur
> anywhere in the main code stream, even in the middle of a
> machine code instruction.
>[...]

> > Now, if you're taking true asynchronous events, unconnected with the
> > flow of the program - e.g., external interrupts - and turning them
> > into exceptions, the story is different.
>
> Floating point instructions are such asynchronous events, on most
> computers.
>[...]

Is it correct, that since these low-level exceptions are
'asynchronous', they are not necessarily trapped at the point in
program flow where the error occurs, but may instead be trapped some
time later, by which point a totally unrelated function might be
executing -- for example, a function which is not written to be
exception-safe because its author did not think it was doing anything
that could potentially throw, or maybe even a heap allocation? That
would be a nasty error indeed.

------
Regards,
Ivan

Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Ivan J Johnson

unread,
Jun 8, 1999, 3:00:00 AM6/8/99
to
In article <7jga6l$l92$1...@nnrp1.deja.com>,

I agree that it would be nice to direct all error reporting/logging
through a single function. Couldn't you do that by calling such a
function both from the report_error exception handler described above,
and from other (non-exception-related) places elsewhere in the program?

>
> My secondary concern is, to again quote the quote of myself, that
> the report_error signature doesn't communicate what is handled
> gracefully or not. Thus it's difficult to use correctly, e.g.
> in a module that defines a wrapper for the function in order
> to report some errors another way (or whatever).

It's true that the signature doesn't tell you much. But presumably
this will be documented somewhere, and it's also easy to determine just
from looking at the code. Also, I don't see that your proposed macro
alternative is any different in this regard.

> > I don't see that [accidental call] as a problem, because I think
> > it's an unlikely mistake for a programmer to make and because
> > termination is difficult for a tester to ignore. However, if it
> > concerns you, you could do two things: (1) Change the name to
> > report_exception(). This name, plus suitable documentation, plus
the
> > fact that the function does not take any exception as an argument,
> > should together make it unmistakably clear that the function
requires
> > an exception to be already active. (2) Before the try-block in
> > report_exception, query std::uncaught_exception() to determine if an
> > exception is active. If it is not, throw std::logic_error or some
> > exception that you know that your framework will catch.
>
> I agree with both suggestions, although I'm not familiar with
> uncaught_exception. Ok, I'll upgrade to new compiler!
>

Actually, I'd never used it either, until I wrote the above post. Does
anybody else think this is a good or bad idea? Does anybody actually
do this?

> > To me, exception-handling functions like this are the surest way of
> > making sure *all* errors get handled gracefully. Every error is
> > reported to the user
>
> Here's where I totally disagree. The handling of an exception is
> IMO best done locally, in context. And there are only two sound
> outcomes: either (1) fulfill the function's contract, perhaps using
> plan B or plan C, or (2) else pass the buck by throwing/rethrowing.
> By the principle of side-effect free functions, in the case of (1)
> no error should be reported to the user unless the function is at the
> user interface level and error reporting is part of its contract. In
> the case of (2) I cannot think of any situation warranting a user-
> interface level error report, but maybe they exist (e.g., in Windows
> there is the darned drive not ready "Abort/Cancel/Ignore" automatic
> dialog, popped up by low-level file API, which IMO is best turned
off!).
>

I agree that, in general, a low-level function should either succeed or
throw/return an error, but it should not report the error to the user,
because there is not normally enough information available at the point
of failure to know what, if anything, to report. What is fatal in one
context might be harmless in another. But what do you do about errors
(which may or may not be exceptions) in destructors? The typical case
would be a failure to release some resource. As you hint at above, you
might try to close an output file. Closing the file flushes its
buffers to the disk, but the disk has gone bad, been ejected, or
whatever. In that case, I think it's reasonable to notify the user
directly from a low-level function. That error is too severe to
ignore, but not severe enough to require immediate program
termination. And, actually, for me destructors seem to be the most
common place to use a report_exception function like the one we're
discussing.

> > and no error is allowed to abruptly terminate the program and
> > possibly cause the user to lose data.
>
> Here I totally disagree. If only Microsoft Word had the good
> sense to crash instead of corrupting my files! However, might
> hurt sales more to crash than to innocently refuse to read the
> program's own files (after all, blame might lie elsewhere...).
>

Agreed, but that cuts both ways. If only Microsoft Visual C++ had the
good sense to give me a chance to save my files before it crashed!

> Admittedly have not had this particular problem since version 8,
> but illustrates the case: after an error you don't know how to
> handle (at any level) the program is most likely unstable, and may
> do much more harm by continuing than by terminating. (Unless, of
> course, it's the single control program for a badly designed nuclear
> reactor which will immediately blow up in the absence of active
> control.)
>

There is a third option: notify the user (assuming one is present) and
let him make up his own mind about whether to continue, abort
immediately, or try to save his "document."

> > If a program is found to be throwing errors of a previously unknown
> > type, that new type can be accommodated in a few handler functions,
> > rather than in catch blocks strewn throughout the program.
>
> Agreed with regard to *catching* them. Not agreed with regard
> to *handling* them, if you imply global function should do that?
>

Probably we should be more specific about what it means to "handle" an
exception. I'm not suggesting that the same global function be used to
process every exception, under every circumstance. I am suggesting
that there is a great deal of commonality in the way exceptions are
handled at different points in a program, and that this commonality is
best exploited by centralizing the handling in a few functions -- as
few as possible, but no fewer.

I would use a report_exception() function like the one described above
after a try-block wrapping the main function/event loop of a program,
and the same report_exception() after a try-block in my destructors,
for those cases where the destructor might otherwise throw. (Actually,
in real life the framework does the former for me, but I do the latter
so that the user gets the same error message that the framework would
have given her, if the framework had caught the exception.) And I
would try to re-use that same report_exception() in multiple programs,
which isn't difficult since the mapping between system error codes,
exception types, and user error messages doesn't change much from
program to program.

OK, I think the macro solution is workable, and not really that
different from the catch(...) handler function. In both cases, you
have a concise way of catching all the exceptions you want to catch.
Basically, I see the differences as follows:

1. Macros are evil. (just kidding :-)
2. The macro will cause some code bloat.
3. Code will need to be recompiled every time the macro changes, while
with function you only need to relink.
4. The macro could potentially clash with other global names.

Probably, none if the above will be significant. The interesting
difference is in

5. the stack unwinding. With the macro, you simply don't catch what
you don't want. If you don't want to catch those pesky hardware
exceptions, you don't have to. You can just let them terminate your
program. You can then examine the register contents from the moment of
termination, and they will be unaffected by any stack unwinding.
However, I have never found that sort of information useful for
debugging. Have you?

On the other hand, since the stack is never unwound, destructors never
get called, files may not get flushed, etc. (actually, it's
implementation-defined whether destructors get called in the case of an
uncaught exception, but I doubt that this happens for hardware
exceptions). With the handler function, if you don't want to handle a
particular exception, you can re-throw it, and if you like you can log
some context information, such as the file and line number where the
exception was first caught. And, to the extent that the stack is
unwound, destructors will definitely get called. On the whole, I still
prefer the function.

--
Regards,
Ivan

Dave Abrahams

unread,
Jun 8, 1999, 3:00:00 AM6/8/99
to
In article <7jgmlu$pi6$1...@nnrp1.deja.com> , "Alf P. Steinbach"
<alf_st...@my-deja.com> wrote:

>> And how do you propose to get the information into your class T? I
>> obviously don't understand your idea. Care to post some concrete
>> code?
>
> Concrete code, no, this is easy enough. To get info into T, use
> a series of *specific* catches, every place. This code-bloat can
> be hidden away in a simple macro.

So can numerous other sins. I don't see how this macro could be "simple", at
all. Maybe I don't need to see any code, though: your suggestion is already
starting to make me queasy ;)

> Can think of one problem (from other's point of view) with macro
> approach, namely requirement of total recompilation versus just
> total relinking when a new, unrelated exception type is introduced
> in such a way that it requires extension of the standard catch-
> list.

Yes, that among other things is a real problem with your proposal.

> However, in that case *all* current code becomes suspect
> and must be re-tested at all levels, including system test.

Hardly. New exception types don't do anything to affect the control paths
that are available to the program; all they do is carry new information
about what went wrong.

Alf P. Steinbach

unread,
Jun 8, 1999, 3:00:00 AM6/8/99
to
In article <7jh7r3$a...@abyss.West.Sun.COM>,
sta...@West.Sun.COM (Stanley Friesen [Contractor]) wrote:
> In article <7jga6l$l92$1...@nnrp1.deja.com>,

> Alf P. Steinbach <alf_st...@my-deja.com> wrote:
> >> To me, exception-handling functions like this are the surest way of
> >> making sure *all* errors get handled gracefully. Every error is
> >> reported to the user
> >
> >Here's where I totally disagree. The handling of an exception is
> >IMO best done locally, in context.
>
> Umm, I am not sure I understand you here.

Clarification follows.


>
> If you mean that a *problem* or *error* should be handled locally, if
> possible, I agree.
>
> But in that case it is far better to use non-exception methods for
> handling the error. For local error handling, if's and switch's are
> easier to write, and *far* more efficient than throwing exceptions.
>
> Exceptions are really only justified when error handling must be in a
> different function than error detection. Only when crossing function
> boundaries is the expense of throwing an exception a reasonable
> choice.

I totally agree with everything said above. When I wrote "handled
locally" this did not imply "thrown and handled locally", although
that technique can in some cases be useful, too. To complete
the quote of myself (I seem to be entering habit of quoting quotes of
myself), where I should perhaps used a bold font (oops! Usenet!) for
the *third sentence* (btw., Eiffel got this single aspect right):


"
Here's where I totally disagree. The handling of an exception is IMO
best done locally, in context. And there are only two sound outcomes:
either (1) fulfill the function's contract, perhaps using plan B or
plan C, or (2) else pass the buck by throwing/rethrowing. By the
principle of side-effect free functions, in the case of (1) no error
should be reported to the user unless the function is at the user
interface level and error reporting is part of its contract. In the
case of (2) I cannot think of any situation warranting a user-
interface level error report, but maybe they exist (e.g., in Windows
there is the darned drive not ready "Abort/Cancel/Ignore" automatic
dialog, popped up by low-level file API, which IMO is best turned off!).
"

I hope that clarified what I meant to say. And yes, agree also to
the rest -- there are always exceptions, even in exception handling!
Intelligence is required.

Cheers,

Michi Henning

unread,
Jun 9, 1999, 3:00:00 AM6/9/99
to
On 7 Jun 1999, Alf P. Steinbach wrote:

> Specific implementations of C++, however, may provide mechanisms,
> and "catch(...)" is one such possible mechanism, and a very natural
> one (e.g., it's used this way in Visual C++).

Hmmm... Isn't the real motivation for the feature that Windows is incapable
of cleanly terminating a process because it cannot reliably track all
the processes' resources? In other words, how desirable would the feature
be if the OS could guarantee clean process termination (without leaving
locked memory regions behind and such)? To question this further, if
your program detects that such a low-level exception has indeed occurred,
how would you react to it, other than by terminating?

The way I see it, catching things like floating point exceptions via
catch (...) achieves nothing but passing the buck for recovering
OS resources from the OS to the application code. Bad tradeoff, as far as
I am concerned...

Cheers,

Michi.
Copyright 1999 Michi Henning. All rights reserved.
--
Michi Henning +61 7 3236 1633
Triodia Technologies +61 4 1118 2700 (mobile)
PO Box 372 +61 7 3211 0047 (fax)
Annerley 4103 mi...@triodia.com
AUSTRALIA http://www.triodia.com/staff/michi-henning.html

Jerry Leichter

unread,
Jun 9, 1999, 3:00:00 AM6/9/99
to
| > | Standard C++ exceptions are synchronous because they're explicitly
| > | thrown, that is, will always occur *between* statements....
| >
| > Neither part of this statement is true: The first because throws
| > can occur from within certain built-in operators - in particular,
| > new;
|
| C++ exceptions *are* always explicitly thrown, by the program, either
| by a throw statement or by code inserted by the compiler. This is
| at the machine code level. At the source code level, of course, not
| every C++ exception necessarily originates in a "throw" statement.

This is a bizarre use of "explicitly thrown". You're defining it from
the point of view of the compiler writer. To a user of C++, "explicitly
thrown" can only reasonably refer to the code he has actually written
and can actually see. To the designer of the hardware, any exception
(in the hardware sense) is explicitly generated, by real hardware moving
real electrons around. To the designer of the OS, all kinds of actions
hidden inside kernel-mode code can "explicitly" cause actions that to a
purely user-mode program "come out of nowhere".



| In contrast, low-level exceptions can occur anywhere in
| the machine code instruction sequence, even in the middle of a
| machine code instruction.

I can think of no reason at all why the C++ standard, or in virtually
all cases a user of the C++ language, should in any way need to refer to

| On most modern computers floating point instructions execute in
| parallel with the main code stream. Thus, a floating point exception
| (which is just one example of a low-level exception) may occur
| anywhere in the main code stream, even in the middle of a
| machine code instruction.

Another one of those "interesting" definitions. Yes, if you count
instances of machines, and restrict your definition of "modern
computers" just right, then there are probably more x86's out there than
anything else; and, yes, the x86 happens to implement an FP model which
has an FP processor running in parallel with its integer processor. In
this, it is essentially unique - no other "modern" architecture
inherited its FP design from much older designs with separate FP chips.
On the other hand, in most modern machines - and even in some machines
dating back to the late '60's - there can be all kinds of things
happening in parallel.

The distinction you're drawing is based on knowledge of one architecture
and has no really broad meaning.

There *is* a related notion that you're getting close to - though by
continuing to concentrate on the detailed bits-and-bytes of the x86,
you're not likely to get there. That's the notion of an *imprecise*
exception. This idea has also been around since the late '60's. It
refers to an exception (again, in the hardware sense) which, while
explicitly caused by a particular program action, cannot be traced back
to its ultimate cause. For example, an imprecise FP overflow exception
might only tell you, in effect, that one of the last 3 FP instructions
you executed overflowed - but not which one. Imprecise exceptions are
nice for hardware, but a pain for software. Modern machines have tended
to provide ways to avoid them - though often at some execution-time
cost.

Returning to the issue of mapping such hardware events into C++
exceptions: The C++ exception model, in general, doesn't allow you to
determine where an exception was thrown to any finer resolution than
the nearest enclosing try block. Even on an x86, if the compiler had an
extension that mapped FP divide-by-0 into a C++ exception, it would be
straightforward to insert an instruction that waited for the FP unit to
drain at each entry to a try block. Now, I said "in general" above;
you can construct more complex cases in which the exception occurs
during a series of constructor calls, and you need to know which
destructors need to be called. Again, appropriate "drain" code makes
sense.

| Floating point instructions are such asynchronous events, on most
| computers.

Do, please, learn something about the standard usage of terms in this
business - and about something other than the x86.

-- Jerry

Michi Henning

unread,
Jun 10, 1999, 3:00:00 AM6/10/99
to
On 9 Jun 1999, Jerry Leichter wrote:

> | Floating point instructions are such asynchronous events, on most
> | computers.
>
> Do, please, learn something about the standard usage of terms in this
> business - and about something other than the x86.

I'm not sure it is that simple. I think the important point is that
such a hardware exception can happen at unpredictable times, whereas
any other C++ exception has an explicit throw statement somewhere.

The problem with such asynchronous hardware exceptions I see is that
they can occur at unpredictable times and leave the program in an
unpredictable state. How would you implement the strong exception
guarantee in such an environment?

To me, mapping hardware exceptions onto C++ exceptions achieves nothing
but headaches for application programmers.

Cheers,

Michi.


--
Michi Henning +61 7 3236 1633
Triodia Technologies +61 4 1118 2700 (mobile)
PO Box 372 +61 7 3211 0047 (fax)
Annerley 4103 mi...@triodia.com
AUSTRALIA http://www.triodia.com/staff/michi-henning.html

Alf P. Steinbach

unread,
Jun 10, 1999, 3:00:00 AM6/10/99
to
{ Please keep followups on C++ not people. The thread shows
a warming trend, more light please. -mod }

In article <375E93...@smarts.com>,
Jerry Leichter <jerrold....@smarts.com> wrote:

> > > I really don't see the difference between an exception raised in
> > > the built-in new operator, and an exception raised in the built-in

> > > floating divide operator.

> > On most modern computers floating point instructions execute in
> > parallel with the main code stream. Thus, a floating point
> > exception (which is just one example of a low-level exception)
> > may occur anywhere in the main code stream, even in the middle
> > of a machine code instruction.

> [elided stm. that this applies to x86 and that architecture only]


>
> The distinction you're drawing is based on knowledge of one
> architecture and has no really broad meaning.

If there is, IYO, no "difference between an exception raised in


the built-in new operator, and an exception raised in the built-in

floating divide operator":

Please provide standard C++ code that catches the latter kind.

> > Floating point instructions are such asynchronous events, on most
> > computers.
>
> Do, please, learn something about the standard usage of terms in this
> business - and about something other than the x86.

There may be more polite ways to express disagreement. As it is,
I cannot answer since I do not know what is being disagreed with.

Hth.,

- Alf

--
alf_DOT_steinbach_AT_ac_DOT_com (clean the address before replying)


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Ken Hagan

unread,
Jun 10, 1999, 3:00:00 AM6/10/99
to
You are correct. On Intel processors, floating point exceptions are
"released" by the next FP instruction.

This kind of thing is particularly easy to track down when the error is
caused by a window re-painting in the background, and detected in a
dialog box in the foreground. 8-)

It would be trivial for the compiler to generate extra code to flush out
these errors rather closer to the point of occurence. However, MS
stopped doing this as of version 4. Division by zero makes a program
non-conforming, so I don't suppose I can complain.

Ivan J Johnson wrote in message <7ji8a5$bag$1...@nnrp1.deja.com>...


>Is it correct, that since these low-level exceptions are
>'asynchronous', they are not necessarily trapped at the point in
>program flow where the error occurs, but may instead be trapped some
>time later, by which point a totally unrelated function might be
>executing -- for example, a function which is not written to be
>exception-safe because its author did not think it was doing anything
>that could potentially throw, or maybe even a heap allocation? That
>would be a nasty error indeed.

Alf P. Steinbach

unread,
Jun 10, 1999, 3:00:00 AM6/10/99
to
In article <7jig88$dhu$1...@nnrp1.deja.com>,

Ivan J Johnson <iva...@my-deja.com> wrote:
> I agree that it would be nice to direct all error reporting/logging
> through a single function. Couldn't you do that by calling such a
> function both from the report_error exception handler described above,
> and from other (non-exception-related) places elsewhere in the
> program?

If a common report_error function is used, then yes, I think the
facade pattern is the best design.

Due to the comments I think this discussion is now really
about three weakly related topics:


1 How to best implement "catch a general bunch of exceptions",
via dirty macro or dirty (catch-try-rethrow-catch)
function?

2 How to handle "very serious" exceptions in general, where
what's serious is, of course, context-dependent.

3 Using "catch(...)", without final rethrow, in the context of
possible low-level exceptions.


Accordingly I suggest branching into new threads in follow-ups,
with this thread (if continued) addressing only topic 3.

------------------ TOPIC 1 -- macro or function?

> It's true that the signature doesn't tell you much. But presumably
> this will be documented somewhere, and it's also easy to determine
> just from looking at the code. Also, I don't see that your proposed
> macro alternative is any different in this regard.

Is different on three accounts: (1), a macro signifies that one is
stepping outside normal rules of language, or conventional use of
the language (as the report_error function does by requiring to be
called from within a catch), and this is important for maintenance
personell who can see at a glance that a kludge is involved, which
is the case no matter how general exception catching is implemented;
(2), is restricted to a very limited job (catching), in line with
your comment above about splitting functionality; (3), since handling
is decoupled from catching, exception handling support functions can
have normal signatures, which at least improves that part.

There's also a fourth & important difference, but which is not direcly
related to clear signature. Namely (4) that macro allows picking up
file name and line number of place where exception is caught. Of
course can also be supplied as parameters to function, but I'd
prefer to have this thing encapsulated within the catcher mechanism.

It's possible to use catch( ... ) + normal function call + try + throw
+ catch( whatever ) as the mechanism of generic catcher, which can be
made to do only the job of catching and extracting information from
exception. The "catch(...)" is probably not dangerous as long as the
rethrown exception doesn't end up in one. In *this design*, as opposed
to the original proposed report_error(), only points 1 & 4 apply. If
forced by a library or a framework to use the idea of general exception
catching, I'd choose the macro approach to clearly separate good code
(skimming ok) from kludge (needs scrutiny), and to encapsulate stuff
like picking up source code location.


> 3. Code will need to be recompiled every time the macro changes, while
> with function you only need to relink.

Yep, mentioned that some might see that as a problem. To me it is a
*feature*. I would *want* to recompile absolutely everything after
a change with so much global impact as changing the list of
generally caught exceptions (because this implies testing everything).
And the macro approach ensures that a normal build (make), after a
change, will rebuild everything that depends on the exception list.


> 4. The macro could potentially clash with other global names.

Good point.


> On the whole, I still prefer the function.

------------------ TOPIC 2 -- very serious exceptions/errors.

> I agree that, in general, a low-level function should either succeed
> or throw/return an error, but it should not report the error to the
> user, because there is not normally enough information available at
> the point of failure to know what, if anything, to report. What is
> fatal in one context might be harmless in another. But what do you
> do about errors (which may or may not be exceptions) in destructors?

In general, I ignore failed destruction. Not much else to do. But I
must admit that the "ignore errors" approach is not ideal, in fact, it
is so bad that it bothers my conscience almost every day. And I do
know about some alternatives. One is, for example, to *post* an
error to a per-thread singleton somewhere, where inspecting that
singleton & taking appropriate action (notifying user, whatever)
would be the job of the higher level code. However, I'm too lazy
to do this, and I think I would have a [bad word] of a time
convincing my superiors to allocate time for creating that framework
*and* modifying all code to use it.

Another (complementary) method is to pass an "on specific error in
destructor" functor object down from the high level code to the low-
level object that might have trouble in its destructor. This method
addresses a problem with the "post" method, namely that in some
cases, particularly resource dellocation, the object that might fail
destruction is the only one that can properly clean up things.

A third option is to log the error, notify the user, and terminate,
but I guess the user that just removed a diskette or CD will be
slightly annoyed?

> The typical case would be a failure to release some resource. As you
> hint at above, you might try to close an output file. Closing the
> file flushes its buffers to the disk, but the disk has gone bad, been
> ejected, or whatever. In that case, I think it's reasonable to
> notify the user directly from a low-level function. That error is
> too severe to ignore, but not severe enough to require immediate
> program termination. And, actually, for me destructors seem to be
> the most common place to use a report_exception function like the one
> we're discussing.

Not totally opposed to the idea, but should at least be possible to
turn off that user-oriented behavior, since it assumes (1) there is
a user, (2) the call of the low-level function is made in a context
where its obvious to a user what's going on, in particular, where
this program is the foreground, active one, and (3) the user will
*probably* have the means of correcting the cause of the problem.

For example, have you tried to use Microsoft Word as a printing
engine from a Windows NT service (background process)? Even from
a normal program it's difficult, or at least it was difficult some
2-3 years ago, because of dialog boxes popping up, reliance on idea
of having an active user (e.g. user's settings accessed even when
no need of that, in particular, default printer!), etc. (causes
unrelated to this discussion).

In short, my point is that given that low-level function does not
know the context of a call, it may be Ok to provide a default
fatal-error-correction scheme involving the user, but only when
higher level code can override / turn off this behavior.


> > If only Microsoft Word had the good
> > sense to crash instead of corrupting my files! However, might
> > hurt sales more to crash than to innocently refuse to read the
> > program's own files (after all, blame might lie elsewhere...).
> >
> Agreed, but that cuts both ways. If only Microsoft Visual C++ had the
> good sense to give me a chance to save my files before it crashed!

Yes...


> > Admittedly have not had this particular problem since version 8,
> > but illustrates the case: after an error you don't know how to
> > handle (at any level) the program is most likely unstable, and may
> > do much more harm by continuing than by terminating. (Unless, of
> > course, it's the single control program for a badly designed nuclear
> > reactor which will immediately blow up in the absence of active
> > control.)
> >
> There is a third option: notify the user (assuming one is present) and
> let him make up his own mind about whether to continue, abort
> immediately, or try to save his "document."

Additional point: saving documents in this situation is different from
any other situation -- some chance that files saved to will be
corrupted. So, good idea to save to new file names. But now we're
into non-C++ territory!


------------------ TOPIC 3 -- non-rethrowing "catch(...)"

[About the situation with no generic "catch(...)" involved, and
low-level exception occuring]


> You can just let them terminate your
> program. You can then examine the register contents from the moment
> of termination, and they will be unaffected by any stack unwinding.
> However, I have never found that sort of information useful for
> debugging. Have you?

Yes. Doesn't mean I was the guilty party, though.


> On the other hand, since the stack is never unwound, destructors never
> get called, files may not get flushed, etc.
> (actually, it's implementation-defined whether destructors get
> called in the case of an uncaught exception, but I doubt that this
> happens for hardware exceptions).

Visual C++ makes a very good attempt at it, but the documentation
states that there might be problems. Of course, if there *are*
problems, then any self-designed handler would fare no better!

> With the handler function , if you don't want to handle a


> particular exception, you can re-throw it

A low-level exception can only be caught by a simple "catch(...)"
(in C++ implementations support this, at all), which leaves
no information about the exception, so one does not really know
whether to handle the beast or rethrow it would be best action.

A highest level "catch(...)" with logging, cleanup & rethrowing might
be a good solution. Especially if combined with non-standard
functionality to retrieve some information about the low-level
exception. However, a "catch(...)" which just eats the exception
would in all probability be a disservice to the user.


[out of context quoting, about cathing low-level exception with
"catch(...)" and terminating without rethrowing]


> and if you like you can log
> some context information, such as the file and line number where the
> exception was first caught. And, to the extent that the stack is
> unwound, destructors will definitely get called.

Not sure that weights up for convenient crash pinpointing the
origin and allowing just-in-time debugging with dynamic context
preserved. Perhaps employ different methods in release and debug
versions? Weigh in risk of cleanup doing harm / crashing.


Cheers,

- Alf


--
alf_DOT_steinbach_AT_ac_DOT_com (clean the address before replying)

Jerry Leichter

unread,
Jun 10, 1999, 3:00:00 AM6/10/99
to
| > The distinction you're drawing is based on knowledge of one
| > architecture and has no really broad meaning.
|
| If there is, IYO, no "difference between an exception raised in
| the built-in new operator, and an exception raised in the built-in
| floating divide operator":
|
| Please provide standard C++ code that catches the latter kind.

C++ standard chooses not to defined an exceptions that might result
from, say, a division by 0. In fact, following many years of precedent
in C, it says that "If during the evaluation of an expression, the
result is not mathematically defined or not in the range of
representable values for its type, the behavior is undefined ... [Note:
most existing implementations of C++ ignore integer overflows.
Treatment of division by zero, forming a remainder using a zero divisor,
and all floating point exceptions vary among machines, and is usually
adjustable by a library function.]"

You seem to feel that there is something fundamental about FP arithmetic
that makes it impossible for a particular implementation to define an
exception and say that "for this implementation, the undefined-by-the-
standard result of x/0.0 is to throw DIV_BY_0". You're wrong. Having
made the commitment to do this, the implementation - to be useful -
would need to do other things as well - but there's nothing fundamental
to C++ or FP arithmetic preventing it from doing so.

It *is* true that the lack of uniformity in hardware implementations -
combined with established coding practices - that have prevented the
*Standard* from specifying that various kinds of arithmetic exception
conditions should throw exceptions. But that's quite another story.

| > Do, please, learn something about the standard usage of terms in

| > business - and about something other than the x86.
|
| There may be more polite ways to express disagreement. As it is,
| I cannot answer since I do not know what is being disagreed with.

You're correct, and I apologize for my tone.
-- Jerry

Stanley Friesen [Contractor]

unread,
Jun 10, 1999, 3:00:00 AM6/10/99
to
In article <Pine.OSF.4.05.990609...@azure.dstc.edu.au>,

Michi Henning <mi...@triodia.com> wrote:
>
>Hmmm... Isn't the real motivation for the feature that Windows is incapable
>of cleanly terminating a process because it cannot reliably track all
>the processes' resources?

Gads, aren there NO good programmers at MS?

With 32-bit protected mode programs, there is NO excuse for the OS not
tracking ALL per-process resources, since each process is isolated,
and has to call the OS through a trap or gate. This means there is
certain hook for capturing all resource allocations - the OS gate itself.

>The way I see it, catching things like floating point exceptions via
>catch (...) achieves nothing but passing the buck for recovering
>OS resources from the OS to the application code. Bad tradeoff, as far as
>I am concerned...

For an OS that is *still* not up to par.

Alf P. Steinbach

unread,
Jun 11, 1999, 3:00:00 AM6/11/99
to
In article <jY473.13261$AL5....@ndnws01.ne.mediaone.net>,
Dave Abrahams <abra...@mediaone.net> wrote:
> In article <7jgmlu$pi6$1...@nnrp1.deja.com> , "Alf P. Steinbach"

> <alf_st...@my-deja.com> wrote:
>
>
> > However, in that case *all* current code becomes suspect
> > and must be re-tested at all levels, including system test.
>
> Hardly. New exception types don't do anything to affect the control
> paths that are available to the program; all they do is carry new
> information about what went wrong.

Even at the pure C++ technical level this is not the case. A function
with an exception specification may have worked splendidly, rethrowing
lower-level exceptions as expected, yet may terminate the program when
functions that it uses start throwing unexpected exceptions. Whether
that is because of a change in the code or, perhaps, in the conditions
the code operates in (whatever the reason is for incorporating a new
exception type in std. list).

Secondly, the exception handling (as opposed to catching, e.g.
cleanup, retry, whatever) may not be prepared to deal correctly with
the new exception, since in practice there's no such thing as perfect
code -- perhaps except for Knuth's TeX.

Therefore, retesting everything that just might be involved with the
new exception type is, IMO, the only prudent thing to do.

Hth.,

- Alf


--
alf_DOT_steinbach_AT_ac_DOT_com (clean the address before replying)


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Dave Abrahams

unread,
Jun 11, 1999, 3:00:00 AM6/11/99
to
In article <7jm5vk$ood$1...@nnrp1.deja.com> , "Alf P. Steinbach"
<alf_st...@my-deja.com> wrote:

> a macro signifies that one is
> stepping outside normal rules of language, or conventional use of
> the language (as the report_error function does by requiring to be
> called from within a catch), and this is important for maintenance
> personell who can see at a glance that a kludge is involved, which

> is the case no matter how general exception catching is implementedchanism.

<snip>

> If
> forced by a library or a framework to use the idea of general exception
> catching, I'd choose the macro approach to clearly separate good code
> (skimming ok) from kludge (needs scrutiny), and to encapsulate stuff
> like picking up source code location.

I think Mr. Steinbach's definition of "kluge" is a bit outside the commonly
accepted definition. I hope that the report_error() approach will be come a
common idiom in C++ programming, and thus easily recognizable.

The logic that seems to be at work here doesn't bode well for the craft of
programming in C++. For example, there are communities making the transition
from C to C++ where templates are unfamiliar territory; I suppose in that
culture, programmers should use macros instead, just to warn people that
something unusual is going on?

Macros march across scopes, changing the meaning of code
Macros are difficult to debug
....etc...
I don't need to expound all of the evils of macros

Using a macro just because the alternative might be unfamiliar to a reader
strikes me as deeply regressive.

>It's possible to use catch( ... ) + normal function call + try + throw
>+ catch( whatever ) as the mechanism of generic catcher, which can be
>made to do only the job of catching and extracting information from
>exception.

So, in this proposal, all functions which can throw must actually be called
from within the "generic catcher"? Or have I misunderstood?

James...@dresdner-bank.com

unread,
Jun 11, 1999, 3:00:00 AM6/11/99
to
> On 7 Jun 1999, Alf P. Steinbach wrote:
>
> > Specific implementations of C++, however, may provide mechanisms,
> > and "catch(...)" is one such possible mechanism, and a very natural
> > one (e.g., it's used this way in Visual C++).
>
> Hmmm... Isn't the real motivation for the feature that Windows is
incapable
> of cleanly terminating a process because it cannot reliably track all
> the processes' resources? In other words, how desirable would the
feature
> be if the OS could guarantee clean process termination (without
leaving
> locked memory regions behind and such)? To question this further, if
> your program detects that such a low-level exception has indeed
occurred,
> how would you react to it, other than by terminating?

As far as I can tell, this is a problem with every OS. At some point,
the distinction between persistent resources and non-persistent ones
depends on the application; there is no way the system can make the
calls with 100% accuracy. Thus, UNIX (and most other systems) will
recover all memory, open file descripters, file locks, and a number of
other resources, but will calmly leave temporary files and such lying
around, simply because the OS has no way of knowing which files are
temporary, and which ones are meant to contain persistent data.

--
James Kanze mailto:
James...@dresdner-bank.com
Conseils en informatique orientée objet/
Beratung in objekt orientierter
Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49 (069) 63 19 86
27


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Hyman Rosen

unread,
Jun 11, 1999, 3:00:00 AM6/11/99
to
"Dave Abrahams" <abra...@mediaone.net> writes:
> I think Mr. Steinbach's definition of "kluge" is a bit outside the
commonly
> accepted definition. I hope that the report_error() approach will be come
a
> common idiom in C++ programming, and thus easily recognizable.

I hope so. I saw it, had that flash of "Oh, yeah!" insight, and promptly
added it to some code I'm writing.

> Using a macro just because the alternative might be unfamiliar to a reader
> strikes me as deeply regressive.

Of course it is. Anyone who suggests awful multi-line macros instead
of your clean approach probably just doesn't get C++.

Alf P. Steinbach

unread,
Jun 11, 1999, 3:00:00 AM6/11/99
to
In article <al_73.823$5H.3...@ndnws01.ne.mediaone.net>,
"Dave Abrahams" <abra...@mediaone.net> wrote:
> In article <7jm5vk$ood$1...@nnrp1.deja.com> , "Alf P. Steinbach"

> <alf_st...@my-deja.com> wrote:
>
> > a macro signifies that one is
> > stepping outside normal rules of language, or conventional use of
> > the language (as the report_error function does by requiring to be
> > called from within a catch), and this is important for maintenance
> > personell who can see at a glance that a kludge is involved, which
> > is the case no matter how general exception catching is
> > implemented.
>
> <snip>

>
> > If forced by a library or a framework to use the idea of general
> > exception catching, I'd choose the macro approach to clearly
> > separate good code (skimming ok) from kludge (needs scrutiny), and
> > to encapsulate stuff like picking up source code location.
> >
>
> I think Mr. Steinbach's definition of "kluge" is a bit outside the

You may call me "Alf"... ;-)


> commonly accepted definition. I hope that the report_error() approach
> will be come a common idiom in C++ programming, and thus easily
> recognizable.
>

> The logic that seems to be at work here doesn't bode well for the
> craft of programming in C++. For example, there are communities
> making the transition from C to C++ where templates are unfamiliar
> territory; I suppose in that culture, programmers should use macros
> instead, just to warn people that something unusual is going on?

Nothing unusual is going on in that case -- that's normal use of
the language. As a general rule, every defensible use of a macro
signifies a shortcoming of the language. So macros should be used
only where the alternative is worse (e.g., one clearly defensible
use is as header file guards; the particular usage as a "catch-list"
is borderline).


> Macros march across scopes, changing the meaning of code
> Macros are difficult to debug
> ....etc...
> I don't need to expound all of the evils of macros

Correct.

>
> Using a macro just because the alternative might be unfamiliar to a
> reader strikes me as deeply regressive.

Also agreed.


>
> > It's possible to use catch( ... ) + normal function call + try +
> > throw + catch( whatever ) as the mechanism of generic catcher,
> > which can be made to do only the job of catching and extracting
> > information from exception.

> So, in this proposal, all functions which can throw must actually be
> called from within the "generic catcher"? Or have I misunderstood?

Yes, I think you've misunderstood, whether you actually mean (1) that


"I hope that the report_error() approach will be come a common idiom

in C++ programming, and thus easily recognizable.", or (2) the apparent
implication of the above that the scheme would be impractical (these
are contradictory views).

In the case of (1), I'd certainly hope not: the requirements of
exception catching & handling are extremely unlikely to be uniform
throughout an application. In the case of (2), if a function F
calls functions that might throw any number of exceptions types
from a known list, which is the same (or nearly the same) in a
large number of places in the application, and redesign could not
fix this sorry situation with a reasonable amount of work, then the
approach would or could be warranted, and F would have to do all
its normal case work within the generic catcher, which as I see it
really would be a kludge. But in general, if given a choice, I'd
prefer a better design...

Marc Espie

unread,
Jun 11, 1999, 3:00:00 AM6/11/99
to
In article <Pine.OSF.4.05.990610...@azure.dstc.edu.au>,

Michi Henning <mi...@triodia.com> wrote:
>On 9 Jun 1999, Jerry Leichter wrote:

>> | Floating point instructions are such asynchronous events, on most
>> | computers.

>> Do, please, learn something about the standard usage of terms in this


>> business - and about something other than the x86.

>I'm not sure it is that simple. I think the important point is that


>such a hardware exception can happen at unpredictable times, whereas
>any other C++ exception has an explicit throw statement somewhere.

Any half-decent chip (alpha, for instance) has explicit directives to
deal with this problem. In that case, you've got an instruction to wait
for all pending computations to have completed. Decent compilers know
how to use this.

On brain-dead chips, you can use a decent OS, that will work around such
problems... or in case that's impossible, the chip is not supported.

In an ideal world, the chip would be dead.

I feel a bit sorry for the poor guys who have to work around broken chips
and broken OSes, but I really, really don't like to have to cater to that
brokenness. Reminds me of the 64K malloc limit and such non-sense as
`small/fat/huge' memory models.

If you prefer, the asynchronous nature of arithmetic overflows and zero
divides is well-known, and there have been sane solutions to it everywhere
for quite a few years now... One always wonders at the inability of
Intel/Microsoft to catch a clue...

--
Marc Espie
|anime, sf, juggling, unicycle, acrobatics, comics...
|AmigaOS, OpenBSD, C++, perl, Icon, PostScript...
| `real programmers don't die, they just get out of beta'

Andrei Alexandrescu

unread,
Jun 12, 1999, 3:00:00 AM6/12/99
to
In article <al_73.823$5H.3...@ndnws01.ne.mediaone.net>,

"Dave Abrahams" <abra...@mediaone.net> wrote:
> I hope that the report_error() approach will be
come a
> common idiom in C++ programming, and thus easily recognizable.

I agree with Dave. I also find the EatException() function cool. It's a
great piece of reuse, while multiple catch'es suck. Macros suck just as
well.

I gave it the name of EatException() to make things clear for people who
disagree with this technique.

Actually, I even implemented EatException() in production code. The
problem is, MSVC 6 didn't get it correctly. The program crashes in
the second throw. Does anyone know of a workaround?

Andrei

Dave Abrahams

unread,
Jun 12, 1999, 3:00:00 AM6/12/99
to
In article <7josmm$nsa$1...@nnrp1.deja.com> , "Alf P. Steinbach"
<alf_st...@my-deja.com> wrote:

> Even at the pure C++ technical level this is not the case. A function
> with an exception specification may have worked splendidly, rethrowing
> lower-level exceptions as expected, yet may terminate the program when
> functions that it uses start throwing unexpected exceptions. Whether
> that is because of a change in the code or, perhaps, in the conditions
> the code operates in (whatever the reason is for incorporating a new
> exception type in std. list).

Oh, well, of course you're right. If you don't like unexpected termination
and you're foolish enough to use exception-specifications, you deserve what
you get, though!

> Secondly, the exception handling (as opposed to catching, e.g.
> cleanup, retry, whatever) may not be prepared to deal correctly with
> the new exception, since in practice there's no such thing as perfect
> code -- perhaps except for Knuth's TeX.

Isn't this a red herring? How often have you ever done anything unique in a
catch clause based on exception type? In my code there's almost never a need
for anything but catch(...) when I use catch clauses at all. Most code is
exception-neutral, and simply passes the error along to the caller after
cleaning up. In very rare cases, there might be some kind of alternative
recovery action, like:

bool succeeded = false;
try {
efficient_method();
succeeded = true;
}
catch ( efficient_method_failed& ) {}
if (!succeeded)
inefficient_method();

but this is so unusual I had to think hard about how to write it.

> Therefore, retesting everything that just might be involved with the
> new exception type is, IMO, the only prudent thing to do.

Of course, if you never use exception-specifications and stick to catch(...)
except for error reporting, this is easy. Otherwise, you have a maintenance
nightmare on your hands...

Hope this _really_ helps,
Dave

Dave Abrahams

unread,
Jun 12, 1999, 3:00:00 AM6/12/99
to
>> > It's possible to use catch( ... ) + normal function call + try +
>> > throw + catch( whatever ) as the mechanism of generic catcher,
>> > which can be made to do only the job of catching and extracting
>> > information from exception.
>
>> So, in this proposal, all functions which can throw must actually be
>> called from within the "generic catcher"? Or have I misunderstood?
>
> Yes, I think you've misunderstood, whether you actually mean (1) that
> "I hope that the report_error() approach will be come a common idiom
> in C++ programming, and thus easily recognizable.",

No, not that.

> or (2) the apparent
> implication of the above that the scheme would be impractical (these
> are contradictory views).

Not that (about the implication) either; I'm just trying to understand what
you're proposing in the text I quoted at the top of this message. I don't
see how it could work unless all functions that can throw are called from
within the generic catcher. So if you have a serious alternative/variant for
report_error(), let's see it!

> In the case of (2), if a function F
> calls functions that might throw any number of exceptions types
> from a known list, which is the same (or nearly the same) in a
> large number of places in the application, and redesign could not
> fix this sorry situation with a reasonable amount of work,

And what, exactly, is so sorry about that situation?

> then the
> approach would or could be warranted, and F would have to do all
> its normal case work within the generic catcher, which as I see it
> really would be a kludge. But in general, if given a choice, I'd
> prefer a better design...

You haven't made yourself clear here at all. If you have something in mind,
a little bit of sample code would go a long way to explaining it.

Valentin Bonnard

unread,
Jun 14, 1999, 3:00:00 AM6/14/99
to
Dave Abrahams wrote:

> In very rare cases, there might be some kind of alternative
> recovery action, like:
>
> bool succeeded = false;
> try {
> efficient_method();
> succeeded = true;
> }
> catch ( efficient_method_failed& ) {}
> if (!succeeded)
> inefficient_method();
>
> but this is so unusual I had to think hard about how to write it.

You thougth too hard:

try {
efficient_method ();
} catch (efficient_method_failed&) {
inefficient_method();
}

Even if it isn't theoricaly equivalent, I hope that in
practice it will be.

> Of course, if you never use exception-specifications

except throw() for code which simply isn't exception safe,
or possibly dtors

--

Valentin Bonnard

Software

unread,
Jun 24, 1999, 3:00:00 AM6/24/99
to

eed...@filenet.com wrote in article <7hs4ui$f4e$1...@nnrp1.deja.com>...
>Hello
>
>I am new to exception handling in C++ and am thinking of creating a
>custom exception class. Is it wise and or desirable to create it using
>the exception class defined by the C++ standard library as its base
>class?
>
>Thanks,
>
>E. Edeen
>eed...@filenet.com


Hi,

I'm not an early bird in this discussion, but still I'd like
to make a point no one else covered:
The std exception hierarchie doesn't derive virtual from
std::exception. That means you should not derive from
std::exception AND another exception derived from
std::exception (maybe for class MyOutOfRange). If you
do so, std::exception is no longer a so called
"non-ambigous public base class" and your exception
cannot be called by a catch(std::exception) clause.
In short: MI doesn't work as expected with std::exception
as a base class.

This is one thing to consider when thinking of deriving
from std::exception.

Shobi

dave_a...@my-deja.com

unread,
Jul 1, 1999, 3:00:00 AM7/1/99
to
I think I finally see why the users of some compiler implementations
dislike catch(...). "Avoid catch(...)" has crept into many coding
standards over the years, but many people have forgotten why.

As far as I can tell, the only good reason is that on compilers which
use the same mechanism for "structured" and "C++" exceptions, you can't
do post-mortem debugging. If an assert fires in a program not run under
the debugger, you would like to be able to bring up the debugger at the
point of failure. If the point point of failure is enclosed in
catch(...), it doesn't work.

Of course, I have previously mentioned that there are other compilers
available for these systems which don't mix structured and C++
exceptions; presumably they don't have the same problems.

-Dave

Ross Smith

unread,
Jul 1, 1999, 3:00:00 AM7/1/99
to
dave_a...@my-deja.com wrote:
>
> I think I finally see why the users of some compiler implementations
> dislike catch(...). "Avoid catch(...)" has crept into many coding
> standards over the years, but many people have forgotten why.
>
> As far as I can tell, the only good reason is that on compilers which
> use the same mechanism for "structured" and "C++" exceptions, you can't
> do post-mortem debugging. If an assert fires in a program not run under
> the debugger, you would like to be able to bring up the debugger at the
> point of failure. If the point point of failure is enclosed in
> catch(...), it doesn't work.

No. The reason why catch(...) is a bad idea is simply that it's useless.
It gives you no information about what went wrong, so what can you
possibly do about it? Better to ignore the exception and let it
propagate, because the next level up may know what to do with it.

"Never test for an error condition you don't know how to handle."
-- Anon

--
Ross Smith <ros...@ihug.co.nz> The Internet Group, Auckland, New Zealand
========================================================================
The good news, according to the FCC, is that television viewing won't be
interrupted by the Y2K problem. The bad news, according to the rest of
us, is that television viewing won't be interrupted by the Y2K problem.
-- Jonathan Erickson in Dr Dobb's Journal

Lisa Lippincott

unread,
Jul 2, 1999, 3:00:00 AM7/2/99
to
Ross Smith <ros...@ihug.co.nz> wrote:

> No. The reason why catch(...) is a bad idea is simply that it's useless.
> It gives you no information about what went wrong, so what can you
> possibly do about it? Better to ignore the exception and let it
> propagate, because the next level up may know what to do with it.
>

> "Never test for an error condition you don't know how to handle."
> -- Anon

Didn't we just have this thread? In my less than humble opinion, Anon
is correct (in the world of exceptions), while Ross Smith expresses a
widespread fallacy.

Catch(...) tells you exactly what went wrong: the code in the try block
failed. In most cases, that should be enough information to recover from
the exception; specific exception handlers may be added to improve
recovery from specific exceptions.

--Lisa Lippincott

Nathan Myers

unread,
Jul 4, 1999, 3:00:00 AM7/4/99
to
Lisa Lippincott <lisa_li...@advisories.com> wrote:
>Ross Smith <ros...@ihug.co.nz> wrote:
>
>> No. The reason why catch(...) is a bad idea is simply that it's useless.
>
>Catch(...) tells you exactly what went wrong: the code in the try block
>failed.

In particular, it allows you to clean up anything you left from the
try block, and then rethrow. No information is lost. I *only* use
catch(...) in my library code, because I don't care why it failed,
and I always rethrow.

--
Nathan Myers
n...@nospam.cantrip.org http://www.cantrip.org/

Stanley Friesen [Contractor]

unread,
Jul 6, 1999, 3:00:00 AM7/6/99
to
In article <377C1DB9...@ihug.co.nz>,

Ross Smith <ros...@ihug.co.nz> wrote:
>
>No. The reason why catch(...) is a bad idea is simply that it's useless.
>It gives you no information about what went wrong, so what can you
>possibly do about it?

This is too strong. It is useless as a *final* handler. But, combined
with a re-throw, it is useful for cleaning up resources not protected
by classes with destructors.

Admittedly it is better to avoid thhis situation, but there may be cases
where it is too difficult to change existing code to be worth the time.

>Better to ignore the exception and let it
>propagate, because the next level up may know what to do with it.

Propagate, certainly. But that does not necessarily mean ignore.

The rule I would propose is:
when "catch(...)" is used, *always* re-throw the original exception.

Lisa Lippincott

unread,
Jul 7, 1999, 3:00:00 AM7/7/99
to
Stanley Friesen [Contractor] <sta...@West.Sun.COM> wrote:

> The rule I would propose is:
> when "catch(...)" is used, *always* re-throw the original exception.

I think you go too far. In some cases, there is a less fallible algorithm
which can be substituted for the code in the try block. As an extreme
example, a central algorithm in the project I'm working on boils down
to this code:

try { return ComplicatedTest(); }
catch( ... ) { return false; }
--Lisa Lippincott

Ivan J Johnson

unread,
Jul 7, 1999, 3:00:00 AM7/7/99
to
In article <7ltep7$d...@abyss.West.Sun.COM>,

sta...@West.Sun.COM (Stanley Friesen [Contractor]) wrote:
> In article <377C1DB9...@ihug.co.nz>,
> Ross Smith <ros...@ihug.co.nz> wrote:
> >
> >No. The reason why catch(...) is a bad idea is simply that it's
useless.
> >It gives you no information about what went wrong, so what can you
> >possibly do about it?
>
> This is too strong. It is useless as a *final* handler. But,
combined
> with a re-throw, it is useful for cleaning up resources not protected
> by classes with destructors.
>
> Admittedly it is better to avoid thhis situation, but there may be
cases
> where it is too difficult to change existing code to be worth the
time.
>
> >Better to ignore the exception and let it
> >propagate, because the next level up may know what to do with it.
>
> Propagate, certainly. But that does not necessarily mean ignore.
>
> The rule I would propose is:
> when "catch(...)" is used, *always* re-throw the original exception.

This thread already has examples of how catch(...) can be useful, which
Mr. Smith apparently overlooked.

As for your proposed rule, what about catch(...) blocks in destructors,
or in main()? What about performance-tuning operations that are
*allowed* to fail?
--
Regards,
Ivan

License to compile?
http://www.deja.com/group/us.issues.occupations.computer-programmers


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Nick Ambrose

unread,
Jul 7, 1999, 3:00:00 AM7/7/99
to

Ivan J Johnson wrote:

What if catch(...) catches an OS exception caused by dereferencing a
garbage pointer and corrupting goodness-knows what .... trying to continue
on from "something bad but I am not sure what" doesn't sound safe to me.

Nick

Ivan J Johnson

unread,
Jul 8, 1999, 3:00:00 AM7/8/99
to
In article <37839926...@interdyn.com>,
Nick Ambrose <ni...@interdyn.com> wrote:

>
>
> Ivan J Johnson wrote:
>
> >
> > This thread already has examples of how catch(...) can be useful,
which
> > Mr. Smith apparently overlooked.
> >
> > As for your proposed rule, what about catch(...) blocks in
destructors,
> > or in main()? What about performance-tuning operations that are
> > *allowed* to fail?
>
> What if catch(...) catches an OS exception caused by dereferencing a
> garbage pointer and corrupting goodness-knows what .... trying to
continue
> on from "something bad but I am not sure what" doesn't sound safe to
me.

An implementation that trapped hardware exceptions with catch(...)
would not conform to the Standard. I assume you are thinking of
Windows and MSVC? I would agree that it's probably not a good idea to
try to recover from a GPF; a more sensible response would be to
terminate the offending process and, if necessary, arrange for it to be
automatically restarted. However, that still doesn't preclude using
catch(...). To complete the example below, you would need to use the
Win32 _set_se_translator function to map those troublesome hardware
exceptions to a C++ structure.

struct SE_Exception
{
SE_Exception( unsigned int n ) : nSE( n ) {}
unsigned int nSE;
};

void f()
{
try
{
do_something(); // might throw
}
catch(...)
{
report_exception();
}
}

void report_exception()
{
try
{
throw;
}
catch(std::exception& e)
{
cout << e.what() << endl;
}
catch(CException* pe)
{
ProcessException(pe);
}
catch(SE_exception& se)
{
// probably a hardware exception, spit it out
throw;
}
catch(...)
{
cout << "unknown error" << endl;
}
}

--
Regards,
Ivan


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Nick Ambrose

unread,
Jul 8, 1999, 3:00:00 AM7/8/99
to

Ivan J Johnson wrote:

I wasn't aware that such an implementation would be non-conforming,
although I am by no means an expert on what is or is not allowed by the
standard (I guess since the standard presumably makes no mention of
os/hardware-specific exceptions, it's almost by definition)

Nick

Ivan J Johnson

unread,
Jul 9, 1999, 3:00:00 AM7/9/99
to
In article <3785109B...@interdyn.com>,

Hmm . . . I think I misspoke there. Usually those hardware exceptions
pop up in situations that are 'undefined behavior' according to the
Standard. Therefore, the implementation can do whatever it pleases,
including throwing an exception. In any case, it's just a peculiarity
of one particular platform; AFAIK most systems don't do that.

--
Regards,
Ivan


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

bla...@flash.net

unread,
Jul 9, 1999, 3:00:00 AM7/9/99
to
In article <37839926...@interdyn.com>, Nick Ambrose
<ni...@interdyn.com> wrote:

> Ivan J Johnson wrote:
>
> > In article <7ltep7$d...@abyss.West.Sun.COM>,
> > sta...@West.Sun.COM (Stanley Friesen [Contractor]) wrote:
> > > In article <377C1DB9...@ihug.co.nz>,
> > > Ross Smith <ros...@ihug.co.nz> wrote:
> > > >
> > > >No. The reason why catch(...) is a bad idea is simply that it's useless.
> > > >It gives you no information about what went wrong, so what can you
> > > >possibly do about it?
> > >
> > > This is too strong. It is useless as a *final* handler. But, combined
> > > with a re-throw, it is useful for cleaning up resources not protected
> > > by classes with destructors.

[snip]


> What if catch(...) catches an OS exception caused by dereferencing a
> garbage pointer and corrupting goodness-knows what .... trying to continue
> on from "something bad but I am not sure what" doesn't sound safe to me.

But if one didn't have a catch block at all, those would "continue on from
..." anyway.

void f() {
g(); // throws something wild
}

Oh, now we need to clean up something.

void f() {
something smth;
try {
g();
}
catch ( ... ) {
cleanup( smth );
throw; // re-throw exception
}
}

This is something fundamental about exceptions, their transparency, an
aspect that allows almost all code to seem ignorant of them, yet still
arrange for the correct thing to happen when an exception does occur.

Whenever I read code that has explicit error handling, I cringe. It's such
a mess to be checking return codes after all those calls, and placing
little bits of (redundant) cleanup code throught the mainline code. I was
reading a book that discussed coding style, and an absurd number of these
dealt with the complexities of embedding these error "handlets" throught
the main code.

There is a BETTER WAY. There are exceptions. Like any new tool, they
require some new paradigms and concepts, but the resulting clarity and
simplicity is almost unbelievable in comparison.

Ivan J. Johnson

unread,
Jul 10, 1999, 3:00:00 AM7/10/99
to
In article <1999070921...@ares.flash.net>,

bla...@flash.net wrote:
> In article <37839926...@interdyn.com>, Nick Ambrose
> <ni...@interdyn.com> wrote:
> > What if catch(...) catches an OS exception caused by dereferencing a
> > garbage pointer and corrupting goodness-knows what .... trying to
continue
> > on from "something bad but I am not sure what" doesn't sound safe
to me.
>
> But if one didn't have a catch block at all, those would "continue on
from
> ..." anyway.

I'm not sure what you mean by that. The default is for uncaught
exceptions to terminate the program.

>
> void f() {
> g(); // throws something wild
> }
>
> Oh, now we need to clean up something.
>
> void f() {
> something smth;
> try {
> g();
> }
> catch ( ... ) {
> cleanup( smth );
> throw; // re-throw exception
> }
> }

That is one way, but it is usually better to incorporate the cleanup
into 'something's destructor when possible.

>
> This is something fundamental about exceptions, their transparency, an
> aspect that allows almost all code to seem ignorant of them, yet still
> arrange for the correct thing to happen when an exception does occur.
>
> Whenever I read code that has explicit error handling, I cringe. It's
such
> a mess to be checking return codes after all those calls, and placing
> little bits of (redundant) cleanup code throught the mainline code. I
was
> reading a book that discussed coding style, and an absurd number of
these
> dealt with the complexities of embedding these error "handlets"
throught
> the main code.
>
> There is a BETTER WAY. There are exceptions. Like any new tool, they
> require some new paradigms and concepts, but the resulting clarity and
> simplicity is almost unbelievable in comparison.

Yes, absolutely. It's common for C functions to shrink over 50% when
they are converted to use exceptions and the resource-acquisition-is-
initialization technique. For error handling, exceptions come as close
as possible to the ideal that "the programmer does nothing, and yet
nothing remains undone."

--
Regards,
Ivan


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

bla...@flash.net

unread,
Jul 10, 1999, 3:00:00 AM7/10/99
to
In article <7m6uub$1oc$1...@nnrp1.deja.com>, "Ivan J. Johnson"
<iva...@my-deja.com> wrote:

> In article <1999070921...@ares.flash.net>,
> bla...@flash.net wrote:
> > In article <37839926...@interdyn.com>, Nick Ambrose
> > <ni...@interdyn.com> wrote:
> > > What if catch(...) catches an OS exception caused by dereferencing a
> > > garbage pointer and corrupting goodness-knows what .... trying to
> > > continue
> > > on from "something bad but I am not sure what" doesn't sound safe
> > > to me.
> >
> > But if one didn't have a catch block at all, those would "continue on
> > from ..." anyway.
>
> I'm not sure what you mean by that. The default is for uncaught
> exceptions to terminate the program.

I was referring to individual functions, not the entire call chain from
main(). I assume that one would put a catch ( ... ) block in main, since
otherwise, an uncaught exception may or may not call destructors as part
of unwinding the stack.

> > void f() {
> > g(); // throws something wild
> > }
> >
> > Oh, now we need to clean up something.
> >
> > void f() {
> > something smth;
> > try {
> > g();
> > }
> > catch ( ... ) {
> > cleanup( smth );
> > throw; // re-throw exception
> > }
> > }
>
> That is one way, but it is usually better to incorporate the cleanup
> into 'something's destructor when possible.

The code was to illustrate my point about how it was equivalent, from an
outside point-of-view, not one of style. I get tired of seeing replies on
these newsgroups talking of style, when style comments are not relevant,
"int main()" being the most common :-) Maybe I need to put more
disclaimers on my code examples, stating their scope, and whether they are
illustrating a point or a style issue.

Yes, one wants to use destructors for cleanup almost exclusively, since it
is clearer and much less error-prone, due to specification of cleanup at
the point of initialization, and no redundant code to get wrong.

[snip]


> For error handling, exceptions come as close
> as possible to the ideal that "the programmer does nothing, and yet
> nothing remains undone."

And yet there are those who still advocate a style of "light-weight"
constructors that basically do nothing useful, with a real init() function
that must be called. Maybe when certain major compilers start using a
zero-overhead in the non-throw case implementation of exceptions, they
will change their minds. Heck, it's the destructor for an object that can
add the overhead for exceptions, not really the constructor. I sure hope
nobody will start suggesting not to use destructors (in general) either!

Joe Keane

unread,
Jul 11, 1999, 3:00:00 AM7/11/99
to
In article <060719991629255707%lisa_li...@advisories.com>,

Lisa Lippincott <lisa_li...@advisories.com> wrote:
>As an extreme example, a central algorithm in the project I'm working
>on boils down to this code:
>
>try { return ComplicatedTest(); }
> catch( ... ) { return false; }

Bad!

That's the kind of code where someone who finally tracks down a subtle
problem is likely to hunt down the original coder and give an unpleasant
expression of their opinion on guidelines for handling errors.

Just don't do it.

--
Joe Keane, amateur mathematician

Ivan J. Johnson

unread,
Jul 11, 1999, 3:00:00 AM7/11/99
to
In article <1999071018...@ares.flash.net>,
bla...@flash.net wrote:
> [...]

> And yet there are those who still advocate a style of "light-weight"
> constructors that basically do nothing useful, with a real init()
function
> that must be called.

Really? Who advocates that? Where can I send the flames? :-)

--
Regards,
Ivan


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Robert O'Dowd

unread,
Jul 12, 1999, 3:00:00 AM7/12/99
to
Joe Keane wrote:
>
> In article <060719991629255707%lisa_li...@advisories.com>,
> Lisa Lippincott <lisa_li...@advisories.com> wrote:
> >As an extreme example, a central algorithm in the project I'm working
> >on boils down to this code:
> >
> >try { return ComplicatedTest(); }
> > catch( ... ) { return false; }
>
> Bad!
>
> That's the kind of code where someone who finally tracks down a subtle
> problem is likely to hunt down the original coder and give an unpleasant
> expression of their opinion on guidelines for handling errors.
>
> Just don't do it.
>

The above approach, doesn't offend my sensibilities. Consider the
following scenario.

1) ComplicatedTest() presumably returns a bool (true or false) based
on some conditions.

2) ComplicatedTest() also may throw exceptions to indicate (say)
inability to complete parts of the test. For example, it
could not find a data file that is fundamental to doing the
test.

3) I want to do a complex series of operations based on results of
the test. If ComplicatedTest() returns false, or throws, then
there is no benefit gained by running those ops. For example,
if the missing data file is needed to do the operations.
In either case, cleanup can proceed.

So, I could do this as follows.

bool WantToDoTest() throw()
{
bool retval = false;
try
{
retval = ComplicatedTest();
}
catch (...)
{
retval = false;
}
return retval;
}


void MonsterOperation()
{
if (WantTodoTest())
{
// tromp some monsters
}
else
{
// can't tromp. Recover.
}
}

Of course, utility of this approach depends on what side effects I
can expect from ComplicatedTest(). As long as I take responsibility
for recovery from errors sensibly, I fail to see why this approach
is necessarily evil/unmaintainable/whatever.

What am I missing?????


-<Automagically included trailer>
Robert O'Dowd Ph +61 (8) 8259 6546
MOD/DSTO Fax +61 (8) 8259 5139
P.O. Box 1500 Email:
robert...@dsto.defence.gov.au
Salisbury, South Australia, 5108

Disclaimer: Opinions above are mine and may be worth what you paid for
them

Nick Ambrose

unread,
Jul 12, 1999, 3:00:00 AM7/12/99
to

bla...@flash.net wrote:

> In article <7m6uub$1oc$1...@nnrp1.deja.com>, "Ivan J. Johnson"
> <iva...@my-deja.com> wrote:
>
> > In article <1999070921...@ares.flash.net>,
> > bla...@flash.net wrote:
> > > In article <37839926...@interdyn.com>, Nick Ambrose
> > > <ni...@interdyn.com> wrote:
> > > > What if catch(...) catches an OS exception caused by dereferencing a
> > > > garbage pointer and corrupting goodness-knows what .... trying to
> > > > continue
> > > > on from "something bad but I am not sure what" doesn't sound safe
> > > > to me.
> > >
> > > But if one didn't have a catch block at all, those would "continue on
> > > from ..." anyway.
> >
> > I'm not sure what you mean by that. The default is for uncaught
> > exceptions to terminate the program.
>
> I was referring to individual functions, not the entire call chain from
> main(). I assume that one would put a catch ( ... ) block in main, since
> otherwise, an uncaught exception may or may not call destructors as part
> of unwinding the stack.

Well, the point i was really making (and I think the context got lost in the
mist of this thread somewhere) was that it is probably quite insane to try
to
continue from an exception caught with catch(...) as (i believe) someone
else
stated previously.

Nick

Alf P. Steinbach

unread,
Jul 12, 1999, 3:00:00 AM7/12/99
to
In article <7m93ji$kes$1...@nnrp1.deja.com>,

"Ivan J. Johnson" <iva...@my-deja.com> wrote:
> In article <1999071018...@ares.flash.net>,
> bla...@flash.net wrote:
> > [...]
> > And yet there are those who still advocate a style of "light-weight"
> > constructors that basically do nothing useful, with a real init()
> function
> > that must be called.
>
> Really? Who advocates that? Where can I send the flames? :-)
>

Send flames to Microsoft -- that's the approach used in MFC
(Microsoft Foundation Classes)...

Although the ideal would be a single-level class invariant
established by the constructor, a virtual Init function can
sometimes be useful as a solution to the problem of *virtual
construction* (overriding parts of construction in a subclass).
And that's the exact problem addressed by the use of Init-style
funtions in MFC and other application frameworks. E.g., a
wrapper class for an API-level window, where an object of that
class logically represents a window -- as a generalization of
this problem, consider a set of wrapper classes for any existing
class hierarchy, where the wrapped classes can't be modified, and
the wrapper class hierarchy should mirror the wrapped hierarchy.

There are some other possible solutions (e.g. passing a parts-
constructor object as a parameter to the constructor), including the
option of using Java, where method calls from a constructor are
virtual calls (can lead to subtle errors but is very practical).

It might be interesting to think about possible solutions in
terms of exception safety -- I haven't really done that.

Apart from solving the virtual construction problem with about
zero baggage, within the class where the problem resides, Init-
functions are of course evil evil evil.

Hth.,

- Alf


--
alf_DOT_steinbach_AT_ac_DOT_com (clean the address before replying)

Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

bla...@flash.net

unread,
Jul 13, 1999, 3:00:00 AM7/13/99
to
In article <7mdh89$t95$1...@nnrp1.deja.com>, "Alf P. Steinbach"
<alf_st...@my-deja.com> wrote:

> In article <7m93ji$kes$1...@nnrp1.deja.com>,
> "Ivan J. Johnson" <iva...@my-deja.com> wrote:
> > In article <1999071018...@ares.flash.net>,
> > bla...@flash.net wrote:
> > > [...]
> > > And yet there are those who still advocate a style of "light-weight"
> > > constructors that basically do nothing useful, with a real init()
> > function
> > > that must be called.
> >
> > Really? Who advocates that? Where can I send the flames? :-)
> >
>
> Send flames to Microsoft -- that's the approach used in MFC
> (Microsoft Foundation Classes)...

"Just don't do it" (MFC)

> Although the ideal would be a single-level class invariant
> established by the constructor, a virtual Init function can
> sometimes be useful as a solution to the problem of *virtual
> construction* (overriding parts of construction in a subclass).

Certainly.

But don't make your general users pay!

Derived classes have to be aware of it, but not the general public. The
basic idea is to have the hierarchy "private", and have a templated
wrapper class instantiated for each class.

// reusable code

template<class T>
class wrapper : public T {
public:
wrapper( int i ) { T::init( i ); }

// one could also define templated forwaring ctors to init
// in case derived wants to take extra params
};

// specific hierarchy

class Base_ {
protected:
Base_();
void init( int );
};

typedef wrapper<Base_> Base;

class Derived_ : public Base_ {
protected:
Derived_();
void init( int i ) {
Base_::init( i );
// ...
}
};

typedef wrapper<Derived_> Derived;

// usage

void user()
{
Derived d;
// call Base_ ctor
// call Derived_ ctor
// call wrapper ctor, which calls Derived_::init
}

Unfortunately, this doesn't catch some errors. For example, it doesn't
catch a derived class deriving from the wrapper typedef (i.e. Base).

// allowed
class BadDerived_ : public Base { // oops, used Base instead of Base_
// ...
};

This could be solved by making the wrapper class "final", using the
virtual base class idiom:

template<class T>
class final_base {
private:
final_base() { }
friend class T;
};

template<class T>
class wrapper : public T, private virtual final_base<wrapper<T> > {
// ...
// wrapper ctor can call final_base ctor, but not derived classes
};

It also allows a bad derived class to make its ctor public, thus allowing
users to instantiate it.

class BadDerived_ : public Base_ {
public:
BadDerived_(); // oops, public
};

void bad_user() {
BadDerived_ bad; // allowed, but init is never called
}

This could be solved by adding an abstract base class to Base_, whose pure
virtual function is only defined in wrapper:

template<class T>
class wrapper : public T {
public:
wrapper( int i ) { T::init( i ); }

// one could also define templated forwaring ctors to init
// in case derived wants to take extra params
private:
virtual void defined_by_wrapper() { }
};

class wrapped_base {
private:
virtual void defined_by_wrapper() = 0;

// I hope I got this syntax correct (my compiler doesn't support
// templated friends)
template<class T>
friend class wrapper<T>;
};

// specific hierarchy

class Base_ : protected wrapped_base {
protected:
Base_();
void init( int );
};

What a mess.

This wrapper concept can also be used to call a "virtual" cleanup function
in the derived class.

template<class T>
class wrapper : public T {
public:
~wrapper() { T::cleanup(); }
};

All of the different issues presented above are orthogonal, and can be
combined in any way (i.e. init and/or cleanup, with or without either the
"final" base class for wrapper and the abstract base class).

> And that's the exact problem addressed by the use of Init-style
> funtions in MFC and other application frameworks.

But this "solution" revokes many of the safety guarantees that
constructors provide.

[snip]


> There are some other possible solutions (e.g. passing a parts-
> constructor object as a parameter to the constructor), including the
> option of using Java, where method calls from a constructor are
> virtual calls (can lead to subtle errors but is very practical).
>
> It might be interesting to think about possible solutions in
> terms of exception safety -- I haven't really done that.

As long as the functions are exception-safe (whatever definition you use),
the result from the wrapper above has the same guarantee. If an exception
occurs in the init function, then the base objects are destroyed (i.e.
everything but the wrapper).

[snip]

0 new messages