C++ exception handling is defective

?

=?ISO-8859-15?Q?Juli=E1n?= Albo

Zorro said:
The concept I am referring to is exceptional events that can happen as
your program executes. For instance, the operating system does not die
on a division by 0. It simply aborts your program.

What the operating system does has nothing to do with the issue.
Thus, the point is that, a C++ compiler's run-time library must sense and
report such events to your program so you can do something about them.

The point is that all compilers must do what you want just because you like
it that way? If not, there are tons of books and other resources that
explains the reasons for the design decisions of the C++ language.
 
S

Simon G Best

Hello!
The simplicity of stack unraveling of C++ is not without defective
consequences. The following article points to C++ examples showing the
defects. An engineer aware of defects can avoid hard-to-find bugs.

http://distributed-software.blogspot.com/2007/01/c-exception-handling-is-defective.html

I've read this thread, as well as your blog article and referenced
document (which seems to be plugging your Z++), and it seems you're
misunderstanding /why/ the behaviour of division by zero is undefined.

My guess is that the rationale behind it being undefined is something
like the following.

When a processor encounters division by zero, it may well throw an
exception (*not* a C++ exception), complain in some other way, ignore
it, or do something else. What it does depends on the processor. But,
whatever it is, it's beyond the scope of the C++ standard, because the
C++ standard is not there to define such processor behaviour.

If a processor somehow reports division by zero to, say, an operating
system, then that operating system may well respond by sending a signal
to the relevant program, terminating execution of that program, ignoring
it, or doing something else. What it does depends on the operating
system. But, whatever it is, it's beyond the scope of the C++ standard,
because the C++ standard is not there to define such operating system
behaviour.

If the C++ standard was to specify behaviour for division by zero, then
since the processor's behaviour and operating system's behaviour are
beyond the scope of the C++ standard, such specified behaviour may well
have to be implemented in such a way as to prevent the division by zero
from actually occurring in the first place. That would impose often
unnecessary and unwanted overheads. C++ just isn't that sort of language.

Indeed, there are a great many unsafe things that C++ allows programmers
to do. There are unsafe things that C++ allows programmers to do
without even throwing C++ exceptions when they're done. Instead, C++
provides mechanisms for use /when the programmer wants or needs them./
It's the programmer's responsibility to write safe code, /not/ C++
itself. So, C++ provides exception mechanisms /without/ imposing them
on the programmer. If the programmer needs well-behaved exception
handling, the programmer can then use the exception mechanisms provided
accordingly. That's the kind of language that C++ is.

So, if, when doing a division, there's the real (though hopefully
exceptional) possibility of it being a division by zero, the thing to do
is to do that division within a try block, checking for division by zero
before doing the division, and throwing an exception if it would be
division by zero. This could, of course, be wrapped up in your own
number class. No need to change the language itself.

:)
 
I

IR

Zorro said:
5. The defect is in the technique used. Of course it can be
corrected. Show me a compiler that has in fact corrected this
problem. The point is that, it takes a great deal beyond plain
stack unraveling to correct this problem.

Basically, you're just saying that RAII in C++ is broken.
This is a tough statement, you're gonna have hard time proving it...

Incidentally, as others already mentionned it, you should start using
a recent compiler.

Cheers,
 
Z

Zorro

Simon said:
Hello!


I've read this thread, as well as your blog article and referenced
document (which seems to be plugging your Z++), and it seems you're
misunderstanding /why/ the behaviour of division by zero is undefined.

My guess is that the rationale behind it being undefined is something
like the following.

When a processor encounters division by zero, it may well throw an
exception (*not* a C++ exception), complain in some other way, ignore
it, or do something else. What it does depends on the processor. But,
whatever it is, it's beyond the scope of the C++ standard, because the
C++ standard is not there to define such processor behaviour.

If a processor somehow reports division by zero to, say, an operating
system, then that operating system may well respond by sending a signal
to the relevant program, terminating execution of that program, ignoring
it, or doing something else. What it does depends on the operating
system. But, whatever it is, it's beyond the scope of the C++ standard,
because the C++ standard is not there to define such operating system
behaviour.

If the C++ standard was to specify behaviour for division by zero, then
since the processor's behaviour and operating system's behaviour are
beyond the scope of the C++ standard, such specified behaviour may well
have to be implemented in such a way as to prevent the division by zero
from actually occurring in the first place. That would impose often
unnecessary and unwanted overheads. C++ just isn't that sort of language.

