JonnyK
JonnyK

Reputation: 61

Is it okay to use "go to" from a catch statement

Everything I have ever been told is that go to's are evil and stay away from them, but I think they may help me here (?). I would like to provide the user an option to restart the application when an exception is caught and am having a bit of trouble wrapping my head around what to do...

My application will be monitored by another process, but there are some exceptions where I want to the user to be able to decide what to do without returning control to the calling process.

Is something like this "acceptable"? Any other suggestions?

Thanks so much!

int main(){

    initialize:
       try{
        //do things
       }
       catch(...)
       {
          cout<<"Would you like to try initializing again?"<<endl;

          //if yes
            goto initialize;

          //if not
          abort(); //or something...
        }

return 0;
}

Upvotes: 6

Views: 5401

Answers (8)

Stewart
Stewart

Reputation: 4992

I really like this use of goto. As long as it's kept in a very small and local scope, it really avoids the spaghetti that everyone fears and actually makes things clearer than the alternatives.

try_again:
try {
    do_something();
} catch (const std::exception& e) {
    std::cerr << e.what() << std::endl;
    goto try_again;
}

If I write the logic in other ways, I add more variables, more logic, more lines, more indenting. I don't like this while loop because our variable name doesn't make sense for the first pass, and we pollute our business logic by adding conditions to exit the loop.

bool try_again = true;
while(try_again) {
    try {
        do_something();
        try_again = false;
    } catch( const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
}

A do/while loop makes it a little better: we need to do something to our special variable in the error handling to try again. But we still have extra variables, logic, and nesting. Also, do/whiles are super uncommon and take a little extra brain power to recall while reading.

bool try_again;
do {
    try_again = false;
    try {
        do_something();
    } catch( const std::exception& e) {
        std::cerr << e.what() << std::endl;
        try_again = true;
    }
} while (try_again);

The original while could be simplified with break, and this is probably the nicest variant of the loops, but it hurts if we need another little for-loop in there, or a switch statement.

while(true){
    try {
        do_something();
        break;
    } catch( const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
}

I would argue that the goto solution is clearer than both of those loops. Before posting, I barely caught a last-moment bug in my do/while loop. Another nice thing about the goto solution is if you do screw up with the label location, an error should occur:

try {
    try_again:
    do_something();
} catch (const std::exception& e) {
    std::cerr << e.what() << std::endl;
    goto try_again;
}

error: cannot jump from this goto statement to its label

note: jump bypasses initialization of try block

Upvotes: 0

MMT
MMT

Reputation: 11

Yes. Based on the C++ draft Standard:

goto statement can be used to transfer control out of a try block or handler.

For example, this code is legal:

lab:  try {
  T1 t1;
  try {
    T2 t2;
    if (condition)
      goto lab;
    } catch(...) { /* handler 2 */ }
  } catch(...) { /* handler 1 */ }

However, it is important to mention that:

When this happens, each variable declared in the try block will be destroyed in the context that directly contains its declaration.

Upvotes: 1

sharptooth
sharptooth

Reputation: 170499

Yes, technically it is okay, but usual "goto considered harmful" considerations apply.

Upvotes: 5

DevSolar
DevSolar

Reputation: 70263

A goto can always be avoided, giving cleaner code.

By the way, the same goes for breaks outside a switch. The continue keyword is marginally less condemnable, because at least it honors the enclosing loop's condition.

It is important to catch exceptions at the right place - where you can handle the condition most effectively.

And if a condition turns out to be inconvenient (like the "try again?" in my case), consider negating it ("fail?") for cleaner structure.

// Tries until successful, or user interaction demands failure.
bool initialize() {
    for ( ;; ) {
        try {
            // init code
            return true;
        }
        catch ( ... ) {
            cout << "Init Failed. Fail Program?" << endl;
            if ( yes ) {
                return false;
            }
        }
    }
}

int main() {
    if ( ! initialize() ) {
        return EXIT_FAILURE;
    }
    // rest of program
    return EXIT_SUCCESS;
}

Notes: This does not use goto or break, and it does not recurse (especially not from within a catch block).

Upvotes: 5

BЈовић
BЈовић

Reputation: 64223

Normal way of handling exceptions is at the place where you can do something. It is obvious that the place you are trying to handle them is not right, since you have to use goto.

So, something like this :

void mainLoop() // get user settings, process, etc
{
   try
   {
     // 1) get user settings
     // 2) process data
     // 3) inform of the result
   }
   catch( const exception_type & e)
   {
     // inform of the error
   }      
}

int main()
{
  try
  {
    while(true)
      mainLoop();
  }
  catch(...)
  {
    std::cout<<"an unknown exception caught... aborting() " << std::endl;
  }
}

Upvotes: 1

Chris Walton
Chris Walton

Reputation: 2533

The original quote was (I believe) "uncontrolled use of goto considered harmful". Gotos can be useful, but must be used in a controlled fashion. There is one programming technique, to create reentrant subroutines, dependent on the state of the program or data, that positively demands directed jumps. Though this technique may well be considered old fashioned, I understand that it is still used, but is hidden by more modern language and compiler features. The point of the control is that you must stop and ask yourself, not just "is there a more structured way of doing the same thing" - @Justin - but also "under what specific conditions will I use a goto?" Being convenient is probably not a sufficient condition to use it, without this wider answer.

Upvotes: 2

user418748
user418748

Reputation:

you could try :

int main()
{
     while(true)
     {
          try
          {
               program();
          }
          catch(std::exception& e)
          {
               std::cout << "Start again?" << std::endl;
               //Check ...
               if(!go_on)
                    break;
          }
     }
     return 0;
}

Upvotes: 6

Mārtiņš Briedis
Mārtiņš Briedis

Reputation: 17762

Why not like this?

while(true){
  //Do stuff
  if(exit){
    break;
  }
}

or

continue = true;
do{
  //Do stuff
  if(exit){
    continue = false;
  }
}while(continue);

Upvotes: 9

Related Questions