C++ exception handling is defective

R

Rolf Magnus

Zorro said:
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

None of the C++ examples in the PDF linked from there is actually using
exception handling. They all invoke undefined behavior. They don't show a
defect in C++'s exception handling, but rather a lack of the author's
understanding of C++.

BTW: Those examples are also using the <iostream.h> header that has been
outdated in C++ for about 10 years now and has actually never even been
part of the C++ Standard.
 
I

Ian Collins

Rolf said:
Zorro wrote:



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

None of the C++ examples in the PDF linked from there is actually using
exception handling. They all invoke undefined behavior. They don't show a
defect in C++'s exception handling, but rather a lack of the author's
understanding of C++.
Agreed, I wonder which compiler and environment produced the results
claimed.
BTW: Those examples are also using the <iostream.h> header that has been
outdated in C++ for about 10 years now and has actually never even been
part of the C++ Standard.
Not to mention idiomatic C (void).
 
D

David W

Zorro said:
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-defecti
ve.html

The fact that the code samples use <iostream.h> and not <iostream> suggests that
a very old compiler was used to demonstrate the supposed exception bugs. If the
code is updated for <iostream> and the divisions by zero are replaced by throws
(/0 causes undefined behaviour) and a more recent compiler is used, I think
you'll find that the destructor is indeed called when the stack is unwound. It
looks like a defective compiler rather than a defective language.

DW
 
?

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

David said:
The fact that the code samples use <iostream.h> and not <iostream>
suggests that a very old compiler was used to demonstrate the supposed
exception bugs. If the code is updated for <iostream> and the divisions by
zero are replaced by throws (/0 causes undefined behaviour) and a more
recent compiler is used, I think you'll find that the destructor is indeed

Better say a correct compiler. I have seen Borland Kilyx failing to call
destructors in cases similar to that (but correct). I even asked here if
the code has some error I failed to see, if I remember well. And Borland
acknowledged the bug, but failed to provide a solution (at least during the
time that I have the patience to keep checking).
 
Z

Zorro

Zorro said:
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

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

This is a common reply to several comments, respectfully.

1. The use of one or another header file is cosmetic for this example.
2. When you say a correct compiler will do better, please specify the
compiler.
3. Division by 0 happens at system level and is not necessary to turn
it into a throw.
4. For this example, the compiler is Microsoft Visual C++ version 6.
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.
6. If you are not familiar with the concept, and do not see the real
issue, avoid revealing it.

With respect, the best way I could present my response.

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
 
I

Ian Collins

Zorro said:
This is a common reply to several comments, respectfully.

1. The use of one or another header file is cosmetic for this example.

It may or may not be cosmetic, but is asking for flak.
2. When you say a correct compiler will do better, please specify the
compiler.

Changing you example to standard C++:

#include <iostream>

struct simple
{
int s;
simple() { s = 5;}
~simple(void) {std::cout << "Destructor ..." << std::endl;}
};

int main(){
int i = 1, j = 0;
try {
simple spl;
throw 0;
}
catch(...) {
std::cout << "Caught it" << std::endl;
}
std::cout << "Finishing" << std::endl;
return 0;
}

any compiler should give the correct result. If not, it is broken.
3. Division by 0 happens at system level and is not necessary to turn
it into a throw.

Then why use it in your examples?
4. For this example, the compiler is Microsoft Visual C++ version 6.

Old and not very compliant.
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.

Show me one that exibits it.
6. If you are not familiar with the concept, and do not see the real
issue, avoid revealing it.
Which concept?
 
K

Kai-Uwe Bux

Zorro said:
This is a common reply to several comments, respectfully.

1. The use of one or another header file is cosmetic for this example.
2. When you say a correct compiler will do better, please specify the
compiler.

Your compiler did just fine. The output you present is a perfectly valid
output for the program.
3. Division by 0 happens at system level and is not necessary to turn
it into a throw.

The problem with the division by 0 is that it simply is undefined behavior
according to the standard. The language specification places no
requirements upon the observable behavior of any program that executed a
division by 0. Therefore, the output you attribute to incorrect handling of
exceptions is a perfectly valid output for your program (and so would be
any other).

