GOTO, why are you adverse to using it

Started by
74 comments, last by SmkViper 9 years, 8 months ago

# 3 Or the honest-to-god goto model:


int do_work(int stuff)
{
    int res1 = 0, res2 = 0, res3 = 0;
    int success = 0;

    res1 = acquire_one_resource(stuff);
    if (!res1) goto cleanup;

    res2 = acquire_another_resource(stuff);
    if (!res2) goto cleanup;

    res3 = acquire_more_resources(stuff);
    if (!res3) goto cleanup;

    /* etc. */

    success = 1;

cleanup:
    /* here the only requirement is that the cleanup process
     * ignores unacquired resources, perhaps with some minor
     * logic if you have pointers to resources (unusual) */
    free_resource(res3);
    free_resource(res2); // would you look at that, all the cleanup code
    free_resource(res1); // in one single spot, no code duplication
    return success;
}

I use this kind of thing fairly frequently -- one additional way (when needed) is you could have 3 different cleanup target LABELs if there were complications with running each different free_resource(). call (or their possibly very different setup processing example -- 3D graphics where you have many different types of objects you have to setup all at once... )

Also its typical that this overall function only does the initializations (and the real processing is elsewhere and extensive) so the cleanups here would all be 'FAIL conditions and the return(1) would be above the cleanup block and the bottom return a return(0) -- since if you failed allocating resources-the caller needs tio handle that.

for readability (when tehre is a whole list of similar statements I also frequently do :

if ( ! (res1 = acquire_one_resource(stuff))) goto FAIL_CLEANUP;

if ( ! (res2 = acquire_another_resource(stuff))) goto FAIL_CLEANUP;

if ( ! (res3 = acquire_more_resources(stuff))) goto FAIL_CLEANUP;

And I always capitalize my goto labels to make them stand out all the more

Or as I mentioned :

if ( ! (res1 = acquire_one_resource(stuff))) goto FAIL_CLEANUP_ONE;

if ( ! (res2 = acquire_another_resource(stuff))) goto FAIL_CLEANUP_ANOTHER;

if ( ! (res3 = acquire_more_resources(stuff))) goto FAIL_CLEANUP_MORE;

Typically cleanup code is also near the bottom of the subroutine - making it the expected place to look if the code uses this pattern consistantly.

Another pattern Ive sometimes used (a backward jumping GOTO) was for local retry ( more in ancient days when data seeks/reads/writes across a network/from disk could fail at anypoint ), and another was parsing custom text file save formats with 'none or many' flavored data text lines.

Of course those were complicated by retry limits of some kind to eventually signal upward as a significant failure.

I was actually appalled that entire applications I was handed had not just no retries but often no error return checking at all on such operations.

--------------------------------------------[size="1"]Ratings are Opinion, not Fact
Advertisement

Wow, a lot of posts in 2 days, thanks for all of the replies. I believe Bacterius explained it pretty much the way I use it, cleanup. If I create using new, it needs to be deleted before I leave the routine, otherwise I incur a memory leak. I use break and continue as well. Mainly it's because I'm set in my ways and have no trouble following my own code. I always use the exact same labels for every goto. For exit and cleanup routines, it's ex:, if I want to use a loop without the hassle of dealing with a for statement, it's lp:. On rare occasions I need to reinitialize the loop by going above that code, it's always lp0:. I don't use it a huge amount but it works like this for me, code not executed is CPU cycles not wasted, the same principle of the early Z fail for GPUs.

As far as the 1000 lines of code, that was just an example. None of my routines are that large, but the main INI parsing routine comes close (913 lines). Due to it being written that way I can run through 918 INI files in less that 4 seconds because of it being in the cache and it is looping back to the beginning. The most used commands are the first checks such as models and lights.

Anyways it's just the way I program, I doubt I will ever have a job in the industry. I use computers at work but it's just for inventory management, nothing more. I'm quite used to it and it may look like spaghetti but I can easily read it due to that I use consistent naming. I haven't use RAII in over a decade, forgot what it's even used for ;)

I am interested in some of the other methods of avoiding it that have been mentioned here, if you folks wouldn't mind explaining some of them to me, I would be much abliged for your help,

******************************************************************************************
Youtube Channel

I haven't use RAII in over a decade, forgot what it's even used for ;)


This is what the C++ code using RAII looks like:
bool do_work(int stuff) {
  ResourceOneGuard guard_one(stuff);
  if (guard_one.bad_status())
    return false;
  
  ResourceTwoGuard guard_two(stuff);
  if (guard_two.bad_status())
    return false;
  
  ResourceThreeGuard guard_three(stuff);
  if (guard_three.bad_status())
    return false;
  
  return true;
}
If the classes Resource*Guard provide meaningful destructors that do the clean up, the returns will automatically call them appropriately.

I haven't use RAII in over a decade, forgot what it's even used for ;)


This is what the C++ code using RAII looks like:

bool do_work(int stuff) {
  ResourceOneGuard guard_one(stuff);
  if (guard_one.bad_status())
    return false;
  
  ResourceTwoGuard guard_two(stuff);
  if (guard_two.bad_status())
    return false;
  
  ResourceThreeGuard guard_three(stuff);
  if (guard_three.bad_status())
    return false;
  
  return true;
}
If the classes Resource*Guard provide meaningful destructors that do the clean up, the returns will automatically call them appropriately.

There are two versions of the RAII approach, and the one you've posted isn't considered kosher by many. The constructors for the resource guards are supposed to throw exceptions if there's an issue, not have some kind of invalid state marker.

Me personally? I'm in the camp that feels that exceptions are gotos on steroids. http://www.joelonsoftware.com/items/2003/10/13.html

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

There are two versions of the RAII approach, and the one you've posted isn't considered kosher by many. The constructors for the resource guards are supposed to throw exceptions if there's an issue, not have some kind of invalid state marker.


What do those people think of std::ifstream?

I am aware of the possibility of throwing exceptions from the constructor, but I didn't want to introduce another C++ element into the conversation, in case there are any beginners reading this.

I also avoid exceptions at all costs. The only time I use them is with file I/O routines. I use the try function and then include a catch block immediately below it. If there is an error I log it and pass it back through the return value. I even have dedicate log routines for plain text, DX errors that use getlasterror for the full error description and several others. Even when I code I build error checking into the code as the routine is constructed. I even split the log into a runtime log and an exit log in-case of a crash during exit. It is also saved to a temp log each time a new scene is loaded in-case of a crash.

That was a good article Promit.

******************************************************************************************
Youtube Channel

Why would I ever want to use a goto? I simply have no use for them. So unnecessary are they to me, that I am only ever reminded of their existence until someone on a forum brings it up. I'm not sure much adverse to it, it's just that I have so many other tools that do a much better job that it's totally redundant.

Then of course there's the fact that when it comes to being in the workforce, you'll be a lot more likely to get a job and keep it if you don't use them.

The exceptions are "gotos on steroids" argument is a weak one. It can be suggested that absolutely anything that changes program flow is a "goto on steroids". Exceptions differ hugely in that the thrower of the exception has no knowledge of where execution will resume from, unlike gotos that have a specific fixed target. Like it or not, they are the way languages tend to work now, and you'd be hard pressed to avoid them (or write sucky code if you do) in a language like C#.

"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

Then of course there's the fact that when it comes to being in the workforce, you'll be a lot more likely to get a job and keep it if you don't use them.


That's just silly. If you use a goto in a place where it makes the code clear (granted, these situations are exceedingly rare), nobody will have a problem with it.

This is a bit like writing sentences that end in prepositions. Some scholars of English more than two centuries ago decided that it was wrong, and even though it's clear to every modern grammarian that it's perfectly O.K., people keep repeating this myth. It's not exactly like that, because at the time when Dijkstra wrote his famous piece on the dangers of goto, it was very common for people to write code that abused goto in all sorts of ways, often making the code a tangled mess with no recognizable flow structure (see spaghetti code). In that context, his advice made perfect sense. Fortunately for us, people don't write code like this anymore, and you won't have any problems being employed as a programmer if you use a goto to break out of nested loops in a place where doing so keeps the code cleaner than the alternatives.

I think exceptions have no place other than in the case of a fatal, unrecoverable failure in a program. When I was learning Java way back, I was encouraged to use exceptions as the default way of reporting and handling errors. I always felt that was a bit clunky, and it made for code that was filled with blocks that I felt never belonged in the code at all. I always favoured explicit error checking over exceptions. Who thought it was a great idea to throw a NumberFormatException when you feed bogus data to convert a string to an integer in Java?? That pretty much sums it up...

Not to be a goto hater for the sake of it, but could someone post an actual example in C++ where a goto was appropriate in a well structured function?

I see goto as a symptom, not a disease, much as others have said.

This topic is closed to new replies.

Advertisement