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;
}
Why not like this?
while(true){
//Do stuff
if(exit){
break;
}
}
or
continue = true;
do{
//Do stuff
if(exit){
continue = false;
}
}while(continue);
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;
}
A goto
can always be avoided, giving cleaner code.
By the way, the same goes for break
s 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).
Yes, technically it is okay, but usual "goto considered harmful" considerations apply.
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.
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;
}
}