What puts your program into an inconsistent state is not some exception but
the division by 0, which is UB.
4. For this example, the compiler is Microsoft Visual C++ version 6.
5. The defect is in the technique used. Of course it can be corrected.

The defect lies in the undefined behavior.
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.

Your program does not illustrate problem with stack unravelling. It
illustrates that all bets are off once you have undefined behavior.
6. If you are not familiar with the concept, and do not see the real
issue, avoid revealing it.

Huh?


Best

Kai-Uwe Bux
 
Z

Zorro

Kai-Uwe Bux said:
Your compiler did just fine. The output you present is a perfectly valid
output for the program.


The problem with the division by 0 is that it simply is undefined behavior
according to the standard. The language specification places no
requirements upon the observable behavior of any program that executed a
division by 0. Therefore, the output you attribute to incorrect handling of
exceptions is a perfectly valid output for your program (and so would be
any other).

What puts your program into an inconsistent state is not some exception but
the division by 0, which is UB.


The defect lies in the undefined behavior.


Your program does not illustrate problem with stack unravelling. It
illustrates that all bets are off once you have undefined behavior.


Huh?


Best

Kai-Uwe Bux

Kai-Uwe, division by 0 is undefined just as trying to deref a null
pointer. These are exceptional cases that can happen. Simply calling
them (undefined behavior) does not help in anyway. Are you saying that,
if one of these cases, or many similar ones, happens the program should
necessarily terminate? Then what is the point of having an exception
mechanism?
Yes, the output is correct in accordance to C++ standard. But, is it
acceptable just because that is how it is?

Thanks for your contribution as it brought one more point to attention.

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
 
Z

Zorro

Ian said:
It may or may not be cosmetic, but is asking for flak.


Changing you example to standard C++:

#include <iostream>

struct simple
{
int s;
simple() { s = 5;}
~simple(void) {std::cout << "Destructor ..." << std::endl;}
};

int main(){
int i = 1, j = 0;
try {
simple spl;
throw 0;
}
catch(...) {
std::cout << "Caught it" << std::endl;
}
std::cout << "Finishing" << std::endl;
return 0;
}

any compiler should give the correct result. If not, it is broken.


Then why use it in your examples?


Old and not very compliant.


Show me one that exibits it.

Which concept?

Ian, the stream library for output is not the issue. I only tried to
write less because that was not the point. However, you are replacing
division by 0 with a throw. What the example is speaking of is about
exceptions that can happen without you throwing them. Although I am not
saying that your version does not fail.

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. 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. Indeed, in
this example the compiler does report. The problem is the way the
run-time library is handling the recovery, known as stack unraveling.
One needs to do a few more things as housekeeping besides popping the
stack, and passing the exception to the next call.

Thanks for your comment.

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
 
K

Kai-Uwe Bux

Zorro said:
Kai-Uwe Bux said:
Zorro said:
Zorro wrote:
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
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
[snip]
2. When you say a correct compiler will do better, please specify the
compiler.

Your compiler did just fine. The output you present is a perfectly valid
output for the program.
3. Division by 0 happens at system level and is not necessary to turn
it into a throw.

The problem with the division by 0 is that it simply is undefined
behavior according to the standard. The language specification places no
requirements upon the observable behavior of any program that executed a
division by 0. Therefore, the output you attribute to incorrect handling
of exceptions is a perfectly valid output for your program (and so would
be any other).

What puts your program into an inconsistent state is not some exception
but the division by 0, which is UB.
[snip]

Kai-Uwe, division by 0 is undefined just as trying to deref a null
pointer. These are exceptional cases that can happen. Simply calling
them (undefined behavior) does not help in anyway. Are you saying that,
if one of these cases, or many similar ones, happens the program should
necessarily terminate? Then what is the point of having an exception
mechanism?

The try-throw-catch mechanism is not meant to handle those cases. I have
outlined the benefit of try-throw-catch in the following post:

http://groups.google.com/group/comp...=Bux+exception+assert&rnum=1#e308bdf6553abbc6

Yes, the output is correct in accordance to C++ standard. But, is it
acceptable just because that is how it is?

