Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Exceptions vs Status codes

Reply
Thread Tools

Exceptions vs Status codes

 
 
Gianni Mariani
Guest
Posts: n/a
 
      05-17-2004

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


 
Reply With Quote
 
 
 
 
Alf P. Steinbach
Guest
Posts: n/a
 
      05-17-2004
* Gianni Mariani <(E-Mail Removed)> 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?
 
Reply With Quote
 
 
 
 
Ian
Guest
Posts: n/a
 
      05-17-2004
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
 
Reply With Quote
 
JKop
Guest
Posts: n/a
 
      05-17-2004
Alf P. Steinbach posted:

> * Gianni Mariani <(E-Mail Removed)> 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
 
Reply With Quote
 
Gianni Mariani
Guest
Posts: n/a
 
      05-17-2004
Alf P. Steinbach wrote:
> * Gianni Mariani <(E-Mail Removed)> 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.



 
Reply With Quote
 
Gianni Mariani
Guest
Posts: n/a
 
      05-17-2004
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.


 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      05-17-2004
* Gianni Mariani <(E-Mail Removed)> 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?
 
Reply With Quote
 
Gianni Mariani
Guest
Posts: n/a
 
      05-17-2004
Alf P. Steinbach wrote:
> * Gianni Mariani <(E-Mail Removed)> 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.


 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      05-18-2004
* Gianni Mariani <(E-Mail Removed)> schriebt:
> Alf P. Steinbach wrote:
> > * Gianni Mariani <(E-Mail Removed)> 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?
 
Reply With Quote
 
Gianni Mariani
Guest
Posts: n/a
 
      05-18-2004
Alf P. Steinbach wrote:
> * Gianni Mariani <(E-Mail Removed)> 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 !



 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
how to write codes to connect access database using html codes hiralcp Software 0 12-18-2007 08:49 PM
How to use file I/o codes with form and controls codes Allen ASP .Net 1 12-03-2007 12:04 AM
Architecture question: Exceptions vs status codes chen ASP .Net Web Services 7 01-12-2006 12:16 AM
Virtual Key Codes, Scan Codes and ASCII Codes in C gj_williams2000@yahoo.co.uk C Programming 2 08-20-2005 11:04 AM
RegEx replace of html codes to ascii codes Greg -- ASP .Net 4 08-09-2005 07:27 PM



Advertisments