473,386 Members | 1,738 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

Exceptions vs Status codes


I'm involved in a new project and a new member on the team has voiced a
strong opinion that we should utilize exceptions.

The other members on the team indicate that they have either been burned
with unmaintainable code (an so are now not using exceptions). My
position is that "I can be convinced to use exceptions" and my
experience was that it let to code that was (much) more difficult to debug.

The team decided that we'd give exceptions a go. Yeah for progress !

I've been rattling my tiny brain on the issue and I have come to the
conclusion that exceptions are not as flexible as status codes.

a) For debugging purposes we want stack traces - but only for cases
where the exception is caught in a place we consider a "bad" thing happened.

For example, say we have a "file" object that fails to "open" the file.
In most cases, it's ok, we don't really care, it's really not
exceptional except if it's an "important" file. I really don't know if
it's important at the lower levels (where the exception is thrown).

I'm ok with psuedo stack trace e.g.
T func( T0 arg0, T1 arg1 ... )
try
{
... func magic ...
}
catch ( ... )
{
LOG( "func", arg0, arg1 ... ); //etc
throw; // rethrow
}
This does not look too bad but it's not foolproof because no-one knows
which of the arguments actually make sense to log at any point in time.
I suspect there are also optimizer hits because the values need to be
stashed away "in case" of exception while in regular code we would be
logging the arguments like:
if ( ! func( v1, v2, v3 ) )
{
LOG( WARNING, "func() Failed", v1, v2, v3 ... );
return falase;
}

And hence only if "WARNING" level logging is turned on do the values
need to get stashed.

b) It would really be nice to know what is going to catch an exception.
I know this is a bit of a wild thought but it would be nice if there
was a low-cost was of throwing context dependant exceptions. I suppose
we could do that using a TSS variable if we had co-operating
caller/thrower semantics.

I know this is a contentious topic, I really don't want to start another
exception war, but I'd really like to hear from people experienced in
using exceptions in big complex projects and the things to avoid and
techniques to use to get the best bang for sweat.

The usual rules are clear.
1. assert for logic errors - absolutely no exceptions used to detect
logic errors.
2. exceptions should occur infrequently in "normal" running of the code.
3. RAII everywhere etc.

I'm more interested in fine tuning.

G
Jul 22 '05 #1
9 2310
* Gianni Mariani <gi*******@mariani.ws> schriebt:

I'm involved in a new project and a new member on the team has voiced a
strong opinion that we should utilize exceptions.

The other members on the team indicate that they have either been burned
with unmaintainable code (an so are now not using exceptions). My
position is that "I can be convinced to use exceptions" and my
experience was that it let to code that was (much) more difficult to debug.

The team decided that we'd give exceptions a go. Yeah for progress !
Although strictly off-topic, it would be interesting to know how a team
where all but one favored not using exceptions, came to decide that they
should use exceptions.
I've been rattling my tiny brain on the issue and I have come to the
conclusion that exceptions are not as flexible as status codes.
The comparision is meaningless.

Do not replace status codes by exceptions.

Or vice versa.
a) For debugging purposes we want stack traces
C++ as a language has no facilities for obtaining stack trace information,
but nearly all tool-sets have.

- but only for cases
where the exception is caught in a place we consider a "bad" thing happened.
Unclear.
For example, say we have a "file" object that fails to "open" the file.
In most cases, it's ok, we don't really care, it's really not
exceptional except if it's an "important" file. I really don't know if
it's important at the lower levels (where the exception is thrown).
Do not use exceptions to indicate that a file open failed.

It's not a breach of contract.

It's _normal_ and _expected_ behavior that a file open fails.

An exception does not indicate failure.

It indicates breach of (normal case) contract: that a function wasn't able
to do whatever it was designed and contractually obligated to do.
b) It would really be nice to know what is going to catch an exception.
I know this is a bit of a wild thought but it would be nice if there
was a low-cost was of throwing context dependant exceptions. I suppose
we could do that using a TSS variable if we had co-operating
caller/thrower semantics.


The language C has a facility like the one you dream of. It's called
'longjmp'. However, that's not supported in C++ except when using C++
as C -- e.g. no stack based objects that need to be destroyed.

But why would you _want_ such spaghetti?

--
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 22 '05 #2
Ian
Gianni Mariani wrote:

<snip>
I know this is a contentious topic, I really don't want to start another
exception war, but I'd really like to hear from people experienced in
using exceptions in big complex projects and the things to avoid and
techniques to use to get the best bang for sweat.

