C++ exception handling is defective

A

Alf P. Steinbach

* Zorro:
Is it really due to the significant
efficiency that C++ provides, that there is no resumption, for
instance. Or perhaps it has something to do with a form of
implementation that has been accepted as standard?

Rather it's about safety. Microsoft wanted resumption, but didn't
manage to convince any others (IIRC). Mostly the idea of correcting and
resuming at the C++ or machine code instruction level is just Wrong;
however, it is a valid option at the block or function level, like the
exception handling in Eiffel, a retry of some higher level instead of
resumption. To emulate the Eiffel exception handling in C++ you can use
the template pattern (which has nothing to do with C++ templates, look
it up if you haven't heard about it). It would of course be more
practical & useful if C++ had closures...
 
Z

Zorro

Alf said:
* Zorro:

Rather it's about safety. Microsoft wanted resumption, but didn't
manage to convince any others (IIRC). Mostly the idea of correcting and
resuming at the C++ or machine code instruction level is just Wrong;
however, it is a valid option at the block or function level, like the
exception handling in Eiffel, a retry of some higher level instead of
resumption. To emulate the Eiffel exception handling in C++ you can use
the template pattern (which has nothing to do with C++ templates, look
it up if you haven't heard about it). It would of course be more
practical & useful if C++ had closures...

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Thank you for your comment. If I go over everything you have packed in
one paragraph, this will go on forever. Just one point: MS has hardly
cared about convincing others to do what it wants to do. Otherwise it
would comply with the standard.

I am not, in any way, trying to extend the points you have made to a
lengthy discussion. Thanks again.

Regards.
 
I

Ian Collins

Zorro said:
I believe, in this one case, we are simply talking about the usefulness
of the C++ exception model. Is it really due to the significant
efficiency that C++ provides, that there is no resumption, for
instance. Or perhaps it has something to do with a form of
implementation that has been accepted as standard?
The C++ exception model is designed for C++ generated exceptions, not
for asynchronous events generated form outside.
No language can ever be completely safe. The intent is to move towards
a safer language, just as the introduction of "class" made a better C.
So now, all I am saying about C++ exception is that, it was not
well-thought. There is no need to change to a different language. The
standard can take a closer look and re-evaluate the benefits of the
current model. At least we can point out to other ideas by citing other
languages. One can close his ears to any argument by continually
bringing up "overhead". So be it.
Overhead isn't the point at issue, differentiating between exceptions
thrown by the application and asynchronous events generated by the
operating environment is.
 
?

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

Zorro said:
I believe, in this one case, we are simply talking about the usefulness
of the C++ exception model. Is it really due to the significant
efficiency that C++ provides, that there is no resumption, for
instance. Or perhaps it has something to do with a form of
implementation that has been accepted as standard?

You can read "The Design and Evolution of C++". It explains the rationale of
a bunch of design decisions.
 
Z

Zorro

Ian said:
The C++ exception model is designed for C++ generated exceptions, not
for asynchronous events generated form outside.
Overhead isn't the point at issue, differentiating between exceptions
thrown by the application and asynchronous events generated by the
operating environment is.

Thanks and regards.
 
Z

Zorro

Simon said:
Exceptions are raised *by throwing them.* In C++, that's what raising
an exception is. You can't have a raised exception that isn't thrown,
because you can't have a /thrown/ exception that isn't thrown.

Mr. Best. I accidently went to the Z++ page in Wikipedia and saw your
interference. So I decided to reply to your comment, even though I wish
this thread to end immediately.

When you run the example that Ian did on Linux, you get "Arithmetic
Exception" and a crash. That means an exception was raised at a "level"
outside of scope of C++. Read my statement, and your comment to it
again to see if you are making any sense.

With all the respect, I am not interested in persuing this matter.
Regards.
 
?

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

What do you mean by "C++ does not impose safety on the programmer".
So, how come you cannot call a private method? It is the purpose of a
modern language to help with the safety.

Then you say "C++ provides tools ....". Does C not do that already? So
why use C++?


C++ does not impose safety on the programmer by not requiring that the
safety-features are used, you can write very unsafe code in C++ if you
choose to. But C++ does provide features (such as private) that enables
the user to write safe code (these kind of things are the tools Simon
was referring to).
 
A

Andre Kostur

Mr. Best. I accidently went to the Z++ page in Wikipedia and saw your
interference. So I decided to reply to your comment, even though I
wish this thread to end immediately.

When you run the example that Ian did on Linux, you get "Arithmetic
Exception" and a crash. That means an exception was raised at a
"level" outside of scope of C++. Read my statement, and your comment
to it again to see if you are making any sense.

However, you are confusing two different things. The "Arithmetic
Exception" is not an exception in the C++ sense of the word. If that error
message had read "Arithmetic FUBAR", would that help? Attempting to
"catch" this type of exception (directly) makes no sense at all.
With all the respect, I am not interested in persuing this matter.

Usenet threads take on a life of their own... regardless of the
originator's wishes.
 
G

Grizlyk

Simon said:
Interesting. Is that because, on your architecture, 0xFFFF is -0? (I'm
just guessing.)

No, "divide by zero" can occure not only while you are dividing by 0,
in many cases, when CPU can not store result, that even can take more
memory, than source and divider have together.

Unsufficient memory can happends, when we are dividing very big number
to very small number ( due to CPU hardware limitations ), when divider
less than 1 (for floationg point) and 0 is just a limit of all here, as
most small number. Mathematically, to store result from 0 we need
memory of unlimited size.
But we don't want the unnecessary overhead when there's no possibility
of divide-by-zero. C++ leaves it to the programmer to decide whether or
not checks are needed, rather than imposing such checks.

1. "Divide by zero" in most cases is error, for result is not the same,
as expect programmer. If it does not matter to him to get the concrete
result, why is he dividing? It is better to do addition - faster and
safe operation :)

2. We must report ALL errors to apropriate handler. Always. It is not
"overhead".

3. "Divide by zero" handled by CPU practicaly, already handled,
similarly "bad memory access". Do we throw in the cases or silently
call exit()?

Lets programmer make selection. To do it, standard must allow for
programmer to make selection between "throw" or "not throw".

Now C++ programmer can not throw easy, No one of way below is allowed
by standard:
a) check a std:: flag _divide_error
a/=b; if(std::_divide_error)throw bad_divide();
b) do automatic throw
try{ a/=b; }catch(...){}
and programmer can not do local selection of one of them.