Indeed, there are a great many unsafe things that C++ allows programmers
to do. There are unsafe things that C++ allows programmers to do
without even throwing C++ exceptions when they're done. Instead, C++
provides mechanisms for use /when the programmer wants or needs them./
It's the programmer's responsibility to write safe code, /not/ C++
itself. So, C++ provides exception mechanisms /without/ imposing them
on the programmer. If the programmer needs well-behaved exception
handling, the programmer can then use the exception mechanisms provided
accordingly. That's the kind of language that C++ is.

So, if, when doing a division, there's the real (though hopefully
exceptional) possibility of it being a division by zero, the thing to do
is to do that division within a try block, checking for division by zero
before doing the division, and throwing an exception if it would be
division by zero. This could, of course, be wrapped up in your own
number class. No need to change the language itself.

:)

There are a number of great comments, but this one sums it up rather
thoroughly.

What seems to be happening is this. When you use "throw", the compiler
generates code to destroy objects in relevant scopes, should an
exception happen. Otherwise, the compiler has no clue where to put such
code. Thus, even though exceptions (such as div by zero) are caught,
the code to call destructors (at end of try, or function body) is
simply skipped.

What that all means, again is this. There is code to destroy objects in
nested scopes. However, unless the "throw" is used, the compiler will
be caught unguarded, and code for destroying objects will be skipped.

That is fair as a standard. The compiler is just doing what the
standard requires, even VC++ 6 does it right. It is also fair to expect
the engineer to check for every possible case of exception and put a
"throw" for it.

I have no further argument on this issue. Just a comment, and nothing
else.

Given that all operating systems report errors (exceptions) rather than
crashing, and in fact the program can even check the error code, etc.
and continue to execute (perhaps taking the necessary actions), how
hard is it for the standard to have a list of common exceptions that
may happen unexpectedly?
The answer is that, the mechanism used in C++ still will need the
"throw" in order to generate code for NOT skipping the destruction of
objects.
I hope this helps, but it is not intended to start another wave. I
agree with all of you.

Thanks for your comments. Have a great day.

Regards,
(e-mail address removed)
http://www.zhmicro.com
http://distributed-software.blogspot.com
http://groups-beta.google.com/group/computerlangZ?lnk=la&hl=en
 
?

=?ISO-8859-15?Q?Juli=E1n?= Albo

Zorro said:
Given that all operating systems report errors (exceptions) rather than
crashing, and in fact the program can even check the error code, etc.

Invalid reasoning, because the premise is false. Unless you use a definition
of "Operating system" that excludes, for example, ms-dos.
 
I

Ian Collins

Zorro said:
Given that all operating systems report errors (exceptions) rather than
crashing, and in fact the program can even check the error code, etc.
and continue to execute (perhaps taking the necessary actions), how
hard is it for the standard to have a list of common exceptions that
may happen unexpectedly?

Not all implementations are hosted. Even those that are (UNIX like
systems for example) may signal exception asynchronously. This makes it
extremely difficult to handle the event in the same context as the
running application in order to convert the system event into an
application one.
 
P

Pete Becker

Zorro said:
What seems to be happening is this. When you use "throw", the compiler
generates code to destroy objects in relevant scopes, should an
exception happen. Otherwise, the compiler has no clue where to put such
code. Thus, even though exceptions (such as div by zero) are caught,
the code to call destructors (at end of try, or function body) is
simply skipped.

No. If div by zero throws an exception that's implementation-specific
behavior. If your implementation does something silly like not run
destructors as part of its implemenation-specific behavior, that's a
problem with your implementation, not with the language.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
D

dasjotre

Zorro said:
how
hard is it for the standard to have a list of common exceptions that
may happen unexpectedly?

I suspect much harder than it is for you to write

#include <eh.h>

void my_handler(UINT, struct _EXCEPTION_POINTERS* pex)
{
if(pex->ExceptionRecord->ExceptionCode ==
EXCEPTION_INT_DIVIDE_BY_ZERO)
throw divide_by_zero();
}

// main
_set_se_translator(&my_handler);
 
P

Pete Becker

Zorro said:
how
hard is it for the standard to have a list of common exceptions that
may happen unexpectedly?