The usual rules are clear.
1. assert for logic errors - absolutely no exceptions used to detect
logic errors.
2. exceptions should occur infrequently in "normal" running of the code.
3. RAII everywhere etc.

One bit of advice I'd like to offer is to have a base exception class
that has file, line and text members. This covers most of the
objections. You know where the exception was thrown and why.

Your exception base constructor can also double as your logger.

I tend to use a macro to throw such an exception,

#define Whinge( info ) throw Exception( (info), __FILE__, __LINE__ )

You can use this to make a 'soft assert'.

Use the exception's what() member to print out the details if required.
Very handy if you use a unit test framework like cppunit.

Ian
Jul 22 '05 #3
Alf P. Steinbach posted:
* Gianni Mariani <gi*******@mariani.ws> schriebt:

I'm involved in a new project and a new member on the team has voiced
a strong opinion that we should utilize exceptions.

The other members on the team indicate that they have either been
burned with unmaintainable code (an so are now not using exceptions).
My position is that "I can be convinced to use exceptions" and my
experience was that it let to code that was (much) more difficult to
debug.

The team decided that we'd give exceptions a go. Yeah for progress !


Although strictly off-topic, it would be interesting to know how a team
where all but one favored not using exceptions, came to decide that
they should use exceptions.

I disagree: If topical on-topic C++ conversation leads to a team's
preference for exceptions, then it *is* topical and hence, *on*-topic.
-JKop
Jul 22 '05 #4
Alf P. Steinbach wrote:
* Gianni Mariani <gi*******@mariani.ws> schriebt:
I'm involved in a new project and a new member on the team has voiced a
strong opinion that we should utilize exceptions.

The other members on the team indicate that they have either been burned
with unmaintainable code (an so are now not using exceptions). My
position is that "I can be convinced to use exceptions" and my
experience was that it let to code that was (much) more difficult to debug.

The team decided that we'd give exceptions a go. Yeah for progress !

Although strictly off-topic, it would be interesting to know how a team
where all but one favored not using exceptions, came to decide that they
should use exceptions.


It's a small team - 1 person is 20% of the team ! I think I was the
swing vote and the other hold-out was promised a number of things to
avoid issues he didn't like.

Many years ago I avoided exceptions because there were too many issues
with compilers' support. Since then I have seen so much unmaintainable
code with exceptions that I simply was not convinced. The new team
member seems to have a different experience and was able to convince me
that there is a way to do it right.

I've been rattling my tiny brain on the issue and I have come to the
conclusion that exceptions are not as flexible as status codes.

The comparision is meaningless.

Do not replace status codes by exceptions.

Or vice versa.


Everything you can do with exceptions you can do with status codes and
visa versa (given that you are writing the code that is).

The argument is that :
if ( ! DoSomthing() )
{
LOG( somthing bad happened );
}

is equivalent to

try { DoSomthing(); } catch ( X & x ) { LOG( somthing bad happened ); }

but with exceptons, I can reduce the complexity of the code:

try {
DoSomthing();
DoSomthing();
DoSomthing();
} ...

or more to the point you can use return values and keep the code simple.

try {
DoSomthing( File( "X.file" ) ) = BlastBytes();
}....

Nice, easy to read code that stops in it's tracks is "somthing bad happens".
a) For debugging purposes we want stack traces

C++ as a language has no facilities for obtaining stack trace information,
but nearly all tool-sets have.



- but only for cases
where the exception is caught in a place we consider a "bad" thing happened.

Unclear.


That's the point.

For example, say we have a "file" object that fails to "open" the file.
In most cases, it's ok, we don't really care, it's really not
exceptional except if it's an "important" file. I really don't know if
it's important at the lower levels (where the exception is thrown).

Do not use exceptions to indicate that a file open failed.


I think this *depends* on the use of the file object.

It's not a breach of contract.

It's _normal_ and _expected_ behavior that a file open fails.
What is normal is context dependant. For example, if a configuration
file is *required* for the code to run, it is an "exceptional" situation
while if a user requested file does not exist, well that's user error
which (IMHO) should not be an exception.

An exception does not indicate failure.

It indicates breach of (normal case) contract: that a function wasn't able
to do whatever it was designed and contractually obligated to do.
So there are:

a) breach of contract - hard assert
b) breach of *normal* contract - exception

So one has to define what *normal* contract is ? I have no particular
exception [:-)] to this idea but it does require documentation.
b) It would really be nice to know what is going to catch an exception.
I know this is a bit of a wild thought but it would be nice if there
was a low-cost was of throwing context dependant exceptions. I suppose
we could do that using a TSS variable if we had co-operating
caller/thrower semantics.