4. Not only "Divide by zero", all processable harware errors must be
processed as point 3. I think.
Sorry, I just didn't understand that. "try to find a cause of it"? "if
we will not find any, we will not process the error also"?

Well,well:

try - verb, "to attempt to do any"
will try - future indefinite of try
to find - verb infinitive, "make a search of any"
will try to find - will try in order to find
a - "any kind of next noun"
cause - noun, "source of an action, root"
of - "cause of thunder" is "thunder cause"
it - you know what is "it"
and so on,

Ask if need more explanations :)
I didn't understand the second half of that. But as for "may be is not
a best solution, because most CPUs can handle the error", the problem is
that (as I understand it) the operating system deals with such things.

it is not easy for programmer, that OS will do anything with his error,
OS can not repair his program. Programmer want to restore after errors
and C++ exceptions one of the way to do it.
Different operating systems deal with such things in different ways.

Yes, and C++ exceptions is one of the way to hide the differences.
And, as I said above, such things are beyond the scope of the C++
specification.

No, the question of safe and predictable excution is not beyond scope
of the C++, but bridge between "throw" and "CPU" of course beyond scope
of the C++.
Such checking only needs to be done in those cases where undefined
behaviour would otherwise be possible, and would be unacceptable. Such
checks don't need to be done for those cases where such undefined
behaviour isn't able to occur anyway.

The "checks" already is being doing by CPU, you can not deny "checks"
and you must do not do something for "checks". If no errors accured -
no "checks" will happen.
As usual, C++ does not impose safety on the programmer; instead, it
provides tools to help the programmer write safe code.

In the case of "divide by zero" , we are speaking excactly about that
C++ does not provides tools to help the programmer write safe code. No
tools.
 
K

Kai-Uwe Bux

Grizlyk said:
Simon G Best wrote: [snip]
As usual, C++ does not impose safety on the programmer; instead, it
provides tools to help the programmer write safe code.

In the case of "divide by zero" , we are speaking excactly about that
C++ does not provides tools to help the programmer write safe code. No
tools.

That is not entirely correct: you can write a wrapper template for the
arithmetic types. I confine myself to a rough outline. Also, I will
acknowledge that it is impossible to make such wrappers behave exactly like
the built in types (e.g., with regard to automatic promotions and
conversions). However, in many cases, such a wrapper can be used. E.g., for
debugging pointers, I do use a smart pointer whose operator* method does
assert( underlying_pointer != 0 ).

#include <stdexcept>
#include <string>

struct divide_by_zero : public std::eek:ut_of_range {

divide_by_zero ( std::string const & msg )
: std::eek:ut_of_range( msg )
{}

}; // divide_by_zero

template < typename ArithmeticType >
struct throw_divide_by_zero {

typedef ArithmeticType base_type;

explicit
throw_divide_by_zero ( base_type x = 0 )
: data ( x )
{}

friend
throw_divide_by_zero operator+ ( throw_divide_by_zero lhs,
throw_divide_by_zero rhs ) {
return ( throw_divide_by_zero( lhs.data + rhs.data ) );
}

// many more friends
// ...

// and finally, division:

friend
throw_divide_by_zero operator/ ( throw_divide_by_zero lhs,
throw_divide_by_zero rhs ) {
if( rhs.data == 0 ) {
throw ( divide_by_zero( std::string( "division by 0" ) ) );
}
return ( throw_divide_by_zero( lhs.data + rhs.data ) );
}

private:

base_type data;

}; // throw_divide_by_zero

#include <iostream>

int main ( void ) {
try {
throw_divide_by_zero<int> a ( 0 );
throw_divide_by_zero<int> b ( 3 );
b / a;
}
catch ( divide_by_zero const & c ) {
std::cout << c.what() << '\n';
}
}


Best

Kai-Uwe Bux
 
Z

Zorro

Kai-Uwe Bux said:
Grizlyk said:
Simon G Best wrote: [snip]
As usual, C++ does not impose safety on the programmer; instead, it
provides tools to help the programmer write safe code.

In the case of "divide by zero" , we are speaking excactly about that
C++ does not provides tools to help the programmer write safe code. No
tools.

That is not entirely correct: you can write a wrapper template for the
arithmetic types. I confine myself to a rough outline. Also, I will
acknowledge that it is impossible to make such wrappers behave exactly like
the built in types (e.g., with regard to automatic promotions and
conversions). However, in many cases, such a wrapper can be used. E.g., for
debugging pointers, I do use a smart pointer whose operator* method does
assert( underlying_pointer != 0 ).

#include <stdexcept>
#include <string>

struct divide_by_zero : public std::eek:ut_of_range {

divide_by_zero ( std::string const & msg )
: std::eek:ut_of_range( msg )
{}

}; // divide_by_zero

template < typename ArithmeticType >
struct throw_divide_by_zero {

typedef ArithmeticType base_type;

explicit
throw_divide_by_zero ( base_type x = 0 )
: data ( x )
{}

friend
throw_divide_by_zero operator+ ( throw_divide_by_zero lhs,
throw_divide_by_zero rhs ) {
return ( throw_divide_by_zero( lhs.data + rhs.data ) );
}

// many more friends
// ...

// and finally, division:

friend
throw_divide_by_zero operator/ ( throw_divide_by_zero lhs,
throw_divide_by_zero rhs ) {
if( rhs.data == 0 ) {
throw ( divide_by_zero( std::string( "division by 0" ) ) );
}
return ( throw_divide_by_zero( lhs.data + rhs.data ) );
}

private:

base_type data;

}; // throw_divide_by_zero

#include <iostream>

int main ( void ) {
try {
throw_divide_by_zero<int> a ( 0 );
throw_divide_by_zero<int> b ( 3 );
b / a;
}
catch ( divide_by_zero const & c ) {
std::cout << c.what() << '\n';
}
}


Best

Kai-Uwe Bux

Kai-Uwe, this is nice. However, look at all the code you have written
for something so simple. And you also expect everyone to remember how
to do such things in all cases, possibly in complex situations?

I am not entering the discussion. I was just wondering if you thought
about your extensive creativity.

Regards.
 
Z

Zorro

Erik said:
C++ does not impose safety on the programmer by not requiring that the
safety-features are used, you can write very unsafe code in C++ if you
choose to. But C++ does provide features (such as private) that enables
the user to write safe code (these kind of things are the tools Simon
was referring to).

Erik, I am not sure where you disagree with me. I understand what Simon
means by tools. Then, I am saying that the use of those tools imposes
restrictions (if you will). A C++ programmer not using C++ features is
a human decision. The language cannot impose upon you to use it one way
or the other. Finally, I am saying that, C has sufficient features to
be used safely. The conclusion I am drawing is that, the reason for
something like private is so you can avoid mistakes in a large program.
I admit I made it very brief, but that is what I meant.

Regards.
 
Z

Zorro

Andre said:
However, you are confusing two different things. The "Arithmetic
Exception" is not an exception in the C++ sense of the word. If that error
message had read "Arithmetic FUBAR", would that help? Attempting to
"catch" this type of exception (directly) makes no sense at all.

I did not say "Arithmetic Exception" is any form of C++ exception.
Please note what I am saying.

Exceptions occur at various levels of a system (CPU, layers of OS).
Each layer, either can handle the problem, or passes it to the next
layer. Now, if you wish to handle "Arithmetic Exception" in C++, you
need to throw it within C++. But whether you do, or do not throw the
exception in C++, somewhere in the system the exception will be raised.
It is an exception, and is not a FUBAR. Just because it was not thrown
within a C++ program does not change it to a FUBAR.

Requiring to throw exceptions that are expected to be caught is a
decision. It is not related to making or not making sense.

Hope this helps.
Usenet threads take on a life of their own... regardless of the
originator's wishes.

OK Andre. I must agree with you in that. Whether I can keep up with it
remains to be seen. Sometimes an incorrect use of a word (as it can
happen at this time of night) starts a whole new spark.

Regards.
 
?

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

means by tools. Then, I am saying that the use of those tools imposes
restrictions (if you will). A C++ programmer not using C++ features is
a human decision. The language cannot impose upon you to use it one way
or the other. Finally, I am saying that, C has sufficient features to
be used safely. The conclusion I am drawing is that, the reason for
something like private is so you can avoid mistakes in a large program.
I admit I made it very brief, but that is what I meant.

I was only giving an answer to the question 'What do you mean by "C++
does not impose safety on the programmer"'.
 
?

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

No, "divide by zero" can occure not only while you are dividing by 0,
in many cases, when CPU can not store result, that even can take more
memory, than source and divider have together.

Unsufficient memory can happends, when we are dividing very big number
to very small number ( due to CPU hardware limitations ), when divider
less than 1 (for floationg point) and 0 is just a limit of all here, as
most small number. Mathematically, to store result from 0 we need
memory of unlimited size.

Correct me if I'm wrong but is not this caused by the way floating
point arithmetic is performed? I seem to recall that first the numbers
are modified so that they have the same exponent, and while doing this
I suspect that the mantissa can become so small that it is rounded to
0, in which case the division will be by 0.
 
G

Gavin Deane

Zorro said:
I did not say "Arithmetic Exception" is any form of C++ exception.
Please note what I am saying.

Exceptions occur at various levels of a system (CPU, layers of OS).
Each layer, either can handle the problem, or passes it to the next
layer. Now, if you wish to handle "Arithmetic Exception" in C++, you
need to throw it within C++. But whether you do, or do not throw the
exception in C++, somewhere in the system the exception will be raised.
It is an exception, and is not a FUBAR. Just because it was not thrown
within a C++ program does not change it to a FUBAR.

Yes, it does. It seems to me reading this thread that your entire
question is based on coincidence. CPU designers have the concept of
what happens in awkward situations like divide by zero, and C++
programmers have the concept of what happens in responce to a throw
statement, and while those two concepts are utterly unrelated, both
groups of people (CPU designers and C++ programmers) happen to have
chosen the word "exception" to describe their concept.

If CPU designers had instead chosen the word "FUBAR" for their concept,
and C++ programmers had chosen the word "tadpole" for theirs, do you
really think your original question would have arisen?

Gavin Deane
 
G

Grizlyk

Kai-Uwe Bux said:
That is not entirely correct: you can write a wrapper template for the
arithmetic types.

No, we can not write wrapper, because "divide by zero" error rising in
CPU, out of C++ scope.

1. C++ compiler can install low-level error handler, that will silently
ignore the error, but standard _do not require_ it.
2. C++ compiler can install low-level error handler, that will throw
(user can not generate throw from low-level error handler), but
standard _do not require_ it.
3. C++ compiler can install low-level error handler, that will set
errno (user sometimes can do it from low-level error handler), but
standard _do not require_ it.

In theory, compiler can generate exception even when overflow has
occured while addition operation. Let it is discussable.

But in the case of "divide by zero" very often is only way to handle
the error is terminating program, due to hardware unmaskable CPU rised
signal, not program rised. You can not write low-level handler for
concrete target system in order to continue execution.

So i think C++ standart at least must garantee, that no differences
will be for overflow at addition or divide operations (for example,
silently ignore errors or ignore & setup error flag).
 
G

Gavin Deane

Grizlyk said:
No, we can not write wrapper, because "divide by zero" error rising in
CPU, out of C++ scope.

Have you missed the point? In C++, the built in arithmetic types
provide no protection against divide-by-zero. If the code calls for
divide-by-zero, it will compile. What then happens at run-time is not
defined by the C++ stadard. But you snipped Kai-Uwe's code which showed
a wrapper template that included the following overloaded operator:

<quote>
friend
throw_divide_by_zero operator/ ( throw_divide_by_zero lhs,
throw_divide_by_zero rhs ) {
if( rhs.data == 0 ) {
throw ( divide_by_zero( std::string( "division by 0" ) ) );
}
return ( throw_divide_by_zero( lhs.data + rhs.data ) );
}
</quote>

I think the last line should actually have been
return ( throw_divide_by_zero( lhs.data / rhs.data ) );
Maybe a copy and paste error?

Anyway, using that template instead of built in arithmentic types, the
programmer is protected against divide-by-zero. If you try and divide
by zero, before you get to any use of the / operator on built in types,
the overloaded operator/ in Kai-Uwe's class detects the situation and
throws a C++ exception *INSTEAD OF* attempting to divide a built in
arithmetic type by zero.

Gavin Deane
 

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