Re: try, finally

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



--- Jason Cipriani <jason.cipriani@xxxxxxxxx> wrote:
> > On Wed, Mar 19, 2008 at 12:13 PM, Jason Cipriani
> >  <jason.cipriani@xxxxxxxxx> 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@xxxxxxxxxxx> 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@xxxxxxxxxx> 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@xxxxxxxxx> 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

[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux