public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* try, finally
@ 2008-03-19 16:13 Jason Cipriani
  2008-03-19 16:14 ` Jason Cipriani
                   ` (4 more replies)
  0 siblings, 5 replies; 23+ messages in thread
From: Jason Cipriani @ 2008-03-19 16:13 UTC (permalink / raw)
  To: gcc-help

Does GCC have anything similar to the MS and Borland compiler's __try
and __finally keywords? When using GCC I often find that I have code
like this (a moderately complex, and highly contrived, example):

====

  void *data1 = NULL, *data2 = NULL, *data3 = NULL;

  try {

    if (!(data1 = malloc(1000)))
      throw Something();
    if (!(data2 = malloc(1000)))
      throw Something();
    if (!(data3 = malloc(1000)))
      throw Something();

  } catch (...) {

    // cleanup code
    free(data3);
    free(data2);
    free(data1);
    throw;

  }

  // the same cleanup code
  free(data3);
  free(data2);
  free(data1);

===

Where I am duplicating cleanup code for normal returns and exception
handling, but what I really want to do is something like this:

===

void *data1 = NULL, *data2 = NULL, *data3 = NULL;

__try {

  if (!(data1 = malloc(1000)))
    throw Something();
  if (!(data2 = malloc(1000)))
    throw Something();
  if (!(data3 = malloc(1000)))
    throw Something();

  // do stuff

} __finally {

  // cleanup code
  free(data3);
  free(data2);
  free(data1);

}

===

Thanks,
Jason

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 16:13 try, finally Jason Cipriani
@ 2008-03-19 16:14 ` Jason Cipriani
  2008-03-19 17:02   ` Tim Prince
  2008-03-19 16:37 ` Brian Dessent
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 23+ messages in thread
From: Jason Cipriani @ 2008-03-19 16:14 UTC (permalink / raw)
  To: gcc-help

On Wed, Mar 19, 2008 at 12:13 PM, Jason Cipriani
<jason.cipriani@gmail.com> wrote:
> Does GCC have anything similar to the MS and Borland compiler's __try
>  and __finally keywords? When using GCC I often find that I have code
>  like this (a moderately complex, and highly contrived, example):

Heh, well, the example wasn't really that complex. I had typed a
weirder one and then simplified it but left this comment in.




>
>  ====
>
>   void *data1 = NULL, *data2 = NULL, *data3 = NULL;
>
>   try {
>
>     if (!(data1 = malloc(1000)))
>       throw Something();
>     if (!(data2 = malloc(1000)))
>       throw Something();
>     if (!(data3 = malloc(1000)))
>       throw Something();
>
>   } catch (...) {
>
>     // cleanup code
>     free(data3);
>     free(data2);
>     free(data1);
>     throw;
>
>   }
>
>   // the same cleanup code
>   free(data3);
>   free(data2);
>   free(data1);
>
>  ===
>
>  Where I am duplicating cleanup code for normal returns and exception
>  handling, but what I really want to do is something like this:
>
>  ===
>
>  void *data1 = NULL, *data2 = NULL, *data3 = NULL;
>
>  __try {
>
>   if (!(data1 = malloc(1000)))
>     throw Something();
>   if (!(data2 = malloc(1000)))
>     throw Something();
>   if (!(data3 = malloc(1000)))
>     throw Something();
>
>   // do stuff
>
>  } __finally {
>
>   // cleanup code
>   free(data3);
>   free(data2);
>   free(data1);
>
>  }
>
>  ===
>
>  Thanks,
>  Jason
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 16:13 try, finally Jason Cipriani
  2008-03-19 16:14 ` Jason Cipriani
@ 2008-03-19 16:37 ` Brian Dessent
  2008-03-19 17:31 ` Ted Byers
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 23+ messages in thread
From: Brian Dessent @ 2008-03-19 16:37 UTC (permalink / raw)
  To: Jason Cipriani; +Cc: gcc-help

Jason Cipriani wrote:

> Does GCC have anything similar to the MS and Borland compiler's __try
> and __finally keywords? When using GCC I often find that I have code

Adding SEH support to gcc has been tossed around for years but nothing
usable has yet to come of it.  You can still use SEH through
SetUnhandledExceptionFilter() or by manipulating the exception chain at
%fs:0 manually, but there is no built in compiler support for it.

Brian

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 16:14 ` Jason Cipriani
@ 2008-03-19 17:02   ` Tim Prince
  0 siblings, 0 replies; 23+ messages in thread
From: Tim Prince @ 2008-03-19 17:02 UTC (permalink / raw)
  To: Jason Cipriani; +Cc: gcc-help

Jason Cipriani wrote:
> On Wed, Mar 19, 2008 at 12:13 PM, Jason Cipriani
> <jason.cipriani@gmail.com> wrote:
>> Does GCC have anything similar to the MS and Borland compiler's __try
>>  and __finally keywords? When using GCC I often find that I have code
>>  like this (a moderately complex, and highly contrived, example):
> 
> Heh, well, the example wasn't really that complex. I had typed a
> weirder one and then simplified it but left this comment in.
> 
The w32api headers in cygwin replicate some of this functionality.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 16:13 try, finally Jason Cipriani
  2008-03-19 16:14 ` Jason Cipriani
  2008-03-19 16:37 ` Brian Dessent
@ 2008-03-19 17:31 ` Ted Byers
  2008-03-19 17:37 ` me22
  2008-03-25 22:28 ` Ian Lance Taylor
  4 siblings, 0 replies; 23+ messages in thread
From: Ted Byers @ 2008-03-19 17:31 UTC (permalink / raw)
  To: Jason Cipriani, gcc-help

Looks like a routine C++ question.  Unless I missed
something, the RAII idiom applies.  Replace your naked
pointers by a class (an instance of a template such as
an std::auto_ptr would suffice; or the boost
shared_ptr is a good alternative should the semantics
of how you're using the object require it).  You
create an instance of your object holding the resource
and you are guaranteed it will be cleaned up
regardless of what exit path you follow.  If an
exception occurs, the instance of the class will be
cleaned up as part of how the stack is unwound.  If
you exit normally, it will be cleaned up once it goes
out of scope.  No need for syntactic sugar like
__try/__finally.

I don't know about what others prefer to do, or common
practice if there is such a thing, but to me what
passes for common sense is that avoiding vendor
extensions whenever possible minimizes the pain
experienced when the code must pass through another
suite of development tools.  And I like to pass my
code through two different tool chains, just to catch
potential issues that one is better at catching than
the other.  In other words, if it ain't in the
standard, I don't use it unless there is really no
other choice.  In the 15 years I have been using C++,
I have never found it necessary to use anything not in
the standard (since it first apeared), and as a result
I generally have no problem compiling it with any of
the tools I regularly use (GCC and MSVC).

What you say suggests you ought to take an hour or two
to study RAII (resource acquisition is
initialization), and see how far that allows you to
clean up obviously problematic code.

HTH

Ted

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 16:13 try, finally Jason Cipriani
                   ` (2 preceding siblings ...)
  2008-03-19 17:31 ` Ted Byers
@ 2008-03-19 17:37 ` me22
  2008-03-19 19:09   ` Jason Cipriani
  2008-03-25 22:28 ` Ian Lance Taylor
  4 siblings, 1 reply; 23+ messages in thread
From: me22 @ 2008-03-19 17:37 UTC (permalink / raw)
  To: Jason Cipriani; +Cc: gcc-help

On Wed, Mar 19, 2008 at 12:13 PM, Jason Cipriani
<jason.cipriani@gmail.com> wrote:
> Does GCC have anything similar to the MS and Borland compiler's __try
>  and __finally keywords? When using GCC I often find that I have code
>  like this (a moderately complex, and highly contrived, example):
>
>  ====
>
>  [snip code]
>

Basically the same thing in C++:

If you wanted it as an array:

#include <vector>
std::vector<char> data1(1000), data2(1000), data3(1000);

Or, if you're allocating for an object,

#include <memory>
std::auto_ptr<whatever> data1(new whatever);
std::auto_ptr<whatever> data2(new whatever);
std::auto_ptr<whatever> data3(new whatever);

And that's it.  C++'s (default) new throws on failure instead of
returning 0, and the destructors make sure everything that's been
constructed gets destructed properly.

