On Thu, Apr 14, 2011 at 1:15 PM, Uri Guttman <u...@stemsystems.com> wrote:
>>>>>> "BM" == Brandon McCaig <bamcc...@gmail.com> writes:
>  BM> I often use gotos in C for error handling within a function. If
>  BM> you're allocating resources and something later on fails then you
>  BM> usually want (or need) to clean up those resources before
>  BM> returning. I've seen a lot of people duplicate the same cleanup
>  BM> code over and over again. I hate doing that. Goto makes it much
>  BM> cleaner. Just because you can make spaghetti with goto doesn't
>  BM> mean goto always has to make spaghetti. You do need to be careful
>  BM> though and assess any given situation carefully.
>
> that can still be done very cleanly without gotos. one technique is to
> collect all the resources into a structure as you initialize. at any
> point when it fails, you return to an outer func which is given
> pass/fail results. if it failed, the outer func can free up the
> resources in the struct (which it has originally). simple and
> clean. also a better way to deal with error handling than goto the end
> for cleanup. subs make for useful flow control beyond simple abstraction
> and reuse.

That seems like a lot of extra code to achieve the same result.
An extra structure for every function, or at least every related
function, and if you don't want to duplicate the cleanup code
then also an extra function for every such structure to release
its resources... This is how I envision your suggestion in code:

result_t * result = foo_release(foo(arg1, arg2));

if(result == 0)
{
    fprintf(stderr, "Failed to foo.\n");
    return 1;
}

Or maybe:

foo_result_t * foo_result = foo(arg1, arg2);

if(!foo_result->success)
{
    foo_result_release_all(&foo_result);
    fprintf(stderr, "Failed to foo.\n");
    return 1;
}

result_t * result = foo_result_release(&foo_result);

Is this what you had in mind or something completely different?

It seems to me that it's much simpler and more reliable to just
clean it up in one place, inside of the function that failed.
Returning the bad state to the caller unnecessarily puts the
responsibility to clean things up on the caller. So instead of
having a little bit of fuss in one self-contained box you'd be
spreading fuss throughout the entire code base. :-/

I imagine that you are a more experienced C programmer than I am,
but nevertheless I think the goto approach can be much simpler
sometimes. Simple and clean are usually synonymous in code. With
sufficiently short functions (and longer ones should be broken up
anyway) it's easy to see the simple goto error handling and the
whole thing is self contained within that function. The C
functions that this applies to usually look like this for me:

result_t * foo(arg1_t arg1, arg2_t arg2)
{
    result_t * result = 0;

    // Main body... If a non-fatal terminating condition is met
    // (i.e., the function is successful, but finished early)
    // then goto finally. If a fatal terminating condition is met
    // then goto catch.

finally:
    // Cleanup temporary resources and ignore unallocated ones
    // (that is, no matter where in the function the goto is
    // called it should work properly and not crash the
    // application.

    return result;

catch:
    // Cleanup all allocated resources and ignore unallocated
    // ones (that is, no matter where in the function the goto is
    // called it should work properly and not crash the
    // application)...
}

It's modeled somewhat after the try...catch...finally construct
found in modern C-like languages (e.g., C#). The "finally"
section always executes. The "catch" section only executes when
an unrecoverable error occurs.


-- 
Brandon McCaig <http://www.bamccaig.com/> <bamcc...@gmail.com>
V zrna gur orfg jvgu jung V fnl. Vg qbrfa'g nyjnlf fbhaq gung jnl.
Castopulence Software <http://www.castopulence.org/> <bamcc...@castopulence.org>

--
To unsubscribe, e-mail: beginners-unsubscr...@perl.org
For additional commands, e-mail: beginners-h...@perl.org
http://learn.perl.org/


Reply via email to