To me, the current state of affairs with regard to dereferencing a null
pointer or dividing by 0 is acceptable. C++ has basic types that make very
little guarantees. If you want stronger predictability, you should define
simple wrapper classes that provide the additional guarantees. For
instance, I never use pointers directly. Instead I have a simple pointer
wrapper class. It defines operator* so that an assert() is made to catch
dereferencing 0. I also have a wrapper template for the floating point
types (it allows me to overload operator== to do sloppy comparison). This
also catches division by 0.

Also note that this solution is more flexible: I use assert() to catch those
instances, since I consider them bugs. Others may feel that division by 0
is not so much a bug of the program but something due to faulty input. From
that point of view throwing an exception may be more appropriate. I would
not want the language specification make that decision for me.


Best

Kai-Uwe Bux
 
B

benben

Zorro said:
Ian, the stream library for output is not the issue. I only tried to
write less because that was not the point. However, you are replacing
division by 0 with a throw. What the example is speaking of is about
exceptions that can happen without you throwing them. Although I am not
saying that your version does not fail.

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. 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. Indeed, in
this example the compiler does report. The problem is the way the
run-time library is handling the recovery, known as stack unraveling.
One needs to do a few more things as housekeeping besides popping the
stack, and passing the exception to the next call.

So you are actually talking about two separate issues:

1) C++ doesn't throw properly upon divide-by-zero
2) Objects out of scope are not properly destroyed upon divide-by-zero

C++ exception is meant for reporting exceptional circumstances, but for
detecting it. So it is your responsibility to detect and throw properly.

C++ could have properly throw an exception when you try to divide an int
by zero. However, this will put up a significant performance penalty
that not everyone is comfortable with. Alternatively, you can simple
test the divident and throw your own exception:

if (j == 0)
throw divide_by_zero();

i /= j;

This is easy, and you can always create your own type that acts in every
way like an int but throws properly when divided by a zero. So you have
got choices to choose from.

class safe_int
{
int repr;

// ...
};

safe_int operator/=(safe_int lhs, int rhs)
{
if (rhs == 0) throw divide_by_zero();
lhs.repr /= rhs;
return lhs;
}


Once 1) is solved (by using a test or using your own int-like class),
issue 2) is easy. Since the division is no longer a UB, objects out of
scope are guaranteed to be destroyed properly.

Oh btw, please please stop using old compilers!

Regards,
Ben
 
J

jalina

Zorro a écrit :
Yes, the output is correct in accordance to C++ standard. But, is it
acceptable just because that is how it is?

Well this is how C++ has been defined by "philosophy" (no overhead in
generated code, i.e. no test when dividing to check for 0 division so no
exception raised)

I suggest you to look at the ADA language if you want an exception to be
raised when dividing by 0 (I mean without having to throw it yourself)
or when accessing an invalid index in a array or even when there is an
overflow.

J.
 
I

Ian Collins

First, please reply inline and trim signatures.
Ian, the stream library for output is not the issue. I only tried to
write less because that was not the point. However, you are replacing
division by 0 with a throw. What the example is speaking of is about
exceptions that can happen without you throwing them. Although I am not
saying that your version does not fail.
Then you aren't talking about exceptions in the C++ sense of the term.
You are talking about events that are beyond the standard.
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. 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. Indeed, in
this example the compiler does report. The problem is the way the
run-time library is handling the recovery, known as stack unraveling.
One needs to do a few more things as housekeeping besides popping the
stack, and passing the exception to the next call.
It any not be possible for this to happen on many platforms without
checking each and every memory access or operation, which would be
intolerable.
 
D

Dizzy

Zorro said:
Kai-Uwe, division by 0 is undefined just as trying to deref a null
pointer. These are exceptional cases that can happen. Simply calling
them (undefined behavior) does not help in anyway.

It helps in some (other than yours) situations. You see, one design goals of
C++ is to not pay for something you don't use/need. While _you_ would like
the language to check for division by 0 and throw some exception in that
case, others (ME for example) would NOT like it as it will make every
devision in my programs slower for a corner case that I know I never reach.
Thus infriging on my right to have a faster program and on the principle do
not pay a price for what you don't need.