The standard has a list of all standard exceptions. Keep in mind that
hardware "exceptions" are not the same as C++ exceptions, although they
use the same name. C++ doesn't say much about hardware exceptions,
because they're too idiosyncratic. If you don't throw it, it's not
covered by the standard.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
M

Mathias Gaunard

Zorro said:
What seems to be happening is this. When you use "throw", the compiler
generates code to destroy objects in relevant scopes, should an
exception happen. Otherwise, the compiler has no clue where to put such
code. Thus, even though exceptions (such as div by zero) are caught,
the code to call destructors (at end of try, or function body) is
simply skipped.

You're wrong.
`throw' simply throws exceptions.
If you don't use `throw', no exception is thrown. `throw' is the only
way to throw exceptions in C++.
It has been told to you more than ten times already, but division by
zero does *not* throw any exception.


Given that all operating systems report errors (exceptions) rather than
crashing

Not all do, on some machines couldn't even allow it.
 
O

Old Wolf

Roland said:
From K&R B9:
"The header <signal.h> provides facilities for handling exceptional
conditions that arise during execution, such as an inerrupt signal
from an external source or an error in execution.
...
SIGFPE arithmetic error, e.g., zero devide or overflow
...
SIGSEGV illegal storage access, e.g., access outside memory limits"

Not particularly useful to the case in hand, as undefined
behaviour is caused if the signal handler for either of those
signals is not present, or returns (ie. to have defined behaviour
you must have a signal handler which aborts the program).
 
Z

Zorro

Ian said:
Not all implementations are hosted. Even those that are (UNIX like
systems for example) may signal exception asynchronously. This makes it
extremely difficult to handle the event in the same context as the
running application in order to convert the system event into an
application one.

This is an interesting point. Indeed, there is something good in every
comment that helps design safer applications. Let me ask the question
from a different angle, though your comment does not focus on this
aspect of the question. Think of a complex program with a large number
of exceptions, and the possibility of missing something (to be done
later).

If my application did not throw an exception, and it was caused by
external events, why was it caught? I would be more than happy to see
my program crash so I could find my problem. On the other hand, if an
exceptional event can be caught by the runtime library, how come it
cannot clean up properly?

Please do not focus on div-by-zero. Think of some exceptional event,
call it unknown. So, either the catch() can trap unknown exceptions
caused by underlying system (thus, it must clean up correctly), or my
application must crash, as it would without the use of catch().

Is this a fair question?

Please, let us not blame a particular compiler implementation. This is
not specific to any compiler. Try it with your favorite compiler.

Regards,
(e-mail address removed)
http://www.zhmicro.com
http://distributed-software.blogspot.com
http://groups-beta.google.com/group/computerlangZ?lnk=la&hl=en
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

This is an interesting point. Indeed, there is something good in every
comment that helps design safer applications. Let me ask the question
from a different angle, though your comment does not focus on this
aspect of the question. Think of a complex program with a large number
of exceptions, and the possibility of missing something (to be done
later).

If my application did not throw an exception, and it was caused by
external events, why was it caught? I would be more than happy to see
my program crash so I could find my problem. On the other hand, if an
exceptional event can be caught by the runtime library, how come it
cannot clean up properly?

Easy, it was caught because your compiler vendor decided that they
wanted it that way, as others have pointed out earlier division by 0
causes undefined behaviour, which is just that: undefined. Meaning that
just because it was caught on your system there is no guarantee that it
will on someone else system, or your system with a small upgrade, or
whatever. Once a program has entered undefined behaviour anything is
possible, it might even continue and execute the rest of the program
correctly.
 
I

Ian Collins

Please trim signatures (your news reader should be set to do this for
you). You should also add a -- line before yours so news readers can
recognise it for what it is.
This is an interesting point. Indeed, there is something good in every
comment that helps design safer applications. Let me ask the question
from a different angle, though your comment does not focus on this
aspect of the question. Think of a complex program with a large number
of exceptions, and the possibility of missing something (to be done
later).

If my application did not throw an exception, and it was caused by
external events, why was it caught? I would be more than happy to see
my program crash so I could find my problem. On the other hand, if an
exceptional event can be caught by the runtime library, how come it
cannot clean up properly?
I think that is where you make your mistake, the event need not be
caught by the runtime library. It is more likely to be caught by the
operating system (if the environment is hosted) as a result of a trap
generated by the underlying hardware. The means of delivery is
dependant on the operating environment, not the running application
(which includes the C++ runtime).