Why bother with void* and C?

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 19:09   ` Jason Cipriani
@ 2008-03-19 19:09     ` me22
  2008-03-20  5:50       ` Jason Cipriani
  2008-03-20 13:30       ` Noel Yap
  2008-03-19 21:04     ` Ted Byers
  1 sibling, 2 replies; 23+ messages in thread
From: me22 @ 2008-03-19 19:09 UTC (permalink / raw)
  To: Jason Cipriani; +Cc: gcc-help

On Wed, Mar 19, 2008 at 3:02 PM, Jason Cipriani
<jason.cipriani@gmail.com> wrote:
>  Does std or boost have some kind of "smart pointers" that let me
>  define the cleanup actions without having to write too much additional
>  code?
>

Yup, boost::shared_ptr (std::shared_ptr, in C++1x) will let you do that.

boost::shared_ptr<xmlDoc> doc( xmlReadFile(...), xmlFreeDoc );
if (!doc) throw something();

etc

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 17:37 ` me22
@ 2008-03-19 19:09   ` Jason Cipriani
  2008-03-19 19:09     ` me22
  2008-03-19 21:04     ` Ted Byers
  0 siblings, 2 replies; 23+ messages in thread
From: Jason Cipriani @ 2008-03-19 19:09 UTC (permalink / raw)
  To: me22; +Cc: gcc-help

> On Wed, Mar 19, 2008 at 12:13 PM, Jason Cipriani
>  <jason.cipriani@gmail.com> wrote:
>
> > Does GCC have anything similar to the MS and Borland compiler's __try
>  >  and __finally keywords? When using GCC I often find that I have code
>  >  like this (a moderately complex, and highly contrived, example):

Thanks for your responses, guys.

On Wed, Mar 19, 2008 at 12:37 PM, Brian Dessent <brian@dessent.net> wrote:
>  Adding SEH support to gcc has been tossed around for years but nothing
>  usable has yet to come of it.  You can still use SEH through
>  SetUnhandledExceptionFilter() or by manipulating the exception chain at
>  %fs:0 manually, but there is no built in compiler support for it.

Oh well. Maybe some day. Thanks for the tips. I probably won't do
anything like messing with SetUnhandledExceptionFilter(), mostly I
just want to simplify code in common situations, I don't want to do
anything *too* strange, though.

On Wed, Mar 19, 2008 at 1:02 PM, Tim Prince <tprince@myrealbox.com> wrote:
>  The w32api headers in cygwin replicate some of this functionality.

Thanks. I'll check those out just to see how they do it, but I'm
actually doing Linux development at the moment, and on Windows I
usually use MinGW GCC.

On Wed, Mar 19, 2008 at 1:30 PM, Ted Byers <r.ted.byers@rogers.com> wrote:
>  What you say suggests you ought to take an hour or two
>  to study RAII (resource acquisition is
>  initialization), and see how far that allows you to
>  clean up obviously problematic code.

Agreed; I know the idiom but I've been always avoided the std and
boost RAII utilities for some reason -- however, it's 2008 and
probably about time for me to bite the bullet, especially with C++0x
coming out some day, I have no excuse.

One situation I frequently find myself in is something like: "Ugh... I
want to write this code and I really don't feel like implementing
everything required to make resource cleanup automatic here; I'll just
put cleanup code everywhere instead". In reality, those things are
already written, I guess.

me22 <me22.ca@gmail.com> wrote:
> Why bother with void* and C?

Just an example.

So, WRT what Ted Byers and me22 said, an issue I typically have that I
always assumed the std / boost utilities couldn't handle (and hence
one reason why I never bother looking into them), is that it's not
always as simple as creating objects with new and delete. For example,
some code I recently wrote (and what prompted me to ask this question)
used libxml2, a C library, from my C++ code; say something like this
(if you are actually familiar with libxml2; I made up the
FindChildNode function):

=====

xmlDoc *doc = NULL;
xmlNode *root, *node;
xmlChar *str1 = NULL, *str2 = NULL, *str3 = NULL;

// load document, fail on error
if (!(doc = xmlReadFile(...)))
  throw Something();

try {

  // get root xml node, fail if document is empty
  if (!(root = xmlDocGetRootElement(doc)))
    throw Something();

  // get "first" node string into str1, fail on error
  if (!(node = FindChildNode(root, "first")))
    throw Something();
  if (!(str1 = xmlNodeGetContent(node)))
    throw Something();

  // get "second" node string into str2, fail on error
  if (!(node = FindChildNode(root, "second")))
    throw Something();
  if (!(str2 = xmlNodeGetContent(node)))
    throw Something();

  // get "third" node string into str3, fail on error
  if (!(node = FindChildNode(root, "third")))
    throw Something();
  if (!(str3 = xmlNodeGetContent(node)))
    throw Something();

  // do something with str1, str2, str3

} catch (...) {

  xmlFree(str3);
  xmlFree(str2);
  xmlFree(str1);
  xmlFreeDoc(doc);
  xmlCleanupParser();
  throw;

}

// and duplicate cleanup code:
xmlFree(str3);
xmlFree(str2);
xmlFree(str1);
xmlFreeDoc(doc);
xmlCleanupParser();

=====

Now, in this specific example: there are a few other third-party
libraries available that provide C++ bindings to libxml2 -- let's say
that for whatever reason they are not acceptable here. And in the
general case, the cleanup code here is not "deleting" something, they
are custom cleanup functions that I have no control over.

If my goal is to reduce the amount of coding I have to do, it is far
easier for me to duplicate the cleanup code than to go and write a
bunch of small C++ wrapper objects with automatic cleanup that I can
use interchangeably with the libxml2 data types.

Does std or boost have some kind of "smart pointers" that let me
define the cleanup actions without having to write too much additional
code?

Thanks,
Jason

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 19:09   ` Jason Cipriani
  2008-03-19 19:09     ` me22
@ 2008-03-19 21:04     ` Ted Byers
  2008-03-20  6:28       ` Jason Cipriani
  1 sibling, 1 reply; 23+ messages in thread
From: Ted Byers @ 2008-03-19 21:04 UTC (permalink / raw)
  To: Jason Cipriani; +Cc: gcc-help

--- Jason Cipriani <jason.cipriani@gmail.com> wrote:
> > On Wed, Mar 19, 2008 at 12:13 PM, Jason Cipriani
> >  <jason.cipriani@gmail.com> wrote:
> >
> > > Does GCC have anything similar to the MS and
> Borland compiler's __try
> >  >  and __finally keywords? When using GCC I often
> find that I have code
> >  >  like this (a moderately complex, and highly
> contrived, example):
> 
> Thanks for your responses, guys.
> 
> On Wed, Mar 19, 2008 at 12:37 PM, Brian Dessent
> <brian@dessent.net> wrote:
> >  Adding SEH support to gcc has been tossed around
> for years but nothing
> >  usable has yet to come of it.  You can still use
> SEH through
> >  SetUnhandledExceptionFilter() or by manipulating
> the exception chain at
> >  %fs:0 manually, but there is no built in compiler
> support for it.
> 
> Oh well. Maybe some day. Thanks for the tips. I
> probably won't do
> anything like messing with
> SetUnhandledExceptionFilter(), mostly I
> just want to simplify code in common situations, I
> don't want to do
> anything *too* strange, though.
>
But examining it for yourself, and possibly developing
exception classes as wrappers for SEH would be a
useful exercise anyway, and if well done may provide
for more useful and more robust exception handling.
 

> [snip]
> On Wed, Mar 19, 2008 at 1:30 PM, Ted Byers
> <r.ted.byers@rogers.com> wrote:
> >  What you say suggests you ought to take an hour
> or two
> >  to study RAII (resource acquisition is
> >  initialization), and see how far that allows you
> to
> >  clean up obviously problematic code.
> 
> Agreed; I know the idiom but I've been always
> avoided the std and
> boost RAII utilities for some reason -- however,
> it's 2008 and
> probably about time for me to bite the bullet,
> especially with C++0x
> coming out some day, I have no excuse.
> 
> One situation I frequently find myself in is
> something like: "Ugh... I
> want to write this code and I really don't feel like
> implementing
> everything required to make resource cleanup
> automatic here; I'll just
> put cleanup code everywhere instead". In reality,
> those things are
> already written, I guess.
> 
This suggests you don't really understand RAII.  There
are no std or boost "utilities" specific to RAII. 
rather, there are classes in both the standard C++
library and in boost that are useful in making
implementing RAII trivially easy.  If you are not
passing your pointers around among a suite of
functions, then using RAII can be as simple as using
an std::auto_ptr instead of a naked pointer.  If,
however, you are passing your pointers around a suite
of functions, then the boost::shared_ptr is a smart
choice.  Just replace the naked pointer by a smart
pointer.

To ignore such a common idiom dooms you to reinventing
the wheel, ignoring the accumulated experience of
software developers with more, or different,
experience than you have yourself.  Even though I have
been programming for almost 30 years, I am ALWAYS
looking for emerging idioms and new design patterns
that better programmers than I have discovered!  And
when I find one that is new to me, and that appears
relevant to a problem I am working on, I use it.  I am
not too proud to learn from others.

> me22 <me22.ca@gmail.com> wrote:
> > Why bother with void* and C?
> 
> Just an example.
> 
> So, WRT what Ted Byers and me22 said, an issue I
> typically have that I
> always assumed the std / boost utilities couldn't
> handle (and hence
> one reason why I never bother looking into them), is
> that it's not
> always as simple as creating objects with new and
> delete.
Get your classes right, with whatever inheritance and
containment necessary, and it CAN become as simple as
creating objects with new, and destructing them with
delete.  (Actually, as far as possible, you want to
leave invoking delete to your smart pointers!!!)

> For example,
> some code I recently wrote (and what prompted me to
> ask this question)
> used libxml2, a C library, from my C++ code; say
> something like this
> (if you are actually familiar with libxml2; I made
> up the
> FindChildNode function):
> 
> =====
> 
> xmlDoc *doc = NULL;
> xmlNode *root, *node;
> xmlChar *str1 = NULL, *str2 = NULL, *str3 = NULL;
> 
Just replacing ALL these pointers with smart pointers
would be a decent first cut at making it exception
safe.

The following code strikes me as an abuse of exception
handling.  Exceptions ought to be reserved for
unpredictable, rare error conditions.  Unless you are
doing ONLY batch processing, you want your interface
to be as user friendly as possible.  Therefore, you
want to, and can, know precisely what conditions would
result in your call to, say, xmlReadFile failing, and
can test for it.  If your test(s) for such conditions
that could lead to failure identifies a potential
problem, you can notify your user of the problem and
prompt him to take corrective action.  Your error
condition, then, doesn't arise.  User error WRT data
input is common enough that it is much better to test
for all errors that you can test for, and prompt the
user for corrective action than it is to just throw
exceptions willy nilly every time a statement doesn't
give you what you wanted.

To use a trivial example, suppose your code does
mathematical analysis and you routinely evaluate a
quotient "x / (a - b)".  You KNOW going in that you'll
encounter a divide by zero error condition either if a
= b or both 'a' and 'b' = 0.  Instead of just letting
the error happen, you test for the possibility of the
error condition, and when detected you terminate the
affected call, returning control to a function that
prompts the user for corrective action and handle the
result.

If a programmer working for me showed me code like
that which you show here, I would probably send him
back to the drawing board for there is no evidence of
any analysis of error conditions and possible error
prevention.  Such a failure guarantees a less than
adequate user experience, and significant wasted user
time, squandered on trying to figure out why the
program didn't do what the user expected it to do.

> // load document, fail on error
> if (!(doc = xmlReadFile(...)))
>   throw Something();
> 
> try {
> 
>   // get root xml node, fail if document is empty
>   if (!(root = xmlDocGetRootElement(doc)))
>     throw Something();
> 
>   // get "first" node string into str1, fail on
> error
>   if (!(node = FindChildNode(root, "first")))
>     throw Something();
>   if (!(str1 = xmlNodeGetContent(node)))
>     throw Something();
> 
>   // get "second" node string into str2, fail on
> error
>   if (!(node = FindChildNode(root, "second")))
>     throw Something();
>   if (!(str2 = xmlNodeGetContent(node)))
>     throw Something();
> 
>   // get "third" node string into str3, fail on
> error
>   if (!(node = FindChildNode(root, "third")))
>     throw Something();
>   if (!(str3 = xmlNodeGetContent(node)))
>     throw Something();
> 
>   // do something with str1, str2, str3
> 
> } catch (...) {
> 
>   xmlFree(str3);
>   xmlFree(str2);
>   xmlFree(str1);
>   xmlFreeDoc(doc);
>   xmlCleanupParser();
>   throw;
> 
> }
> 
> // and duplicate cleanup code:
> xmlFree(str3);
> xmlFree(str2);
> xmlFree(str1);
> xmlFreeDoc(doc);
> xmlCleanupParser();
> 
> =====
> 
> Now, in this specific example: there are a few other
> third-party
> libraries available that provide C++ bindings to
> libxml2 -- let's say
> that for whatever reason they are not acceptable
> here. And in the
> general case, the cleanup code here is not
> "deleting" something, they
> are custom cleanup functions that I have no control
> over.
> 
> If my goal is to reduce the amount of coding I have
> to do, it is far
> easier for me to duplicate the cleanup code than to
> go and write a
> bunch of small C++ wrapper objects with automatic
> cleanup that I can
> use interchangeably with the libxml2 data types.
> 
This doesn't make sense.  If you can write cleanup
code, regardless of which library you're using, you
can do so efficiently using RAII.  You can't use the
existance of one badly written library or another as a
rational for poorly structuring your own code.  If for
some reason, you don't like existing wrapper libraries
for libxml2, then roll your own.  Trust me, your
preference for copy and paste duplication of your
"cleanup" code will come back to bite you at a most
inconvenient moment.  Your code will bloat as you
develop it further, eventually becoming a maintenance
nightmare.  Trust me, I have worked on projects that,
efficiently coded, had in excess of half a million
lines of code, and when a project gets that big, the
last thing you want to do is refactor to remove code
duplication.  It is much better to get it right the
first time through that to have to fix it after it has
become a bloated monster.

> Does std or boost have some kind of "smart pointers"
> that let me
> define the cleanup actions without having to write
> too much additional
> code?
> 
Yes, both do.  std::auto_ptr and boost::shared_ptr,
and there are variants of each used for pointers to
arrays.  You do have to make sure you use the right
version of each, for each pointer you're replacing. 
But if you have to use functions like 'xmlFree' to
properly clean up, there is no option other than to
write your own class that invokes such functions in
its destructor.  The smart pointers only invoke delete
on the enclosed pointer at the right time.  Even so,
writing a wrapper class for a resource you have to
manage remains trivially easy, and much more
maintainable than your copy and paste approach.  And
if you must keep your resource on the heap rather than
on the stack, you would still use your custom resource
class WITH one of the smart pointers.

HTH

Ted

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 19:09     ` me22
@ 2008-03-20  5:50       ` Jason Cipriani
  2008-03-20 13:30       ` Noel Yap
  1 sibling, 0 replies; 23+ messages in thread
From: Jason Cipriani @ 2008-03-20  5:50 UTC (permalink / raw)
  To: me22; +Cc: gcc-help

On Wed, Mar 19, 2008 at 3:09 PM, me22 <me22.ca@gmail.com> wrote:
> On Wed, Mar 19, 2008 at 3:02 PM, Jason Cipriani
>
> <jason.cipriani@gmail.com> wrote:
>
> >  Does std or boost have some kind of "smart pointers" that let me
>  >  define the cleanup actions without having to write too much additional
>  >  code?
>  >
>
>  Yup, boost::shared_ptr (std::shared_ptr, in C++1x) will let you do that.
>
>  boost::shared_ptr<xmlDoc> doc( xmlReadFile(...), xmlFreeDoc );
>  if (!doc) throw something();