However, at the same time, for people with _your_ needs overloading
operator/ and company (or other wrapper solution) can be implemented:
1. easily
2. it does what you need without making the rest (that don't need it) pay
for what you need

So what we have now with the current C++ is a win/win situation. Why fix it
if it works ?
Are you saying that,
if one of these cases, or many similar ones, happens the program should
necessarily terminate?

No, UB doesn't mean terminate, it's just undefined what it happens. That is
you shouldn't do it and if you are unsure (or you KNOW that you might be
doing it) add checks to avoid it. But for the people (and cases) that are
100% sure it will never happen there are no checks needed thus we have less
overhead when we don't need it.
Then what is the point of having an exception
mechanism?

To offer syntax and semantics that make it easy to decouple the place of
error detection with the place of error handling. Also (because it supports
ordinary types and selects the specific type throwed) to make it flexible
in reporting information about the error. But certainly I don't think it
was ment to be used to signal some rare conditions by adding checks where
not all need them and where they can be added easily by those who need
them.

Take another case for example, why doesn't C++ arrays subscript operator
(which are the C arrays actually) and even the standard library containers
(such as vector::eek:perator[]) do not check for range and throw something if
the range is out of bounds ? It is EXACTLY the same reason (however, notice
that std::vector provides .at() that does check I think so you see, you
have a choice).
Yes, the output is correct in accordance to C++ standard. But, is it
acceptable just because that is how it is?

For the reasons explained above (which apply to dozens other such decisions
in the C++ standar) YES YES YES! :)
 
R

Rolf Magnus

Zorro said:
This is a common reply to several comments, respectfully.

1. The use of one or another header file is cosmetic for this example.

If you are talking about what you consider to be defects in the Standard C++
language, at least you have to use that. Otherwise, your credibility is
gone at that point already.
2. When you say a correct compiler will do better, please specify the
compiler.

The compiler's output _is_ correct, just as any other C++ compiler's is.
3. Division by 0 happens at system level and is not necessary to turn
it into a throw.

Exactly. Anything can happen. You just used one specific compiler that
happens to throw an exception in this case and seems to have a bug. That's
not a proper basis to conclude that there must be a flaw in C++'s exception
handling. On my compiler, an exception is never even thrown by that
program, and C++ doesn't require or even suggest it. So your code basically
has nothing to do with C++ exceptions. It only has to with the behavior of
one specific compiler.
4. For this example, the compiler is Microsoft Visual C++ version 6.

Wow, that's an old one. It's not really known for its standard compliance,
but who's to blame it? AFAIK, that compiler is even older than the
standard. It's ancient.
5. The defect is in the technique used. Of course it can be corrected.

It is in the implementation of the compiler and your assumptions, not in the
C++ standard.
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.
6. If you are not familiar with the concept, and do not see the real
issue, avoid revealing it.

Familiar with what concept? You don't seem familiar with the concept of
undefined behavior.
 
P

peter koch

Zorro skrev:
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

You use a very old (from before the C-standard) C++ compiler, write a
program that includes non-standard headers and has undefined behavior
and complain that the output is not what you expected.
This makes the statement "an engineer aware of defects can avoid
hard-to-find bugs" funny in a very sad way.
I hope you can do better than that!

/Peter
 
R

Roland Pibinger

division by 0 is undefined just as trying to deref a null
pointer. These are exceptional cases that can happen. Simply calling
them (undefined behavior) does not help in anyway.

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"
 
?

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

This is a common reply to several comments, respectfully.

3. Division by 0 happens at system level and is not necessary to turn
it into a throw.

As others have pointed out, this is not an exception. However that does
not mean that there does not exist ways to handle it, on a
POSIX-compatible system you can install a signal-handler for the SIGFPE
(floating-point exception signal), though you always run the risk of
getting a signal for some other kind of exception*, though they are not
that common (they usually terminate the app).

* When I say exception I don't mean a C++ exception but rather a CPU
trap exception.
 
?

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

Zorro said:
1. The use of one or another header file is cosmetic for this example.

Is not. In several compilers mixing standard headers and pre-standard ones
is not supported.
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 problem is in your code, not in the compiler. Dividing by zero you
invoke undefined behaviour. What your compiler does in that situation, you
must check what your compiler documentation does, and blame his authors if
what you see contradicts his documentation. From the standard point of
view, no matter what it does, is undefined.
 

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top