The rules relating to the handling of these events are laid down by the
operating environment, not the C++ runtime. There may be some event
that can be handled by the runtime, but it is beyond the scope of the
C++ standard to mandate how these are handled for all environments.

As I said before, in a UNIX like environment, hardware exceptions are
handled through a form of software interrupt known as a signal. The
signal is processed in a different context from the application and
there isn't a means of communicating between the signal handler and the
application context. The same would apply to any system using
interrupts to handle exceptions raised by the processor.
 
P

Pete Becker

Zorro said:
Please, let us not blame a particular compiler implementation. This is
not specific to any compiler. Try it with your favorite compiler.

On the contrary: catching exceptions that were not thrown is not part of
standard C++. If your implementation does this, you need to ask your
compiler vendor for details and, perhaps, for an explanation of how to
use it.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
Z

Zorro

Pete said:
On the contrary: catching exceptions that were not thrown is not part of
standard C++. If your implementation does this, you need to ask your
compiler vendor for details and, perhaps, for an explanation of how to
use it.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)

OK. This is a direct answer, and I appreciate it. Let us be more
specific.

"Catching exceptions that were not thrown is not part of standard C++".

Does this mean:

1. Exception must not be caught (generally, the program will crash).
2. Whether or not catch it is vendor-specific (outside of standard,
hence arbitrary).
3. Program becomes undefined with unpredictable behavior (hardly
useful).

Regards,
(e-mail address removed)
http://www.zhmicro.com
http://distributed-software.blogspot.com
http://groups-beta.google.com/group/computerlangZ?lnk=la&hl=en
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

OK. This is a direct answer, and I appreciate it. Let us be more
specific.

"Catching exceptions that were not thrown is not part of standard C++".

Does this mean:

1. Exception must not be caught (generally, the program will crash).
2. Whether or not catch it is vendor-specific (outside of standard,
hence arbitrary).
3. Program becomes undefined with unpredictable behavior (hardly
useful).

It means that if you have code with a catch-block that gets executed
even though no exception has been thrown it is not standard compliant
behaviour, I would even go as far as saying that it is in direct
violation of the standard.

Just to be clear, that is not what is happening in your code, what
happens there is that you get undefined behavior. And when you get UB
anything is fair, so executing the catch-block is not exactly wrong.
 
P

Pete Becker

Zorro said:
OK. This is a direct answer, and I appreciate it. Let us be more
specific.

"Catching exceptions that were not thrown is not part of standard C++".

Does this mean:

1. Exception must not be caught (generally, the program will crash).
2. Whether or not catch it is vendor-specific (outside of standard,
hence arbitrary).
3. Program becomes undefined with unpredictable behavior (hardly
useful).

It means that the standard does not address it. Again: if your
implementation does this, you need to ask your compiler vendor for
details and, perhaps, for an explanation of how to use it.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
P

Pete Becker

Erik said:
It means that if you have code with a catch-block that gets executed
even though no exception has been thrown it is not standard compliant
behaviour, I would even go as far as saying that it is in direct
violation of the standard.

Just to be clear, that is not what is happening in your code, what
happens there is that you get undefined behavior. And when you get UB
anything is fair, so executing the catch-block is not exactly wrong.

If a program has undefined behavior under the standard, the standard
does not impose any requirements. In particular, the compiler is free to
generate code that executes catch clauses even when there is no
corresponding throw.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
S

Simon G Best

Zorro said:
OK. This is a direct answer, and I appreciate it. Let us be more
specific.

"Catching exceptions that were not thrown is not part of standard C++".

Does this mean:

1. Exception must not be caught (generally, the program will crash).

No. An exception that isn't thrown doesn't even exist. For an
exception to exist, it must be thrown. The throwing of an exception is
the creation of that exception.

Again, things that may be regarded as exceptions /outside/ of C++ /are
not/ C++ exceptions. A processor exception /is not an exception in
C++./ An exception thrown by an operating system /is not an exception
in C++./ The /only/ exceptions in C++ are exceptions thrown /within/ C++.

So, in a sense, yes: 'exceptions' that are not thrown /within/ C++
should not be caught /within/ C++. Except, of course, when it's a case
of undefined behaviour (such as with division by zero), in which case
anything's allowed to happen (including catching exceptions that weren't
thrown). That's what "undefined" means.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top