Thanks, this is exactly the kind of solution I was looking for.

Jason

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 21:04     ` Ted Byers
@ 2008-03-20  6:28       ` Jason Cipriani
  2008-03-20 12:24         ` John Love-Jensen
  2008-03-20 14:50         ` Ted Byers
  0 siblings, 2 replies; 23+ messages in thread
From: Jason Cipriani @ 2008-03-20  6:28 UTC (permalink / raw)
  To: Ted Byers; +Cc: gcc-help

Thanks for the long response, Ted. Although I disagree with most of
it, I appreciate your time. Some of the points you have made are
slightly unclear:

On Wed, Mar 19, 2008 at 5:04 PM, Ted Byers <r.ted.byers@rogers.com> wrote:
>  The following code strikes me as an abuse of exception
>  handling.  Exceptions ought to be reserved for
>  unpredictable, rare error conditions.

How would you handle predictable, common error conditions, then? And
what do you consider to be unpredictable and rare? Going back to the
XML example (since it's an easy one to build on), let's say I have a
document structured like this:

<root>
  <something>
    <value>
      <data>
      </data>
    </value>
  </something>
</root>

If I was writing code to parse that, and found "root", then
"something", then "value", but not "data", I would call that a rare
error. Also the only way to really recover from it is to let the user
know what happened, and to leave the program in a valid state or
terminate it. Using exceptions that way provides for both in a very
simple way. In my examples, for simplicity, I threw a Something() --
but associating error messages with exceptions, and a good exception
inheritance structure, makes error handling and reporting very easy
(e.g. throw EDocumentFormatError(__FILE__, __LINE__, "Data node is
missing from file.")). Take Java as an extreme example of the use of
exceptions.

> Unless you are
>  doing ONLY batch processing, you want your interface
>  to be as user friendly as possible.  Therefore, you
>  want to, and can, know precisely what conditions would
>  result in your call to, say, xmlReadFile failing, and
>  can test for it.  If your test(s) for such conditions
>  that could lead to failure identifies a potential
>  problem, you can notify your user of the problem and
>  prompt him to take corrective action.

By this I assume you mean ask libxml for a clear error string and
somehow display it to the user, correct? If that is the case, I agree.
I hate receiving error messages such as "Error: Operation failed", and
so I do what I can to not display vague messages like that to the user
either. I did leave all that out of my example. However, including the
error message in an exception is an extremely clean way of passing
that information back to the caller. What would your alternative be?
Returning error codes from functions becomes messy, and setting global
error strings with functions such as GetLastErrorMessage() (or
whatever, you get the idea) comes with its own set of problems.

> Your error
>  condition, then, doesn't arise.

I am not sure what you mean. If a file does not exist and therefore
can not be opened, notifying the user of the error does not cause the
file to exist.

> User error WRT data
>  input is common enough that it is much better to test
>  for all errors that you can test for, and prompt the
>  user for corrective action than it is to just throw
>  exceptions willy nilly every time a statement doesn't
>  give you what you wanted.

Agreed. I don't see what is willy nilly. In this particular example, I
have an XML file that contains some data that I need to load. The XML
file must adhere to a very specific structure that has been defined
elsewhere. When the user chooses to load the file, the file must
either be 100% compliant with the defined structure, or it will fail
to load. I have no desire to prompt the user with message boxes such
as "Sorry, but the X data element was missing from this data file,
would you mind telling me what you think the value should be so I can
recover and continue?" A user would not be able to answer that
question in my case anyway. Instead, if the file has a slight problem
(it should not, as the files are not hand-generated), I want to notify
the user that the file could not be loaded. Of course, I would provide
them with a reason why the file could not be loaded, but it is not
always necessary, or even possible, to allow the user to intervene and
correct an error condition themselves. Therefore, in the XML example,
if any one of those operations fails, the entire load operation
*should* fail, and therefore every operation warrants an error return
on failure. In this case, exceptions are a great error return, because
they can store a lot more information than, say, returning an integer
from a function. In fact, this is *precisely* what exceptions are
designed for.

>  To use a trivial example, suppose your code does
>  mathematical analysis and you routinely evaluate a
>  quotient "x / (a - b)".  You KNOW going in that you'll
>  encounter a divide by zero error condition either if a
>  = b or both 'a' and 'b' = 0.  Instead of just letting
>  the error happen, you test for the possibility of the
>  error condition, and when detected you terminate the
>  affected call, ...

I agree completely. This is very reasonable.

> ...returning control to a function that
>  prompts the user for corrective action and handle the
>  result.

This seems like a reasonable way to do that:

class EDivideByZero : public Exception { ... };

int DoTheMath (int x, int a, int b)
  throw (EDivideByZero)
{
  if (a == b)
    throw EDivideByZero("Divide by zero: A must not equal B");
  else
    return x / (a - b);
}

void DoWhatever () {
  try {
    DoTheMath(4, 2, 4);
  } catch (Exception &e) {
    DisplayTheError(e.TheMessage);
  }
}

This notifies the user of the problem, and this also allows
DoWhatever() to handle errors without ever having to have knowledge of
what the error was. It is up to the source of the error to describe
the error.

>  If a programmer working for me showed me code like
>  that which you show here, I would probably send him
>  back to the drawing board for there is no evidence of
>  any analysis of error conditions and possible error
>  prevention.

I am not sure what you mean. There is analysis of error conditions --
all operations are checked for errors. Errors are not left unhandled.
If by no "error prevention" you mean that the user is not given any
notification of *what* operation caused the error -- well I assure you
that's because I left out specific error messages in my example, since
I was asking about SEH and they weren't relevant.

>  Such a failure guarantees a less than
>  adequate user experience, and significant wasted user
>  time, squandered on trying to figure out why the
>  program didn't do what the user expected it to do.

Much of what you say is based on the fact that my example had no
specific error messages. Given that, everything you say is completely
reasonable. However, in the actual code, I do provide error messages,
so most of the issues you are talking about are not problems. In fact,
I even include the file name in the error messages. <g>

>  > If my goal is to reduce the amount of coding I have
>  > to do, it is far
>  > easier for me to duplicate the cleanup code than to
>  > go and write a
>  > bunch of small C++ wrapper objects with automatic
>  > cleanup that I can
>  > use interchangeably with the libxml2 data types.
>  >
>  This doesn't make sense.  If you can write cleanup
>  code, regardless of which library you're using, you
>  can do so efficiently using RAII.  You can't use the
>  existance of one badly written library or another as a
>  rational for poorly structuring your own code.

I am not attempting to rationalize poor structuring of any code.

>  If for
>  some reason, you don't like existing wrapper libraries
>  for libxml2, then roll your own.  Trust me, your
>  preference for copy and paste duplication of your
>  "cleanup" code will come back to bite you at a most
>  inconvenient moment.  Your code will bloat as you
>  develop it further, eventually becoming a maintenance
>  nightmare.

Yes, I know this. This is precisely why I came here to ask about SEH
in GCC... SEH is a feature in other compilers that I use precisely to
prevent the bloat. GCC did not provide it, and now I am looking for an
alternative. You are preaching to the choir, my friend.

> Trust me, I have worked on projects that,
>  efficiently coded, had in excess of half a million
>  lines of code, and when a project gets that big, the
>  last thing you want to do is refactor to remove code
>  duplication.  It is much better to get it right the
>  first time through that to have to fix it after it has
>  become a bloated monster.

Again you are preaching to the choir. I promise. :-)

>  Yes, both do.  std::auto_ptr and boost::shared_ptr,
>  and there are variants of each used for pointers to
>  arrays.  You do have to make sure you use the right
>  version of each, for each pointer you're replacing.
>  But if you have to use functions like 'xmlFree' to
>  properly clean up, there is no option other than to
>  write your own class that invokes such functions in
>  its destructor.

Thanks. Actually, it seems that the boost::shared_ptr lets me use
xmlFree without writing my own class to do it -- see me22's previous
reply -- which is a perfect solution to my issue of duplicating
cleanup code.

> The smart pointers only invoke delete
>  on the enclosed pointer at the right time.

They can do other things besides delete.

> Even so,
>  writing a wrapper class for a resource you have to
>  manage remains trivially easy, and much more
>  maintainable than your copy and paste approach.

Writing a wrapper class is not necessary as long as your cleanup
function takes only one parameter (such as xmlFree), see
boost::shared_ptr. However, it is definitely more maintainable than my
copy and paste approach.

> And
>  if you must keep your resource on the heap rather than
>  on the stack, you would still use your custom resource
>  class WITH one of the smart pointers.

You can just use the smart pointers directly.

>
>  HTH

It is certainly food for thought. I do appreciate your reply.

>
>  Ted

Jason

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-20  6:28       ` Jason Cipriani
@ 2008-03-20 12:24         ` John Love-Jensen
  2008-03-21  2:11           ` Jason Cipriani
  2008-03-20 14:50         ` Ted Byers
  1 sibling, 1 reply; 23+ messages in thread
From: John Love-Jensen @ 2008-03-20 12:24 UTC (permalink / raw)
  To: Jason Cipriani; +Cc: GCC-help, Ted Byers

Hi Jason,

> How would you handle predictable, common error conditions, then?

In my projects, throwing an exception mean "the application is about to
terminate."

That's using the exception mechanism at the extreme of conservative
programming with exceptions.

Assuming you use exceptions with less draconian policy, the exception
mechanism is not for using as normal flow control.  It really means an
exceptional situation, outside of the normal flow control.

For normal flow control -- such as handling predictable, common error
conditions -- you should use return codes.

One way to have more "in your face" return codes, is to return "smart return
code objects".

A smart return code object has this kind of behavior:
+ wraps a return code
+ if the caller does read (query) the return code object,
  THEN the object marks itself as having been checked
  AND the object's destructor is silent
+ if the caller does not read (query) the return code object,
  AND the return code object does NOT have an error status,
  THEN the object's destructor is silent
+ if the caller does not read (query) the return code object,
  AND the return code object DOES have an error status,
  THEN the object's destructor does "something"
  Where "something" is one or more of...
  + an assert
  + a trace to a log
  + throw an exception

I consider a "smart return code object" to be training wheels for result
codes.  But for larger teams, they become very helpful.

> And what do you consider to be unpredictable and rare?

Any violation of the routine's contractual requirements.

For example, if an int parameter must have the value of 0, 1 or 2, and the
value passed in is something other than 0, 1 or 2.

Any violation of the routine's contractual obligations.

For example, if the routine must put the object in state A or B, but was
unable to do so and the object is in state C (a "never happen" situation).

For example, if the object is in an inconsistent state upon return, such
that the object's invariance is violated.  This can happen if a state change
cannot be completed as a transaction, and enough has happened such that the
state of the object cannot be reverted.  (That's why have transaction based
assignment operator -- perhaps using the swap paradigm -- is so very
useful.)

Any "never happen" situations.  (Where, in the case of my applications,
keeping in mind that throwing an exception means "terminate the application,
forthwith".  So throw an exception where it is appropriate to terminate the
application works as a rule-of-thumb in my application.  Your exception
usage policy will likely be less extreme.)

Any "is it okay if the processing continues (without throwing or returning
an error code) with the detected 'broken axle' condition?"  If it's not
okay, then either throw or return an error code.

> If I was writing code to parse that, and found "root", then
> "something", then "value", but not "data", I would call that a rare...

I presume you are speaking of the "meta-layer" (the decision making layer)
above the parser itself.

In my application, ponder "is this failure such that the application should
be terminated ... or can it be handled in some reasonable failsafe fallback
and the application continue running?"

Also useful is writing your own throw handler such that if a throw happens
it forks the application and core dumps the child (useful to have
"core.<PID>" files enabled).  That's how rarely I expect an exception to
occur, even when using exceptions with less draconian policies than
"terminate the application, forthwith".  And with a core you have a good
snapshot of the application and where things went awry.

Also, once you start using exceptions, you have to be careful too.
Exceptions cannot be thrown through a C barrier.  That means that you cannot
propagate an exception through an OS callback or other C callbacks.  You
cannot propagate an exception outside of a thread's entry routine.  And on
some platforms, you cannot propagate an exception out of the "module" (DLL)
that generated the exception.

> Take Java as an extreme example of the use of exceptions.

Java exceptions are not the same thing as C++ exceptions.

In my opinion, Java exceptions are much more useful, and robust, and can be
used in Java situations where one would not use C++ exceptions in analogous
C++.

> What would your alternative be?

Returning error codes from functions.  Even if it is "messy".

> on failure. In this case, exceptions are a great error return, because
> they can store a lot more information than, say, returning an integer
> from a function. In fact, this is *precisely* what exceptions are
> designed for.

An error code from a function does not need to be an int.  The error code
can be an object that can store a lot more information.

> Yes, I know this. This is precisely why I came here to ask about SEH
> in GCC... SEH is a feature in other compilers that I use precisely to
> prevent the bloat. GCC did not provide it, and now I am looking for an
> alternative. You are preaching to the choir, my friend.

C++ has RAII, which is just as useful, and is standard.  No need for an SEH
compiler extension.

I'm glad you found Boost -- amazingly cool C++ enhancers.  :-)

Sincerely,
--Eljay

Note: Ted Byers is spot on.  I would have answered in exactly the same way
as he did, but I doubt I could have answered as eloquently.

Note: Read Herb Sutter's Exceptional C++.
Also, read Herb Sutter & Andrei Alexandrescu's C++ Coding Standards.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 19:09     ` me22
  2008-03-20  5:50       ` Jason Cipriani
@ 2008-03-20 13:30       ` Noel Yap
  2008-03-21  2:27         ` Jason Cipriani
  1 sibling, 1 reply; 23+ messages in thread
From: Noel Yap @ 2008-03-20 13:30 UTC (permalink / raw)
  To: me22; +Cc: Jason Cipriani, gcc-help

On Wed, Mar 19, 2008 at 12:09 PM, me22 <me22.ca@gmail.com> wrote:
> On Wed, Mar 19, 2008 at 3:02 PM, Jason Cipriani
>
> <jason.cipriani@gmail.com> wrote:
>
> >  Does std or boost have some kind of "smart pointers" that let me
>  >  define the cleanup actions without having to write too much additional
>  >  code?
>  >
>
>  Yup, boost::shared_ptr (std::shared_ptr, in C++1x) will let you do that.
>
>  boost::shared_ptr<xmlDoc> doc( xmlReadFile(...), xmlFreeDoc );
>  if (!doc) throw something();

If the pointers never leave the scope, I would use boost::scoped_ptr.
Since shared_ptr keeps a reference count, locks (or atomic operations)
need to be used in order to make it thread safe.

Use shared_ptr iff the ownership needs to be shared.  Use auto_ptr iff
the ownership needs to be transferred and the pointer must never be
copied.  Use scoped_ptr iff the life span of the memory ought to end
when the local scope ends.

Noel

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-20  6:28       ` Jason Cipriani
  2008-03-20 12:24         ` John Love-Jensen
@ 2008-03-20 14:50         ` Ted Byers
  2008-03-21  2:26           ` Jason Cipriani
  1 sibling, 1 reply; 23+ messages in thread
From: Ted Byers @ 2008-03-20 14:50 UTC (permalink / raw)
  To: Jason Cipriani; +Cc: gcc-help

--- Jason Cipriani <jason.cipriani@gmail.com> wrote:
> Thanks for the long response, Ted. Although I
> disagree with most of
> it, I appreciate your time. Some of the points you
> have made are
> slightly unclear:
> 
Just a quick note.  I won't answer all, since Eljay
already did so brilliantly.  I am not sure, though,
why he regards our shared approach to exceptions and
error handling as extremely conservative.

> > Your error
> >  condition, then, doesn't arise.
> 
> I am not sure what you mean. If a file does not
> exist and therefore
> can not be opened, notifying the user of the error
> does not cause the
> file to exist.
> 
But the file the program attempts to open may not
exist because the user had a little typo in the file
name or path he entered.  When he types the file name
and path, before you do anything else, it is dirt
simple to check to see if it exists, and if it
doesn't, prompt the user to correct the file name or
path before you attempt to do anything else.  When you
do this, you prevent any predictable error condition
from happening at all.

Look, C++ has a whole suite of error detection and
handling capability.  To use only exceptions is
somewhat like a carpenter having only a hammer trying
to hammer screws into place.  If all you have is a
hammer, everything looks like a nail.  But if you have
a set of screw drivers in your belt along with your
hammer, you'll use the right tool for the job.

> > User error WRT data
> >  input is common enough that it is much better to
> test
> >  for all errors that you can test for, and prompt
> the
> >  user for corrective action than it is to just
> throw
> >  exceptions willy nilly every time a statement
> doesn't
> >  give you what you wanted.
> 
> Agreed. I don't see what is willy nilly.

You're throwing everywhere.  It is almost as if you
have a bad case of cyber-stomache flu.  You are using
exceptions for flow control.  That is not what they're
there for.  Eljay's brilliant description of return
codes (including especially his smart return code
objects) is a much better option.  And don't forget
assertions have a valid role in many cases also.

> In this
> particular example, I
> have an XML file that contains some data that I need
> to load. The XML
> file must adhere to a very specific structure that
> has been defined
> elsewhere. When the user chooses to load the file,
> the file must
> either be 100% compliant with the defined structure,
> or it will fail
> to load. I have no desire to prompt the user with
> message boxes such
> as "Sorry, but the X data element was missing from
> this data file,
> would you mind telling me what you think the value
> should be so I can
> recover and continue?"

I don't have a quarrel with you there.  Doing that may
well be a stupid thing to do.  But you ought to have
an idea as to what the user CAN do to address the
problem.  Or are you content with leaving the user in
a situation where he can't finish his task because
there is a problem with the data file you need to work
with?  Can the file be repaired?  Regenerated?  What
is the user supposed to do when your program dies just
because of a problem with the data file?  But on the
other hand, maybe the message box you reject out of
hand is the right thing to do because maybe the user
can find the missing data somewhere and enter it.  I
can't tell at this stage because I don't know your
data processing stream.

> A user would not be able to
> answer that
> question in my case anyway. Instead, if the file has
> a slight problem
> (it should not, as the files are not
> hand-generated), I want to notify
> the user that the file could not be loaded. Of
> course, I would provide
> them with a reason why the file could not be loaded,
> but it is not
> always necessary, or even possible, to allow the
> user to intervene and
> correct an error condition themselves. Therefore, in
> the XML example,
> if any one of those operations fails, the entire
> load operation
> *should* fail, and therefore every operation
> warrants an error return
> on failure.
Then design an appropriate suite of error code
objects, and return them.

> In this case, exceptions are a great
> error return, because
> they can store a lot more information than, say,
> returning an integer
> from a function. In fact, this is *precisely* what
> exceptions are
> designed for.
> 
Not quite. They do a lot more than return an error.  I
make extensive use of exceptions and exception
handlers, but you will never find a case in my code
where I use them for flow control.  

At this point, you need to step back and look at the
bigger picture.  The person using your program has a
job to do, and your program ought to be making it
easier for him to do it.  If an error condition
arises, what does he do?  Just give up on getting the
job done?  He'd not last long if he told the boss he
couldn't do his job because your program choked on a
file.  His employer would rightly expect that he do
whatever is necessary to fix the problem and carry on.
 It may well be correct that the user wouldn't have
the knowledge or information required to provide the
detailed data expected at some point in your file, but
you say that the file is not generated manually.  As
the designer of your software, you have to determine
what your user can do to get his job done even when
your program detects a problem in the source data
file.  You have to design your data processing stream
to be fail safe, or at least safe fail, so that what
ever happens, the user can do something to recover and
carry on to finish the job.  If you allow your data
processing stream to be designed in a way that it can
happen that an unrecoverable error can occur, then
your data processing stream is too brittle and needs
to be revised so that the user can recover regardless
of what happens.

I know this isn't always easy, but it is necessary. 
Trust me, I have had to develop software for client's
whose users were secondary school graduates with no IT
experience at all.  They would be baffled at seeing
the kinds of error messages you and I would understand
at a glance.  Stepping out of the mind of a programmer
into the mind of your user is probably one of the
hardest things you'll have to learn to do, but it will
pay dividends.  With some kinds of users, the meanest
thing you can do is abruptly terminate with a brief
error message.  

In some of my programs, I had to go the extra mile and
build in some primitive artificial intelligence, to
guide the user through the process of correcting any
one of a number of problems that could be guaranteed
to arise, using their language, not mine.  Yes, there
are things they can not fix immediately themselves.  

My program would monitor, for example, the hardware
that provided data feeds to the program.  It is
certain that such hardware would fail on occasion. 
The user is not in a position to fill in the missing
data, but my program would a) let them know a problem
had arisen, b) walk them through a process of applying
whatever fixes they COULD try, and if the device
itself had developed a fault, direct them to replace
it (so they can continue to work while the defective
unit is repaired), and they don't find themselves in a
position of putting in several days or weeks on a task
without knowing some piece of hardware had failed. 
That kind of waste or inefficiency can be expensive,
and your clients will bless you or curse you depending
on how well you prevent such waste in the use
/operation of expensive equipment and wasted manpower.

While ensuring errors are always handled, and
consideration is given to what calling code needs to
know, is part of the picture, there is much more to
analysis of possible error conditions, and prediction,
detection and handling of them.  Exceptions have a
role, as do return code objects, and assertions.  But
that is really the small picture that needs
consideration in the context of who the clients and
users are, what they know and what they need to do. 
The software is not complete until you can guarantee
that the user can complete all tasks, that involve use
of the software, that have been assigned by the
client, regardless of what happens.

Trust me, I am not trying to be mean or hard on you. 
I am just trying to pass on a few things you won't
find in the text books, or even in those wonderful
reference books by geniuses like Sutter, or Stroustrup
or Josuttis or Meyers or Lippman.  I can't praise the
books by such authors highly enough, but there is much
learned only by serving people, by developing software
for them, for decades.

In my experience, the vast majority of errors can be
predicted, and therefore prevented, and that means
flow control, and that in turn means primarily error
return code objects.

HTH

Ted

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-20 12:24         ` John Love-Jensen
@ 2008-03-21  2:11           ` Jason Cipriani
  2008-03-21  2:37             ` me22
                               ` (2 more replies)
  0 siblings, 3 replies; 23+ messages in thread
From: Jason Cipriani @ 2008-03-21  2:11 UTC (permalink / raw)
  To: John Love-Jensen; +Cc: GCC-help, Ted Byers

On Thu, Mar 20, 2008 at 8:23 AM, John Love-Jensen <eljay@adobe.com> wrote:
>  In my projects, throwing an exception mean "the application is about to
>  terminate."
>
>  That's using the exception mechanism at the extreme of conservative
>  programming with exceptions.
>
>  Assuming you use exceptions with less draconian policy, the exception
>  mechanism is not for using as normal flow control.  It really means an
>  exceptional situation, outside of the normal flow control.
>
>  For normal flow control -- such as handling predictable, common error
>  conditions -- you should use return codes.

It seems that the root of any disagreement is what kinds of errors
we'd prefer to represent with exceptions. You and Ted would use them
for rare, fatal error conditions, similar in spirit to machine
exceptions such as access violations and invalid instructions. I would
use them for more common errors such as invalid user input, missing
files, network errors, etc. I have a hunch nobody is going to be
having a change of heart any time soon. :-) One thing that I have
often noticed, incidentally, is that the longer a programmer has been
programming before C++, the more they prefer error codes to
exceptions.

One thing I can do, however, is present a strong example of benefits
of using exceptions for more common errors rather than error return
codes. I can think of many such examples but I'll try to construct one
good one. I will warn you ahead of time, this will be a long email
(think of it more as an article, and feel free to comment). :-)

The first example is fairly simple. It becomes more pronounced in
larger applications. Let's say you have a base class that defines an
interface to something, and many derived classes that implement that.
This is a well-known and common situation. Here the example can be a
base interface that describes how to get, say, an image from some
source (Image is an object that can hold an image). For simplicity's
sake we'll say that derived constructors and destructors should not
cause errors, and that all real work should be done by GetImage():

class CImageSource {
public:
  CImageSource ();
  virtual ~CImageSource ();
  virtual void GetImage (Image &img) = 0;
};

Let's say we have 30 different image sources, but here are two of
them. First, using exceptions to represent error conditions:

// This is the base exception we'll be using. It contains a lot of info.
class EBaseException {
   // let these even have a function to display a message box:
   void GUINotify (...); // perhaps this takes some handle to current
window, etc.
   // ... interfaces to private data left out of example ...
private:
   string sourcefile_;
   int sourceline_;
   string message_;
   Severity severity_; // fatal? warning? etc.
   StackTrace trace_; // hypothetical stack trace for sending dump to author.
   // etc...
};

// Provides an image from an HTTP resource.
class CHTTPImageSource {
public:
  void GetImage (Image &) throw (EBaseException)
};

// Provides an image from the FOO-1200 high performance xray vision
// camera, which has a custom set of drivers for talking to it. The
// author of this code assumes no responsibility for misuse of this
// device. ;-)
class CFOO1200ImageSource {
public:
  void GetImage (Image &) throw (EBaseException)
};


You should be getting the idea at this point. Let's say that our
program has a GUI on it. The user has configured the image source
elsewhere. There is a button on the GUI that the user can press to
grab an image from the image source. Pressing that button should
display the image, notifying the user if an error occurs.  Let's say
pressing that button calls OnButton(), which calls
GrabAndDisplayImage() to display the image, again using exceptions:

// In this example ENullPointerException derives from EBaseException, and
// display_ is some thing that displays images. I've left these out. I have also
// used ... in the exception constructor to represent the other things you may
// pass to the hypothetical constructor, __FILE__, __LINE__, whatever.
void UI::GrabAndDisplayImage (CImageSource *source)
  throw (Exception)
{

    Image img;

    if (!source)
      throw ENullPointerException(..., "No image source specified.", ...);

    source->GetImage(img);
    display_->DisplayImage(img);

}

void UI::OnButton () {

  try {
    GrabAndDisplayImage(source_);
  } catch (Exception &x) {
    x.GUINotify();
  }

}


And that's it for the higher level part. The various implementations
of CImageSource throw exceptions containing information about errors
specific to those types of devices. Each CImageSource may even even
define it's own EBaseException subclasses that it can use, the rest of
the application need not be aware of these types. The use of 'image'
in GrabAndDisplayImage() provides for automatic cleanup if any
exceptions are thrown. The button on the GUI does what it needs to do:
grabs and displays the image, notifying the user on error. It is
assumed that the error messages are well-constructed enough to contain
recovery instructions. The code is also exceptionally (pun intended)
clean. There is no duplicated cleanup code, no translation of error
codes from one form to another, no querying of error strings, no
hard-to-read branching. On top of that, every error may have a wealth
of other information associated with it -- information that you could
send to the author as a bug report (functionality that could be taken
care of by EBaseException::GUINotify()), information about the
severity of the error. Logging can be done in the EBaseException
constructor to provide automatic and complete error logging without
modifying any application code. Everything just works. Also the
possibilities are endless. You can take advantage of the exception
hierarchy to provide specific recovery options:

try {
  GrabAndDisplayImage(source_);
} catch (EConfigurationError &x) {
  x.GUINotify();
  DisplayConfigurationGUI();
} catch (EBaseException &b) {
  b.GUINotify();
}

Here, for example, any exception thrown that has derived from
EConfigurationError will cause the code to display a configuration
settings dialog to the user. This provides you with a way to handle
entire classes of errors in similar ways, without caring about the
individual details, and on a per-function basis (for example, in a
batch processing scenario you may just handle EBaseException and not
treat EConfigurationError specially).

I have even seen applications designed where exceptions know a little
bit about how to recover from an error. For example:

class EBaseException {
public:
  virtual void Recover () { }
};

class EConfigurationError : public EBaseException {
public:
  virtual void Recover () { DisplayConfigurationGUI(); }
};

And so on and so forth. That is the magic of exceptions.

Now, let's say we do not want to use exceptions. Instead we are going
to use error codes here. Everything that was done with exceptions
above is also entirely possible using error codes. It's not a matter
of what you can and can't do. However, a lot of extra coding will be
required to emulate all the features of exceptions using error codes.
So, revisit the CImageSource, a la error codes:

class CImageSource {
public:
  CImageSource ();
  virtual ~CImageSource ();
  virtual int GetImage (Image &img) = 0;
};

Here, GetImage() will return an integer error code. The first thing
you may notice is there are no messages inherently associated with
error returns. You can now take two approaches, both with issues. The
first approach:

// Error codes; or you could use const int, enum, whatever you prefer:
#define IMGSRCERR_OK  0
#define IMGSRCERR_NULL_POINTER  1
// Etc...

class CImageSource {
  ...
  // given an error code, return a string
  static string ErrorStr (int errcode);
  ...
};

A few things you should immediately notice. First of all, in order for
ErrorStr() to work properly, all error codes must be unique.
Therefore, either individual subclasses must be coded with awareness
of the other error values in mind (so no conflicts are created for
device-specific error codes), or, alternatively, some very general
broad set of common errors must be defined that any device-specific
errors would fall into (for example, IMGSRCERR_NOTREADY if an image
source device was "not ready", which doesn't necessarily apply to all
sources anyway). The issue with the former is maintenance, although if
you can keep it up, then it's a reasonable solution. The issue with
the latter is vagueness. One common solution I have seen to these
problems is to use, say, 32-bit error codes where the high 16-bits are
some sort of general "class" and the low 16-bits are a specific error
code. In any case, to avoid the vagueness, the implementation of
ErrorStr must be aware of all of the subclasses and their error codes.
This has already complicated things over using exceptions, where this
requirement does not exist.

I mentioned that you could take two approaches to associating strings
with error codes. The second approach is this:

class CImageSource {
  ...
  // Given a source-specific, return a string.
  virtual string ErrorStr (int errcode);
  ...
};

In that approach, each subclass is responsible for defining it's own
error codes and strings. The base implementation could return a
generic message, as well, so unknown codes could be deferred to the
base:

string CHTTPImageSource::ErrorStr (int errcode) {
  string msg;
  if (errcode is known)
    msg = the message;
  else // let base handle unknown/commong messages
    msg = CImageSource::ErrorStr(errcode);
  return msg;
}

This solves most of the problems above, but introduces a different
collision problem where different error codes may mean entirely
different things for different devices. Again, unless each subclass is
aware of the other subclass's error codes, you will have issues. An
example of the issues here is a function that grabs an image from two
sources and does something with them, where you want the error
handling mechanism to be defined by the caller. First with the error
code interface above:

int HandleImages (CImageSource *a, CImageSource *b) {

  Image x, y;
  int err;

  err = a->GrabImage(x);
  if (err != IMGSRCERR_OK)
    return err;

  err = b->GrabImage(y);
  if (err != IMGSRCERR_OK)
    return err;

  DoStuff(x, y);

  return IMGSRCERR_OK;

}

And now if the caller wants to display a message box, well... using
the static ErrorStr as above it could do this:

  int err = HandleImages(a_, b_);
  if (err != IMGSRCERR_OK)
    DisplayError(CImageSource::ErrorStr(err));

If you chose the virtual ErrorStr() interface above, you're outta
luck; you either have to return the error string itself from
HandleImages() or somehow pass information back about which caused the
error.

Using exceptions, of course:

void HandleImages (CImageSource *a, CImageSource *b) {

  Image x, y;

  a->GrabImage(x);
  b->GrabImage(y);

  DoStuff();

}

And to take appropriate action:

  try {
    HandleImages(a_, b_);
  } catch (Exception &x) {
    x.GUINotify();
  }

Using exceptions, every problem above has been avoided, and the code
has a much higher ratio of work to error-checking as well.

It actually gets worse than that in this example. One thing that you
may have noticed above is that no checks for errors in DoStuff() are
performed. Using error codes, your HandleImages() function must check
the return value of DoStuff() and pass it back to the caller. This now
multiplies the set of problems described above:

 - HandleImages() must either return an error string or return info
about which of the 2 GetImage calls and DoStuff returned an error.
 - DoStuff()'s implementation must be aware of the CImageSource
subclasses so that it's error codes do not overlap.
 - Another way of mapping DoStuff() error codes to strings must be
present (if using static ErrorStr the obvious solution to this is to
make ErrorStr be a global function, not a CImageSource static member,
of course -- the problems still exist, though).

On the other hand, using exceptions, the code to handle DoStuff()
errors is *identical* to the code above. DoStuff() throws an
exception, the caller catches it and takes the appropriate action. You
do not need to think about any of these difficulties. You do not need
to add any extra handling logic. Everything just works.

There is another solution to the HandleTwoImages() problem above that
I sometimes see implemented. This solution is to allow each
CImageSource (for example) to define it's own error codes. Then,
define a global set of error codes, some of which duplicate the
meaning of the errors defined by each individual CImageSource but have
different values. Finally, provide a function to map specific error
codes to global ones. You may think this sounds crazy, but this is
actually done very frequently, although generally not for error codes
within an application itself. Consider the actual implementation of
CFOO1200ImageSource, for example. Perhaps the FOO-1200 drivers were
written in C, and all functions return error codes. You have no
control over the values of these error codes, but you are interested
in their specific meanings (for example, the FOO-1200 API defines it's
own error code for "file not found", etc.) in that you want to display
the correct error message to the user. If you want to handle specific
driver error codes as special cases, you must map the driver error
codes to equivalent error codes in your application. In the most
unfortunate case, the driver may even provide an API call to retrieve
the last error string -- however you do not get to take advantage of
this function as you must pass application error codes back to the
application.

Using exceptions can give you a big advantage here. If the FOO-1200
drivers do not provide a driver error -> string function then no,
exceptions will not make the special case handling any more convenient
-- you must still convert driver error codes to exception message
strings per-error. However, if the FOO-1200 drivers do provide error
strings, handling errors with exceptions is as easy as using the
string returned from the drivers in some EFOO1200DriverException. No
special case handling necessary, you do not need to be aware of error
codes at all, there is no transformation from FOO-1200 error space to
application error space, there is no massive lookup table in the
application's ErrorStr() function (for example), there is nothing.
Those are all problems that may come into play specifically when
implementing CHTTPImageSource and CFOO1200Source, and the others. With
exceptions, you can use the minimal amount of device/library-specific
error code -> exception conversion at the lowest level, and after
that, it all just works.

Returning to GrabAndDisplayImage; the new implementation using error
codes (compare this to above implementation using exceptions):

int UI::GrabAndDisplayImage (CImageSource *source) {

    Image img;
    int ret;

    if (!source)
      return IMGSRCERR_NULL_POINTER;

    ret = source->GetImage(img);

    if (ret != IMGSRCERROR_OK)
      ret = display_->DisplayImage(img);

    return ret;

}

void UI::OnButton () {

  int err = GrabAndDisplayImage(source_);

  if (err != IMGSRCERR_OK)
    DisplayError(ErrorStr(err));

}


It's still easy to read, sure, in this simple example. However, it
suffers from every problem described above (and the same collision vs.
vagueness issues now come into play in the implementation of
DisplayImages() as well) -- the amount of thought and care that
actually had to go in to selecting error codes and implementing
ErrorStr() and friends in this case is incredible.

Also something you mat notice. GrabAndDisplayImage now returns
IMGSRCERR_NULL_POINTER if source is NULL. The ErrorStr() function
likely changes this to a message such as "NULL parameter to function".
It must, in order to cover the general case of IMGSRCERR_NULL_POINTER.
The implementation with exceptions, however, threw an
ENullPointerException (thus identifying the general error type) but
with a specific, meaningful message that makes sense in context ("No
image source specified"). You could change the error return code to
IMGSRCERR_NO_SOURCE_SPECIFIED if you want; but then you have lost the
information that it is an error related to NULL parameters being
passed to a function. You could also check for IMGSRCERR_NULL_POINTER
and display the appropriate message in OnButton() -- but that only
works if IMGSRCERR_NULL_POINTER can't be returned for any other
reason. With error return codes, there is no straightforward way to
associate context-specific messages with general errors. You must take
all this into consideration when using error return codes, or else you
start giving the user messages like "Error: I/O error". With
exceptions, you do not have to put any of this kind of thought into
it.

Thus concludes that example. I may have left something out, but I feel
I have said enough to make my point clear. I would not argue whether
or not you "should" use exceptions or "should" use error codes -- I am
a major proponent of using whatever tool is most appropriate, most
convenient, and most familiar to get a job done adequately. Proper
error handling is possible with both methods. Also the above examples
are not the only options -- there are many other ways of expressing
the same things. I wanted to present a relevant example with the hope
of showing that preferring exceptions over error codes can make life a
lot simpler while coding.

There is another example I want to point out, much more briefly: One
thing that you can not do with error codes is return errors from class
constructors. In some cases, this can lead to more complex invariants
on classes and therefore more complex logic to determine the state an
object is in. For example, let's see we have a class ImageBuffer that
can hold some data. Using exceptions, I can do this:

class OutOfMemoryError : public BaseException { ... }
class InvalidDimensionError : public BaseException { ... }

class ImageBuffer {
public:
  // allocates buffer of appropriate size
  ImageBuffer (unsigned w, unsigned h)
    throw (OutOfMemoryError, InvalidDimensionError);
  ~ImageBuffer ();
};

Using exceptions allows me to abort construction of an object on
error. Therefore it is now possible for me to say "if an ImageBuffer
exists, it encapsulates a valid block of memory of the appropriate
size... period." It then becomes reasonable for me to assume that if I
have an ImageBuffer I can use it safely:

void ProcessImages ()
  throw (BaseException)
{

  ImageBuffer a(1000, 1000);
  ImageBuffer b(1000, 1000);

  // do stuff

}

I do not need to verify the state of the ImageBuffers in
ProcessImages. Rather, as long as the caller is handling exceptions
appropriately, everything is taken care of if one of the memory
allocations fails. John Love-Jensen and Ted Byers may likely state
that this is a good example of using exceptions to handle a rare,
fatal -- that is, a memory allocation failure. That is true, but keep
in mind the memory allocation error is used as an example, perhaps a
constructor loads data from a file and throws an exception if the file
does not exists (e.g.) -- this example applies to all of those cases
of "predictable, common errors" as well.

On the other hand, if I am to avoid exceptions I can no longer state
the simple invariant that "if an ImageBuffer exists it is valid". Now,
an ImageBuffer may exist but not be valid. I may implement it like
this:

class ImageBuffer {
public:
  // possibly allocate buffer of appropriate size
  ImageBuffer (unsigned w, unsigned h);
  ~ImageBuffer ();
  int LastErrorCode () const;
};

Now I must jump through the same hoops as in the CImageSource example
above, and additionally I must always check the state of ImageBuffers
(for example, if ImageBuffer has some member function like
RotateImage(), it can not assume valid data, it must check to ensure
that at least some conditions are true before doing it's thing):

int ProcessImages () {

  ImageBuffer a(1000, 1000);
  ImageBuffer b(1000, 1000);
  int e;

  if ((e = a.LastErrorCode()) != 0)
    return e;
  if ((e = b.LastErrorCode()) != 0)
    return e;

  // do stuff

}

Exceptions handle all this logic for you.

>  One way to have more "in your face" return codes, is to return "smart return
>  code objects".
>
>  A smart return code object has this kind of behavior:
>  + wraps a return code
>  + if the caller does read (query) the return code object,
>   THEN the object marks itself as having been checked
>   AND the object's destructor is silent
>  + if the caller does not read (query) the return code object,
>   AND the return code object does NOT have an error status,
>   THEN the object's destructor is silent
>  + if the caller does not read (query) the return code object,
>   AND the return code object DOES have an error status,
>   THEN the object's destructor does "something"
>   Where "something" is one or more of...
>   + an assert
>   + a trace to a log
>   + throw an exception

:-) I hope that you realize how close to a properly designed exception
class this "smart return code" is. See my example above for the
parallels. The advantage that exceptions as used above have over the
"smart return code" you have defined here is they do not suffer from
the collision vs. vagueness problem that I described above. Using
"smart return codes" still requires unique and meaningful error
returns to be defined.

Additionally, your description of a "smart return code" has a lot of
special logic in it. I see a lot of +, AND, and THEN in there. None of
that is necessary when using exceptions. Construct the exception and
throw it. If an exception is caught it was an error.

Most of the logic in your smart return code seems to allow for the
propagation of the error code up the call chain until it is able to be
handled. Exceptions handle this implicitly. Consider:

void SomeFunction ()
  throw (SomeException)
{
  try {
    AnotherFunction();
  } catch (...) {
    throw;
  }
}

It does not matter how deep in the call stack the original exception
originates from. Eventually it will be thrown out of
AnotherFunction(). SomeFunction() then has the option to catch it,
perform it's own part of the recovery process, and then continue
propagating the error upward until it is resolved. SomeFunction() need
not even rethrow the exception -- if SomeFunction() has enough
information to recover from the error completely, it can stop it right
there and continue on it's way.

>  I consider a "smart return code object" to be training wheels for result
>  codes.  But for larger teams, they become very helpful.

The smart return code object as you have defined it could very well be
training wheels for result codes. Proper exceptions provide far more
functionality than smart return codes and error codes. The smart
return code is, in fact, a sort of learning-disabled sibling of an
exception. I mean that in a humorous way. For larger teams, smart
return codes become helpful. Similarly, exceptions become even more
helpful, as they provide all of the benefit of the smart return code,
plus some.

To be honest, while I am not setting out to convince you of one way or
another, I do want to point out that it seems to me that you do, in
fact, see the merit of using exceptions for common, predictable
errors. However, for whatever reason, you are hiding this behind the
"smart return code" mask -- I am guessing that the reason is because
your philosophy is to only use exceptions on fatal errors, but that
exceptions do provide elegant solutions to non-fatal problems, except
to use exceptions directly would violate the philosophy (and so you
have made your own constructs with closely related functionality). I
think that you would have a lot to gain by beginning to favor proper
exceptions over "smart return codes". They're almost the same thing!

For the most part, everything you go on to say is completely
reasonable and/or I addressed it above, although I do have some minor
comments:

>  > And what do you consider to be unpredictable and rare?
>
>  Any violation of the routine's contractual requirements.
>
>  For example, if an int parameter must have the value of 0, 1 or 2, and the
>  value passed in is something other than 0, 1 or 2.
>
>  Any violation of the routine's contractual obligations.
>
>  For example, if the routine must put the object in state A or B, but was
>  unable to do so and the object is in state C (a "never happen" situation).
>
>  For example, if the object is in an inconsistent state upon return, such
>  that the object's invariance is violated.  This can happen if a state change
>  cannot be completed as a transaction, and enough has happened such that the
>  state of the object cannot be reverted.  (That's why have transaction based
>  assignment operator -- perhaps using the swap paradigm -- is so very
>  useful.)

Using exceptions does not make the swap idiom less useful; it provides
implicit exception safety in the same way it provides good error
return safety.

>  Any "never happen" situations.  (Where, in the case of my applications,
>  keeping in mind that throwing an exception means "terminate the application,
>  forthwith".  So throw an exception where it is appropriate to terminate the
>  application works as a rule-of-thumb in my application.  Your exception
>  usage policy will likely be less extreme.)
>
>  Any "is it okay if the processing continues (without throwing or returning
>  an error code) with the detected 'broken axle' condition?"  If it's not
>  okay, then either throw or return an error code.
>
>
>  > If I was writing code to parse that, and found "root", then
>  > "something", then "value", but not "data", I would call that a rare...
>
>  I presume you are speaking of the "meta-layer" (the decision making layer)
>  above the parser itself.
>
>  In my application, ponder "is this failure such that the application should
>  be terminated ... or can it be handled in some reasonable failsafe fallback
>  and the application continue running?"

In the case of the XML example I gave, let's say that not finding the
"data" node was not fatal to the application. Perhaps the user
attempted to load a file from a menu in a GUI and that failed. In this
case the reasonably failsafe fallback is to notify the user of
precisely the condition that caused the error. Nothing else can be
done no matter what the error: the user must load a different file or
somehow fix the offending document with other means. Throwing an
exception with a descriptive error message from the source of the
error, then propagating it up the call chain until something can
handle it appropriately (in this case, by displaying the message in a
message box to the user), handles this elegantly, no matter what the
source of the error. Of course this all relies on constructing
exceptions with useful information in them to begin with.

One important point that this brings to mind is; poorly designed
exceptions will gain you *nothing* over error return codes. In fact,
you may lose something. Take, for example, the Something()'s in my
first message. If used as-is, with no parameters, and if all error
conditions throw a Something(), then really, what can you do with
that? The most you can do is display a message that says "Somewhere,
at some point, some error occurred". That is unacceptable. Exceptions
in themselves do not gain you anything, but exceptions give you a
great framework for simple and powerful error handling and recovery.
Just with any other language construct, "improper" use will render its
potential benefits meaningless.

>  Also useful is writing your own throw handler such that if a throw happens
>  it forks the application and core dumps the child (useful to have
>  "core.<PID>" files enabled).  That's how rarely I expect an exception to
>  occur, even when using exceptions with less draconian policies than
>  "terminate the application, forthwith".  And with a core you have a good
>  snapshot of the application and where things went awry.

Indeed. However, I want to point out that this can also be
accomplished while still using exceptions for lesser errors. Take, for
example, the EBaseException above, which contains some information
about the severity of the error. It would be straightforward to flag
exceptions that should "terminate and dump core" as such. Or when
using the "Recover()" interface that I had mentioned, perhaps you have
one of these:

class EFatalError : public EBaseException {
public:
  virtual void Recover () {
    // ... terminate and dump core ...
  }
}

Of course this makes "Recover" a bad name for the function ;-) let's
call the function "Handle()" instead. Now any exception deriving from
EFatalError could cause the application to terminate, if you choose to
structure your application this way. You can also make the default
implementation of "Handle()" simply display a message box, and now you
have this elegant solution:

void DoSomething () {
  try {
    SomethingThatCouldThrow();
  } catch (EBaseException &x) {
    x.Handle();
  }
}

And that code displays a message box if appropriate, or dumps core if
appropriate, or whatever Handle() does for the exception that was
thrown.

>  Also, once you start using exceptions, you have to be careful too.
>  Exceptions cannot be thrown through a C barrier.  That means that you cannot
>  propagate an exception through an OS callback or other C callbacks.  You
>  cannot propagate an exception outside of a thread's entry routine.  And on
>  some platforms, you cannot propagate an exception out of the "module" (DLL)
>  that generated the exception.

All of this is entirely true. It's a problem that I am familiar with.
There are clever ways to pass exceptions around these boundaries, that
I have used. Error return codes can be passed across such boundaries
with much less of an issue. However, perhaps it is the nature of our
respective work, but I find that the cases where I run into these
boundaries are so relatively rare, that I can not justify them as
reasons to sacrifice the other benefits of exceptions in an
application.

In the case of the C barrier, your only real choice is to keep the
errors as error codes at the lowest level, and only translate to
exceptions on a higher level once you back on the C++ side. This is
not always possible, of course, but some things can make it very easy:
for example, if whatever C API you are using allows your callback
function to return an error code. If the callback in question can
cause some sort of status to eventually be returned to the C++ side of
things, then you can deal with throwing exceptions there. If the
callback can not cause some sort of status to be returned, you are out
of luck -- although IMHO the problem here is more of a shortcoming of
the API you are using. This flavor of solution is also necessary when
the module or threaded code is in C as well.

For thread entry functions and DLLs, there many solutions as well.
Unfortunately you do have to code with awareness of these boundaries
in mind. You could keep errors as error codes on the other side of the
boundary, throwing exceptions only when able. This is what you would
be doing anyways if you were not using exceptions -- you would be
using error codes. Also exceptions are objects just like any other, in
the past I have had a lot of success having threads that must throw
exceptions out of the thread handler store the exception and do no
further processing, and at the next available opportunity pass that
exception object to whatever thread *can* handle it, which then
proceeds to throw the exception (another solution is to have the
thread return a pointer to a new exception object, and defer handling
until the thread terminates) -- in any case error handling in threads
and DLLs using exceptions has all of the same design caveats as
passing any other complex data around.

How you get across these boundaries really does depend on the
situation. Again you have to use the right tools for the job. You can
not throw exceptions out of thread handlers, and so error codes will
help you out more there, at least until the error gets to the point
where you can throw.

>  > Take Java as an extreme example of the use of exceptions.
>
>  Java exceptions are not the same thing as C++ exceptions.
>
>  In my opinion, Java exceptions are much more useful, and robust, and can be
>  used in Java situations where one would not use C++ exceptions in analogous
>  C++.

Can you think of an example that supports this? I do not think that is
a true statement. I think it is more accurate to say that because
exceptions were an important part of Java to begin with, much of the
Java language and APIs has been designed with them in mind, and
therefore they are useful because they are used more frequently and
more effectively. Java exceptions give you nothing that C++ exceptions
do not have. The major problem with C++ exceptions is that they are
frequently used ineffectively -- not that they must necessarily be
ineffective.

For example, Java strictly enforces the throws() clause of a function.
It has done so from the start. Therefore the throws() clause is a very
important part of a a function in Java, it carries a lot of meaning.
In C++, it is very rare to find a compiler that even *attempts* to
have some support for the throw() clause. I use it only as
documentation, basically. Some C++ compilers even issue warnings along
the lines of "throw() clause ignored" when you have it. This is one
example of many reasons why a programmer might construct more useful
exceptions in Java than in C++. Java is a very exception-oriented
language. C++ supports them but the language itself doesn't really
encourage their use in any specific way.

Therefore I feel that many C++ programmers do not understand the
benefits of using exceptions, and proper techniques for effective use,
as well as a Java programmer might. On the other hand, C++ exceptions
do give you that same type of functionality. One feature that I have
always desired the most in a C++ compiler is strict enforcement of
throw() clauses.

Java exceptions are not inherently "better" than C++ exceptions --
rather, exception design in Java should be used as a reference model
for a C++ programmer who also wishes to use exceptions effectively.

>  > on failure. In this case, exceptions are a great error return, because
>  > they can store a lot more information than, say, returning an integer
>  > from a function. In fact, this is *precisely* what exceptions are
>  > designed for.
>
>  An error code from a function does not need to be an int.  The error code
>  can be an object that can store a lot more information.

If you start returning objects from functions on errors, you end up
emulating the functionality of exceptions, but with a different
syntax. Additionally, checking return types of functions rather than
throwing an exception (which automagically branches to a catch, and
also automagically is propagated up the call chain) ends up requiring
error handling logic that can be avoided by using exceptions. Even
more importantly, this begins to interfere with normal function return
values. For example, with exceptions:

  int QueryA () throw (Exception);
  double QueryB () throw (Exception);

These are easy to use, of course. The function declarations are easy
to read and understand. Calling them is simple. It is very clear.
Handling errors is also very clean:

  int a;
  double b;

  try {
    a = QueryA();
    b = QueryB();
  } catch (Exception &x) {
    // handle
  }

The beauty is in the simplicity. You can use the return value from
functions to pass info back. You can catch thrown exceptions to handle
errors. As soon as you start returning error objects, you have to
start adding unnecessary complexity. Even small, small amounts of
complexity, such as this and all it entails:

  ErrorInfo QueryB (double &s); // returns error info

Are unnecessary when using exceptions.

>  > Yes, I know this. This is precisely why I came here to ask about SEH
>  > in GCC... SEH is a feature in other compilers that I use precisely to
>  > prevent the bloat. GCC did not provide it, and now I am looking for an
>  > alternative. You are preaching to the choir, my friend.
>
>  C++ has RAII, which is just as useful, and is standard.  No need for an SEH
>  compiler extension.
>
>  I'm glad you found Boost -- amazingly cool C++ enhancers.  :-)

It's true. There is some really awesome stuff here.

>
>  Sincerely,
>  --Eljay
>
>  Note: Ted Byers is spot on.  I would have answered in exactly the same way
>  as he did, but I doubt I could have answered as eloquently.
>
>  Note: Read Herb Sutter's Exceptional C++.

Got it right here on the shelf, finished. :-)

>  Also, read Herb Sutter & Andrei Alexandrescu's C++ Coding Standards.

I will check this out. I actually might have an eBook version of it
laying around that I have been meaning to look over.

Well, that concludes this week's article.

Jason

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-20 14:50         ` Ted Byers
@ 2008-03-21  2:26           ` Jason Cipriani
  0 siblings, 0 replies; 23+ messages in thread
From: Jason Cipriani @ 2008-03-21  2:26 UTC (permalink / raw)
  To: Ted Byers; +Cc: gcc-help

Ted, I can't respond to everything right now, because I somehow
managed to spend a full hour typing that last email. Hopefully some of
what you say here, I have addressed there. There are two things in
particular that I want to point out, though:

The first:

On Thu, Mar 20, 2008 at 10:49 AM, Ted Byers <r.ted.byers@rogers.com> wrote:
>  Look, C++ has a whole suite of error detection and
>  handling capability.  To use only exceptions is
>  somewhat like a carpenter having only a hammer trying
>  to hammer screws into place.  If all you have is a
>  hammer, everything looks like a nail.  But if you have
>  a set of screw drivers in your belt along with your
>  hammer, you'll use the right tool for the job.

I believe I addressed this in my recent post. Using *only* exceptions
would be a mistake. However, using error codes when exceptions can
make things a lot simpler is also a mistake. The same goes both ways,
"to use only error codes" fits into that analogy as well. There is a
happy medium, and there are some situations where exceptions are more
appropriate, and some where error codes are more appropriate. I take
the approach of using exceptions unless I am forced to do something
else, for example, the following situations:

1) Where performance is critical and exceptions have too much
overhead. For example, if I was writing, say, a function that
intersected a ray with a cylinder to use in a ray tracer
implementation (or whatever), I would certainly not have that function
thrown an exception if the ray did not intersect.

2) Where exceptions simply can't be used, such as in the boundary
conditions mentioned above (especially across C boundaries). In this
case, error codes are the best tool, and eventually they can be used
to throw exceptions at a higher level if that is appropriate.

I do not support using only exceptions. I also do not support using
only error codes. In college, my friends used to call me Jason "Use
the Right Tool For the Job" Cipriani. Well, maybe not. My original
question about SEH was in a situation where SEH was a great tool for
what I was trying to do. The book I just wrote about exceptions is
intended only to show a good example of when exceptions are a better
tool than error codes.

The second thing is:

>  You're throwing everywhere.  It is almost as if you
>  have a bad case of cyber-stomache flu.

LOL. :-) It's better than cyber-diarrhea I suppose.

> You are using
>  exceptions for flow control.

I need to address this because it is not correct. I am using
exceptions for *error handling* flow control. All error handling
involves some sort of trivial flow control. However, do not confuse
this with using exceptions for things like this:

void DoSomething (vector<int> &data) {

  int i = 0;

  try {
    while (true) {
      DoSomethingElse(data[i]);
      ++ i;
      if (i >= data.size()) throw int;
    }
  } catch (...) { }

  // do more stuff

}

NO. DO NOT DO THAT! (heh). That is *not* what exceptions are for at
all. Exceptions are for error conditions that must result in special
handling. I hope that I haven't given an example otherwise -- if I
have I didn't intend for it to look that way.

> That is not what they're
>  there for.  Eljay's brilliant description of return
>  codes (including especially his smart return code
>  objects) is a much better option.  And don't forget
>  assertions have a valid role in many cases also.

Assertions are good for stating assumptions; they notify you when your
assumptions are invalid and also double as documentation when reading
code. However, doing something like this:

FILE *f = fopen("file", "r");
assert(f);

Is *not* a good use for assert(), for hopefully obvious reasons. I
used fopen() as an arbitrary example, substitute your favorite
function instead.


I have read the rest of your reply, but I can not respond to it right
now. I apologize. But I did read it. Thanks.

Jason

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-20 13:30       ` Noel Yap
@ 2008-03-21  2:27         ` Jason Cipriani
  0 siblings, 0 replies; 23+ messages in thread
From: Jason Cipriani @ 2008-03-21  2:27 UTC (permalink / raw)
  To: Noel Yap; +Cc: me22, gcc-help

On Thu, Mar 20, 2008 at 9:30 AM, Noel Yap <noel.yap@gmail.com> wrote:
>
> On Wed, Mar 19, 2008 at 12:09 PM, me22 <me22.ca@gmail.com> wrote:
>  > On Wed, Mar 19, 2008 at 3:02 PM, Jason Cipriani
>  >
>  > <jason.cipriani@gmail.com> wrote:
>  >
>  > >  Does std or boost have some kind of "smart pointers" that let me
>  >  >  define the cleanup actions without having to write too much additional
>  >  >  code?
>  >  >
>  >
>  >  Yup, boost::shared_ptr (std::shared_ptr, in C++1x) will let you do that.
>  >
>  >  boost::shared_ptr<xmlDoc> doc( xmlReadFile(...), xmlFreeDoc );
>  >  if (!doc) throw something();
>
>  If the pointers never leave the scope, I would use boost::scoped_ptr.
>  Since shared_ptr keeps a reference count, locks (or atomic operations)
>  need to be used in order to make it thread safe.
>
>  Use shared_ptr iff the ownership needs to be shared.  Use auto_ptr iff
>  the ownership needs to be transferred and the pointer must never be
>  copied.  Use scoped_ptr iff the life span of the memory ought to end
>  when the local scope ends.

Thanks for the tips!

Jason



>
>  Noel
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-21  2:11           ` Jason Cipriani
@ 2008-03-21  2:37             ` me22
  2008-03-21  2:45               ` Jason Cipriani
  2008-03-21 15:24             ` Noel Yap
  2008-03-21 22:50             ` Brian Dessent
  2 siblings, 1 reply; 23+ messages in thread
From: me22 @ 2008-03-21  2:37 UTC (permalink / raw)
  To: gcc-help

On Thu, Mar 20, 2008 at 10:10 PM, Jason Cipriani
<jason.cipriani@gmail.com> wrote:
> On Thu, Mar 20, 2008 at 8:23 AM, John Love-Jensen <eljay@adobe.com> wrote:
>  >  In my projects, throwing an exception mean "the application is about to
>  >  terminate."
>  >
>  >  That's using the exception mechanism at the extreme of conservative
>  >  programming with exceptions.
>  >
>  >  Assuming you use exceptions with less draconian policy, the exception
>  >  mechanism is not for using as normal flow control.  It really means an
>  >  exceptional situation, outside of the normal flow control.
>  >
>  >  For normal flow control -- such as handling predictable, common error
>  >  conditions -- you should use return codes.
>
>  It seems that the root of any disagreement is what kinds of errors
>  we'd prefer to represent with exceptions. You and Ted would use them
>  for rare, fatal error conditions, similar in spirit to machine
>  exceptions such as access violations and invalid instructions. I would
>  use them for more common errors such as invalid user input, missing
>  files, network errors, etc. I have a hunch nobody is going to be
>  having a change of heart any time soon. :-) One thing that I have
>  often noticed, incidentally, is that the longer a programmer has been
>  programming before C++, the more they prefer error codes to
>  exceptions.
>

Personally, I think that if something can't happen, like a divide by
zero, is a case for an assert, not an exception.  Leave them in the
release if you want, but better, fix them!

I prefer using exceptions for things that you'd rather never happened,
and if everything does perfectly, never will happen.  (Which also
means that their performance doesn't matter.)  Especially when those
are the kinds of things that can rarely be handled locally.  I like
having a fairly clean code path.

Also, exceptions thrown in constructors mean tighter invariants that
you just can't get with return codes.  2-phase construction is evil.

> [snip exceptionally long post that I didn't actually read, sorry :P ]
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-21  2:37             ` me22
@ 2008-03-21  2:45               ` Jason Cipriani
  0 siblings, 0 replies; 23+ messages in thread
From: Jason Cipriani @ 2008-03-21  2:45 UTC (permalink / raw)
  To: me22; +Cc: gcc-help

On Thu, Mar 20, 2008 at 10:36 PM, me22 <me22.ca@gmail.com> wrote:
> On Thu, Mar 20, 2008 at 10:10 PM, Jason Cipriani
>
> <jason.cipriani@gmail.com> wrote:
>
> > On Thu, Mar 20, 2008 at 8:23 AM, John Love-Jensen <eljay@adobe.com> wrote:
>  >  >  In my projects, throwing an exception mean "the application is about to
>  >  >  terminate."
>  >  >
>  >  >  That's using the exception mechanism at the extreme of conservative
>  >  >  programming with exceptions.
>  >  >
>  >  >  Assuming you use exceptions with less draconian policy, the exception
>  >  >  mechanism is not for using as normal flow control.  It really means an
>  >  >  exceptional situation, outside of the normal flow control.
>  >  >
>  >  >  For normal flow control -- such as handling predictable, common error
>  >  >  conditions -- you should use return codes.
>  >
>  >  It seems that the root of any disagreement is what kinds of errors
>  >  we'd prefer to represent with exceptions. You and Ted would use them
>  >  for rare, fatal error conditions, similar in spirit to machine
>  >  exceptions such as access violations and invalid instructions. I would
>  >  use them for more common errors such as invalid user input, missing
>  >  files, network errors, etc. I have a hunch nobody is going to be
>  >  having a change of heart any time soon. :-) One thing that I have
>  >  often noticed, incidentally, is that the longer a programmer has been
>  >  programming before C++, the more they prefer error codes to
>  >  exceptions.
>  >
>
>  Personally, I think that if something can't happen, like a divide by
>  zero, is a case for an assert, not an exception.  Leave them in the
>  release if you want, but better, fix them!
>
>  I prefer using exceptions for things that you'd rather never happened,
>  and if everything does perfectly, never will happen.  (Which also
>  means that their performance doesn't matter.)  Especially when those
>  are the kinds of things that can rarely be handled locally.  I like
>  having a fairly clean code path.
>
>  Also, exceptions thrown in constructors mean tighter invariants that
>  you just can't get with return codes.  2-phase construction is evil.
>
>  > [snip exceptionally long post that I didn't actually read, sorry :P ]

That's OK :-) I don't think I actually read it, either!

I did actually specifically mention that throwing exceptions from
constructors leads to tighter invariants. I think you pretty much said
everything I said in that last post, but a lot more efficiently.

Jason


>  >
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-21  2:11           ` Jason Cipriani
  2008-03-21  2:37             ` me22
@ 2008-03-21 15:24             ` Noel Yap
  2008-03-21 22:50             ` Brian Dessent
  2 siblings, 0 replies; 23+ messages in thread
From: Noel Yap @ 2008-03-21 15:24 UTC (permalink / raw)
  To: Jason Cipriani; +Cc: John Love-Jensen, GCC-help, Ted Byers

On Thu, Mar 20, 2008 at 7:10 PM, Jason Cipriani
<jason.cipriani@gmail.com> wrote:
>  It seems that the root of any disagreement is what kinds of errors
>  we'd prefer to represent with exceptions. You and Ted would use them
>  for rare, fatal error conditions, similar in spirit to machine
>  exceptions such as access violations and invalid instructions. I would
>  use them for more common errors such as invalid user input, missing
>  files, network errors, etc.

I would use them for network errors.  Depending upon why files may be
missing, I may or may not use them for that purpose.  I would
definitely not use them for invalid user input.

I suppose where I would draw the line is whether or not the system has
control over the inputs in question.  For example, if a file is
expected to exist because another system or a user is supposed to
create the file, exceptions shouldn't be used.

I will say, though, that this is how I use exceptions in C++.  I'm
much more lax with exception usage in languages like Python and
possibly Java (I don't have that much experience with Java, but I did
use exceptions for flow control in order to implement an ISO-8601
parser whose grammar seemed to require some lookahead, but I may just
have lacked the skills to eliminate this (mis)use of exceptions).

Noel

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-21  2:11           ` Jason Cipriani
  2008-03-21  2:37             ` me22
  2008-03-21 15:24             ` Noel Yap
@ 2008-03-21 22:50             ` Brian Dessent
  2008-03-21 23:14               ` Noel Yap
  2 siblings, 1 reply; 23+ messages in thread
From: Brian Dessent @ 2008-03-21 22:50 UTC (permalink / raw)
  To: Jason Cipriani; +Cc: John Love-Jensen, GCC-help, Ted Byers

Jason Cipriani wrote:

> One feature that I have
> always desired the most in a C++ compiler is strict enforcement of
> throw() clauses.

Since you seem to be well aware of the design of C++ exceptions you
should realize why this is a near impossibility.  In order for the
compiler to enforce a throw declaration, it would have to have complete
knowledge of the entire outbound call graph of the function.  Two things
stand in the way of this: separate compilation and function
pointers/virtual functions.  The problem of separate compilation (i.e.
that the compiler only has local knowledge of a given translation unit
at any time) can somewhat be dealt with by using gcc --combine, but
that's more of a crutch.  It won't fix the issue that necessitated
separate compilation in the first place: the explostion of complexity as
the size of one "unit" grows.  (And here I'm not speaking at all of the
human factors of keeping source units small and maintainable, I'm
strictly referring to the algorithmic complexity experienced by the
compiler.)

Longer term, the gcc LTO project will eventually provide a better
infrastructure for whole-program optimizations, but it will still not be
able to see past library boundaries.  That might not be a problem if
like you say C++ had been more Java-like and required accurate throw
declarations from the beginning.  But there is just no reasonable way to
expect that now, so from a compiler standpoint the only way to implement
enforcement of throw declarations would be to "see down into" all
library code (unrealistic for all but perhaps embedded applications) or
to rewrite all library code to include accurate throw declarations in
their headers and then tell the compiler it can trust them.  Either way,
it's a ton of work.

And this hasn't even begun to address the issue of function pointers and
virtual methods, where the call graph can't even be known at compile
time.  I'm sure you could construct a pathological testcase without too
much effort for which it would be provably impossible to enforce throw
declarations at compile or link time.

However, you may be interested in Brendon Costa's EDoc++ which consists
of a hacked up gcc that embeds additional exception and callgraph data
into the objects, and a post processing tool to recover, combine, and
analyze it all at link time.  <http://edoc.sourceforge.net/>

Brian

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-21 22:50             ` Brian Dessent
@ 2008-03-21 23:14               ` Noel Yap
  0 siblings, 0 replies; 23+ messages in thread
From: Noel Yap @ 2008-03-21 23:14 UTC (permalink / raw)
  To: GCC-help; +Cc: Jason Cipriani, John Love-Jensen, Ted Byers

On Fri, Mar 21, 2008 at 3:50 PM, Brian Dessent <brian@dessent.net> wrote:
> Jason Cipriani wrote:
>
>  > One feature that I have
>  > always desired the most in a C++ compiler is strict enforcement of
>  > throw() clauses.
>
>  Since you seem to be well aware of the design of C++ exceptions you
>  should realize why this is a near impossibility.  In order for the
>  compiler to enforce a throw declaration, it would have to have complete
>  knowledge of the entire outbound call graph of the function.  Two things
>  stand in the way of this: separate compilation and function
>  pointers/virtual functions.  The problem of separate compilation (i.e.
>  that the compiler only has local knowledge of a given translation unit
>  at any time) can somewhat be dealt with by using gcc --combine, but
>  that's more of a crutch.  It won't fix the issue that necessitated
>  separate compilation in the first place: the explostion of complexity as
>  the size of one "unit" grows.  (And here I'm not speaking at all of the
>  human factors of keeping source units small and maintainable, I'm
>  strictly referring to the algorithmic complexity experienced by the
>  compiler.)
>
>  Longer term, the gcc LTO project will eventually provide a better
>  infrastructure for whole-program optimizations, but it will still not be
>  able to see past library boundaries.  That might not be a problem if
>  like you say C++ had been more Java-like and required accurate throw
>  declarations from the beginning.  But there is just no reasonable way to
>  expect that now, so from a compiler standpoint the only way to implement
>  enforcement of throw declarations would be to "see down into" all
>  library code (unrealistic for all but perhaps embedded applications) or
>  to rewrite all library code to include accurate throw declarations in
>  their headers and then tell the compiler it can trust them.  Either way,
>  it's a ton of work.
>
>  And this hasn't even begun to address the issue of function pointers and
>  virtual methods, where the call graph can't even be known at compile
>  time.  I'm sure you could construct a pathological testcase without too
>  much effort for which it would be provably impossible to enforce throw
>  declarations at compile or link time.
>
>  However, you may be interested in Brendon Costa's EDoc++ which consists
>  of a hacked up gcc that embeds additional exception and callgraph data
>  into the objects, and a post processing tool to recover, combine, and
>  analyze it all at link time.  <http://edoc.sourceforge.net/>

I agree with all this and wanted to add:
+ C linkage (which may be included above) -- since C functions don't
have exception specs, how should they be handled especially if they
wind up calling C++ functions through callbacks
+ template functions -- what if the exception spec is dependent upon
the template parameters?

Noel

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: try, finally
  2008-03-19 16:13 try, finally Jason Cipriani
                   ` (3 preceding siblings ...)
  2008-03-19 17:37 ` me22
@ 2008-03-25 22:28 ` Ian Lance Taylor
  4 siblings, 0 replies; 23+ messages in thread
From: Ian Lance Taylor @ 2008-03-25 22:28 UTC (permalink / raw)
  To: gcc-help

"Jason Cipriani" <jason.cipriani@gmail.com> writes:

> Does GCC have anything similar to the MS and Borland compiler's __try
> and __finally keywords?

Side stepping the whole thread, I'm just going to mention that
implementing try/finally would be a good Google Summer of Code project
for gcc.  http://code.google.com/soc/ .

Ian

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2008-03-25 22:28 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-03-19 16:13 try, finally Jason Cipriani
2008-03-19 16:14 ` Jason Cipriani
2008-03-19 17:02   ` Tim Prince
2008-03-19 16:37 ` Brian Dessent
2008-03-19 17:31 ` Ted Byers
2008-03-19 17:37 ` me22
2008-03-19 19:09   ` Jason Cipriani
2008-03-19 19:09     ` me22
2008-03-20  5:50       ` Jason Cipriani
2008-03-20 13:30       ` Noel Yap
2008-03-21  2:27         ` Jason Cipriani
2008-03-19 21:04     ` Ted Byers
2008-03-20  6:28       ` Jason Cipriani
2008-03-20 12:24         ` John Love-Jensen
2008-03-21  2:11           ` Jason Cipriani
2008-03-21  2:37             ` me22
2008-03-21  2:45               ` Jason Cipriani
2008-03-21 15:24             ` Noel Yap
2008-03-21 22:50             ` Brian Dessent
2008-03-21 23:14               ` Noel Yap
2008-03-20 14:50         ` Ted Byers
2008-03-21  2:26           ` Jason Cipriani
2008-03-25 22:28 ` Ian Lance Taylor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).