The language C has a facility like the one you dream of. It's called
'longjmp'. However, that's not supported in C++ except when using C++
as C -- e.g. no stack based objects that need to be destroyed.

But why would you _want_ such spaghetti?


Actually, you could do this the C++ way with a few simple C++ mechanism's.

Jul 22 '05 #5
Ian wrote:
Gianni Mariani wrote:

<snip>
I know this is a contentious topic, I really don't want to start
another exception war, but I'd really like to hear from people
experienced in using exceptions in big complex projects and the things
to avoid and techniques to use to get the best bang for sweat.

The usual rules are clear.
1. assert for logic errors - absolutely no exceptions used to detect
logic errors.
2. exceptions should occur infrequently in "normal" running of the code.
3. RAII everywhere etc.

One bit of advice I'd like to offer is to have a base exception class
that has file, line and text members. This covers most of the
objections. You know where the exception was thrown and why.

Your exception base constructor can also double as your logger.

I tend to use a macro to throw such an exception,

#define Whinge( info ) throw Exception( (info), __FILE__, __LINE__ )

You can use this to make a 'soft assert'.

Use the exception's what() member to print out the details if required.
Very handy if you use a unit test framework like cppunit.


Yep, the current "assert" mechanism we use will normally do a hard
assert unless it's running in a test case in which case it does a "test
case assert" which actually throws a "TestCase" exception.
Jul 22 '05 #6
* Gianni Mariani <gi*******@mariani.ws> schriebt:

Everything you can do with exceptions you can do with status codes and
visa versa (given that you are writing the code that is).
Only by using exceptions as non-local dynamic goto's. Goto is bad enough.
Non-local dynamic goto's are infinitely worse.

The argument is that :
if ( ! DoSomthing() )
{
LOG( somthing bad happened );
}

is equivalent to

try { DoSomthing(); } catch ( X & x ) { LOG( somthing bad happened ); }
No it isn't (and btw., don't catch exceptions by reference to non-const).
Most obvious, the latter is typically much less efficient. Perhaps less
obvious, the latter can invoke std::terminate where the former does not.
But most important, the latter implementation of DoSomthing has a different
contract the former. The bool-returning version has an obligation to try
to achieve something but does not guarantee it; the exception-throwing
version does guarantee the result, and client code can be written as if
that guarantee holds -- without catching or caring about the exception
(whereas with the status-code version client code must check the status).

but with exceptons, I can reduce the complexity of the code:

try {
DoSomthing();
DoSomthing();
DoSomthing();
} ...
That's incorrect. You have reduced the complexity of one particular
instance of client code. If it's natural for DoSomthing to return
false then you have also increased complexity of other client code.
or more to the point you can use return values and keep the code simple.

try {
DoSomthing( File( "X.file" ) ) = BlastBytes();
}....

Nice, easy to read code
I'm almost throwing up.
Do not use exceptions to indicate that a file open failed.


I think this *depends* on the use of the file object.


It does.

What is normal is context dependant.


It is, but not in the sense you seem to mean.

What is normal for a given function Foo is not context dependent.

But you can use Foo as part of the implementation of code that offers
a different contract than Foo does, e.g. Foo is OpenFile, and the wrapper
code with a somewhat stronger contract is OpenConfigurationFile.

--
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 22 '05 #7
Alf P. Steinbach wrote:
* Gianni Mariani <gi*******@mariani.ws> schriebt:
Everything you can do with exceptions you can do with status codes and
visa versa (given that you are writing the code that is).

Only by using exceptions as non-local dynamic goto's. Goto is bad enough.
Non-local dynamic goto's are infinitely worse.


I agree with the latter but I don't understand *your* definition of
non-local dynamic goto. In essance any use of exceptions is equivalent
to a non-local dynamic goto.
The argument is that :
if ( ! DoSomthing() )
{
LOG( somthing bad happened );
}

is equivalent to

try { DoSomthing(); } catch ( X & x ) { LOG( somthing bad happened ); }

No it isn't (and btw., don't catch exceptions by reference to non-const).
Most obvious, the latter is typically much less efficient.


Only if DoSomthing throws an exception, otherwise in theory it is much
more efficient, right ?

Perhaps less obvious, the latter can invoke std::terminate where the former does not.
std:terminate can be called for all kinds of reasons beyond what happens
here.
But most important, the latter implementation of DoSomthing has a different
contract the former. The bool-returning version has an obligation to try
to achieve something but does not guarantee it; the exception-throwing
version does guarantee the result, and client code can be written as if
that guarantee holds -- without catching or caring about the exception
(whereas with the status-code version client code must check the status).
Which means, *in theory*, you can write simpler-looking code (or
deceptively simpler-looking might be better).
but with exceptons, I can reduce the complexity of the code:

try {
DoSomthing();
DoSomthing();
DoSomthing();
} ...

That's incorrect. You have reduced the complexity of one particular
instance of client code. If it's natural for DoSomthing to return
false then you have also increased complexity of other client code.


It depends on what "natural" means.

or more to the point you can use return values and keep the code simple.

try {
DoSomthing( File( "X.file" ) ) = BlastBytes();
}....

Nice, easy to read code

I'm almost throwing up.


Do explain.
Do not use exceptions to indicate that a file open failed.


I think this *depends* on the use of the file object.

It does.
What is normal is context dependant.

It is, but not in the sense you seem to mean.

What is normal for a given function Foo is not context dependent.

But you can use Foo as part of the implementation of code that offers
a different contract than Foo does, e.g. Foo is OpenFile, and the wrapper
code with a somewhat stronger contract is OpenConfigurationFile.


OK, this I get.

This infers I need a different class for each kind of context a concept
may be used in with respect to exceptions. This can have a significant
impact on code complexity.
Jul 22 '05 #8
* Gianni Mariani <gi*******@mariani.ws> schriebt:
Alf P. Steinbach wrote:
* Gianni Mariani <gi*******@mariani.ws> schriebt:
Everything you can do with exceptions you can do with status codes and
visa versa (given that you are writing the code that is).

Only by using exceptions as non-local dynamic goto's. Goto is bad enough.
Non-local dynamic goto's are infinitely worse.


I agree with the latter but I don't understand *your* definition of
non-local dynamic goto. In essance any use of exceptions is equivalent
to a non-local dynamic goto.


Well, that's my definition... ;-)

The difference is whether you care or not where an exception will be caught.

Exception are beneficial when you don't care about where they'll end up.

Used that way it makes no difference that internally they do some goto'ing;
just like it makes no difference that a 'for'-loop internally does some
goto'ing.

But when you do care, at the point of throwing, where an exception will end
up, then you're _using_ the exception as a goto.

And that has some negative consequences.

In the case of the boolean function that is rewritten as a void function
throwing an exception, that function then takes partial responsibility for
choosing one of two paths in the client code, and to do that it requires the
client code to use a try-catch, and it's then much more diffult to ignore the
logical 'false' result (an exception) should that be desired, and also it's
then much more difficult to write correct client code, because there may be
other possible exceptions, and it's much more difficult to test.
The argument is that :
if ( ! DoSomthing() )
{
LOG( somthing bad happened );
}

is equivalent to

try { DoSomthing(); } catch ( X & x ) { LOG( somthing bad happened ); }

No it isn't (and btw., don't catch exceptions by reference to non-const).
Most obvious, the latter is typically much less efficient.


Only if DoSomthing throws an exception, otherwise in theory it is much
more efficient, right ?


Nope, but neither is it necessarily much less efficient in the case of no
exception.

Some C++ implementations such as Visual C++ has some overhead even in this
case, in order to support low-level (non-C++) exceptions.

But focusing on local overhead or not in the context of clarity and robustness
is nearly always the wrong thing to do (i.e. leading to bad decisions). Local
efficiency can be improved by coding in e.g. assemler language. We don't do
that, for the time that is used on coding in assembler language can be much
more profitably spent on correctness, clarity and high-level optimization in
C++ -- and using exceptions is very much about correctness and clarity.

Perhaps less
obvious, the latter can invoke std::terminate where the former does not.


std:terminate can be called for all kinds of reasons beyond what happens
here.


Yes, but my point is that one must be very careful when thinking about
equivalence-preserving transformations of code.

There is no transformation that is guaranteed to yield 100% equivalent code.

Some things change, if only the line numbering (or whatever); and in the case
of transforming from status code to exception contracts change, efficiency in
different situtations changes, the possibility of calls to std::terminate
(e.g. if an automatic destructor call throws) changes, and so on; in
particular, how to test the code and how it can be tested changes _a lot_.


try {
DoSomthing( File( "X.file" ) ) = BlastBytes();
}....

Nice, easy to read code

I'm almost throwing up.


Do explain.


The File object is destroyed before the assignment. What does DoSomething
return? It's not at all nice code, but in fact incomprehensible code. That,
however, has nothing to do with use of exceptions or not. AFAICS.

What is normal for a given function Foo is not context dependent.

But you can use Foo as part of the implementation of code that offers
a different contract than Foo does, e.g. Foo is OpenFile, and the wrapper
code with a somewhat stronger contract is OpenConfigurationFile.


OK, this I get.

This infers I need a different class for each kind of context a concept
may be used in with respect to exceptions. This can have a significant
impact on code complexity.


Not necessarily a different class, but at least some simple function wrappers.

And yes, that does tend to reduce code complexity.

It reduces complexity because it replaces implied, context-dependent contracts
by explicit, static contracts, which is nearly always a Good Thing (TM).

Implied contracts have to be figured out.

And in maintainance coding, which constitutes the bulk of coding, implied,
context-dependent contracts are seldom fully understood (let's see, if I
change a little here and copy some old working code from another context in
here, yep, it seems to work! hurray!), which means they're not adhered to as
they should, which means bugs and complexity.

--
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 22 '05 #9
Alf P. Steinbach wrote:
* Gianni Mariani <gi*******@mariani.ws> schriebt:
....
logical 'false' result (an exception) should that be desired, and also it's
then much more difficult to write correct client code, because there may be
other possible exceptions, and it's much more difficult to test.

Agreed.

.... snipped lots of stuff ...

try {
DoSomthing( File( "X.file" ) ) = BlastBytes();
}....

Nice, easy to read code
I'm almost throwing up.
Do explain.

The File object is destroyed before the assignment. What does DoSomething
return? It's not at all nice code, but in fact incomprehensible code. That,
however, has nothing to do with use of exceptions or not. AFAICS.


File is not destroyed until the expression is evaluated. DoSomthing
*may* return a proxy or it may be a badly named class ! The point was
that if the File constructor failed by throwing an exception, magically
things would do the Right(TM) thing.

What is normal for a given function Foo is not context dependent.

But you can use Foo as part of the implementation of code that offers
a different contract than Foo does, e.g. Foo is OpenFile, and the wrapper
code with a somewhat stronger contract is OpenConfigurationFile.


OK, this I get.

This infers I need a different class for each kind of context a concept
may be used in with respect to exceptions. This can have a significant
impact on code complexity.

Not necessarily a different class, but at least some simple function wrappers.

And yes, that does tend to reduce code complexity.

It reduces complexity because it replaces implied, context-dependent contracts
by explicit, static contracts, which is nearly always a Good Thing (TM).

Implied contracts have to be figured out.

And in maintainance coding, which constitutes the bulk of coding, implied,
context-dependent contracts are seldom fully understood (let's see, if I
change a little here and copy some old working code from another context in
here, yep, it seems to work! hurray!), which means they're not adhered to as
they should, which means bugs and complexity.


I was thinking that it would add code complexity ! OK, I'll soon find out !

Jul 22 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

8
by: Shane Groff | last post by:
I know this is a recurring discussion (I've spent the last 3 days reading through threads on the topic), but I feel compelled to start it up again. After reading through the existing threads, I...
10
by: Jakob Bieling | last post by:
Hi, somehow the prejudice of exceptions being rather slow (compared to, ie. returning an error value and checking that) keeps sticking around .. at least around me. I guess this is also why I...
2
by: Paul Reddin | last post by:
Hi, I've implemented calling a SP form a trigger using the CALL_PROCEDURE() UDF from http://www-106.ibm.com/developerworks/db2/library/techarticle/dm-0402greenstein/ A couple of questions...
1
by: chen | last post by:
We're having an internal debate about the merits & demerits of returning status codes in the output message vs exceptions to signify errors in handling a Web method. The status code camp is...
4
by: Steve | last post by:
I have read a couple articles online, read my Jesse Liberty book but I am still confused as to just what the best practices are for using exceptions. I keep changing how I'm working with them and...
5
by: tryptik | last post by:
All- I have heard differing points of view on whether or not constructors should throw. I am working on a library, and I need to know if it is bad form for a consturctor to throw. Thanks -J
29
by: mailforpr | last post by:
Sometimes, I can't think of any good reason why I should have the program's logic thrown an exception. Except for catching the exception and printing "Uh, oh" to the screen. I also think that in...
13
by: mike3 | last post by:
Hi. (crossposted because the program is in C++ and some C++-related elements are discussed, hence comp.lang.c++, plus general program design questions are asked, hence comp.programming.) I'm...
37
by: Sweetiecakes | last post by:
Hello I'm a bit confused on how one should handle exceptions. I'm currently building an ADO.NET Windows Forms application. I've built a class for manipulating data within the database in...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.