Exiting from loop

V

vineoff

If I'm having nested loops like:

for (...) for (..) for (...) { /* exit here */ }

and I need to exit from there ^ .

Is it better to use exceptions or goto or some other method?
 
T

Tatu Portin

vineoff said:
If I'm having nested loops like:

for (...) for (..) for (...) { /* exit here */ }

and I need to exit from there ^ .

Is it better to use exceptions or goto or some other method?

goto.
 
K

Karl Heinz Buchegger

vineoff said:
If I'm having nested loops like:

for (...) for (..) for (...) { /* exit here */ }

and I need to exit from there ^ .

Is it better to use exceptions or goto or some other method?

Create a function which holds only the nested loops and
return from that function in the innermost loop.

(Yes: it is a sort of goto, without an explicite goto)
 
M

mlimber

Tatu said:

I find it clearer to have a variable to indicate that it is time to
exit:

bool done = false;
for( int i=0; !done && i < 10; ++i )
{
for( int j=0; !done && j < 10; ++j )
{
for( int k=0; !done && k < 10; ++k )
{
if( k == 5 ) // Some dummy exit condition
{
done = true;
continue; // or break;
}
}
}
}

In general, you should not use exceptions unless the condition is,
well, exceptional. IOW, don't use exceptions as an alternative to break
and continue when there is not actually an error (however that is
defined).

Cheers! --M
 
A

Alf P. Steinbach

* vineoff:
If I'm having nested loops like:

for (...) for (..) for (...) { /* exit here */ }

and I need to exit from there ^ .

Is it better to use exceptions or goto or some other method?

Don't use exceptions (inefficient and unorthodox and possibly dangerous), and
don't use goto (try to find the ACM web version of Dijkstra's "GOTO considered
harmful" (ah, well, Google lists it as first hit)).

As Karl Heinz Buchegger suggested, if you absolutely must _jump_, a function
return can be a tolerable solution.

However, "premature optimization is the root of all evil" (Hoare, Knuth). If
you do the proper continuation checks, and if necessary suitable break and/or
continue, the loops may become more clear to you, and also to the compiler.
Then if there was a bug it may be that it disappears, the code may become more
maintainable, and it may even run faster than with the jump...

That technical stuff aside, a triply nested loop can be indicative of a design
flaw.

What does the triply nested loop do, and how many lines in total?
 
R

red floyd

Alf said:
* vineoff:



Don't use exceptions (inefficient and unorthodox and possibly dangerous), and
don't use goto (try to find the ACM web version of Dijkstra's "GOTO considered
harmful" (ah, well, Google lists it as first hit)).

My $0.02.

1. For bailing out of deeply nested loops, GOTO is not necessarily
harmful. But I don't have my copy of the Standard available, and am not
sure if a goto calls destructors when exiting blocks.

2. An exception would guarantee destructor calling, and would exit your
code, but it might be unnecessarily baroque. If you went this route,
I'd define my own exception (not part of the std::exception hierarchy):

struct loop_bailout_exception { };

3. Nested functions may be your best bet. If you're nesting that
deeply, your problem is probably refactorable as well. Your functions
could return a continue-loop/abort-loop indicator.
 
J

Josh Mcfarlane

red said:
My $0.02.

1. For bailing out of deeply nested loops, GOTO is not necessarily
harmful. But I don't have my copy of the Standard available, and am not
sure if a goto calls destructors when exiting blocks.

Gotos are horribly outdated IMO. The last place I remember using them
was in QBASIC and the BASIC compilers on Apple IIs
2. An exception would guarantee destructor calling, and would exit your
code, but it might be unnecessarily baroque. If you went this route,
I'd define my own exception (not part of the std::exception hierarchy):

But then you're using an exception as a standard part of the code. I
have a 3rd-party API library that does this, and it's such an annoyance
to view the exceptions everytime they're generated in the debugger.
3. Nested functions may be your best bet. If you're nesting that
deeply, your problem is probably refactorable as well. Your functions
could return a continue-loop/abort-loop indicator.

This is probably the best bet. Easiest way to debug and clearest to a
2nd eye.
 
K

Kai-Uwe Bux

red said:
My $0.02.

1. For bailing out of deeply nested loops, GOTO is not necessarily
harmful. But I don't have my copy of the Standard available, and am not
sure if a goto calls destructors when exiting blocks.

The objects are properly destroyed:
[6.6/2]
On exit from a scope (however accomplished), destructors (12.4) are called
for all constructed objects with automatic storage duration (3.7.2) (named
objects or temporaries) that are declared in that scope, in the reverse
order of their declaration. Transfer out of a loop, out of a block, or back
past an initialized variable with automatic storage duration involves the
destruction of variables with automatic storage duration that
are in scope at the point transferred from but not at the point transferred
to. (See 6.7 for transfers into blocks). [Note: However, the program can be
terminated (by calling exit() or abort()(18.3), for example) without
destroying class objects with automatic storage duration. ]



[snip]


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Josh said:
Gotos are horribly outdated IMO. The last place I remember using them
was in QBASIC and the BASIC compilers on Apple IIs

You are entitled to your opinion. However, sometimes oldfashioned can be the
best tool to do a job.

But then you're using an exception as a standard part of the code. I
have a 3rd-party API library that does this, and it's such an annoyance
to view the exceptions everytime they're generated in the debugger.

Huh? Are these exceptions to be caught by the client or to are they caught
within the library?

This is probably the best bet. Easiest way to debug and clearest to a
2nd eye.

Hm, introducing a function just to avoid a goto is barock. A function should
have a well-defined purpose/meaning/contract.

There are gotos that are just fine. Generally, I consider gotos that are
just generalized break and continue statements different from those gotos
that jump all over the place.


Best

Kai-Uwe Bux
 
N

Neil Cerutti

You are entitled to your opinion. However, sometimes
oldfashioned can be the best tool to do a job.

Prompting and verifying input is a case where goto turns out to
seem nice and clean.

#include <iostream>
#include <limits>

template <typename T>
T ranged_prompt(T min, T max)
{
T n;
prompt:
std::cout << "Enter a number from " << min << " to " << max ": ";
std::cout.flush()
std::cin >> n;
if (!std::cin || n < min || n > max) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<int>::max(), '\n');
goto prompt;
}
return n;
}

/* Alternative 1 */
template <typename T>
T ranged_prompt(T min, T max)
{
T n;
bool input_invalid = true;
while (input_invalid) {
std::cout << "Enter a number from " << min << " to " << max ": ";
std::cout.flush()
std::cin >> n;
if (!std::cin || n < min || n > max) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<int>::max(), '\n');
} else {
input_invalid = false;
}
}
return n;
}

/* Other alternatives? */
 
T

Tatu Portin

mlimber said:
I find it clearer to have a variable to indicate that it is time to
exit:

bool done = false;
for( int i=0; !done && i < 10; ++i )
{
for( int j=0; !done && j < 10; ++j )
{
for( int k=0; !done && k < 10; ++k )
{
if( k == 5 ) // Some dummy exit condition
{
done = true;
continue; // or break;
}
}
}
}

In general, you should not use exceptions unless the condition is,
well, exceptional. IOW, don't use exceptions as an alternative to break
and continue when there is not actually an error (however that is
defined).

It is not efficient.
 
K

Kai-Uwe Bux

Neil said:
Prompting and verifying input is a case where goto turns out to
seem nice and clean.

#include <iostream>
#include <limits>

template <typename T>
T ranged_prompt(T min, T max)
{
T n;
prompt:
std::cout << "Enter a number from " << min << " to " << max ": ";
std::cout.flush()
std::cin >> n;
if (!std::cin || n < min || n > max) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<int>::max(), '\n');
goto prompt;
}
return n;
}

/* Alternative 1 */
template <typename T>
T ranged_prompt(T min, T max)
{
T n;
bool input_invalid = true;
while (input_invalid) {
std::cout << "Enter a number from " << min << " to " << max ": ";
std::cout.flush()
std::cin >> n;
if (!std::cin || n < min || n > max) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<int>::max(), '\n');
} else {
input_invalid = false;
}
}
return n;
}

/* Other alternatives? */

Ahem, what about:

template <typename T>
T ranged_prompt(T min, T max)
{
T n;
do {
std::cout << "Enter a number from " << min << " to " << max ": ";
std::cout.flush()
std::cin >> n;
while (!std::cin || n < min || n > max) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<int>::max(), '\n');
return n;
}


Best

Kai-Uwe Bux
Best

Kai-Uwe Bux
 
M

Mogens Heller Jensen

vineoff said:
If I'm having nested loops like:

for (...) for (..) for (...) { /* exit here */ }

and I need to exit from there ^ .

Is it better to use exceptions or goto or some other method?

I'm sure you have a good reason to put all those loops inside eachother
(like e.g. iterating through something three dimensional) - I would
definately go for a goto in this case.

Goto is not dangerous or evil, it's just easy to use in ways that mess up
your code. But it is a clear and precise way of exiting nested loops if you
have found what you are looking for.

To me it seems people tend to forget why they should not use goto, and just
refrain from using it because their teachers told them not to. You should
always consider the solution which creates clear and understandable code
which is easy to maintain.

Best regards,
Mogens
 
A

Alf P. Steinbach

* Kai-Uwe Bux -> Neil Cerutti:
Ahem, what about:

template <typename T>
T ranged_prompt(T min, T max)
{
T n;
do {
std::cout << "Enter a number from " << min << " to " << max ": ";
std::cout.flush()
std::cin >> n;
while (!std::cin || n < min || n > max) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<int>::max(), '\n');
return n;
}

Typos, typos... ;-)

Possibly you meant something like

template< typename T >
T numberFromUser( T min, T max )
{
T n;
bool ok;

do
{
std::cout << "Enter a number from " << min << " to " << max ": ";
std::cin >> n; // cin and cout are synchronized.
ok = (std::cin.good() && min <= n && n <= max);
std::cin.clear();
std::cin.ignore( std::numeric_limits<int>::max(), '\n' );
} while( !ok );
return n;
}

One crucial thing here is the name change, to reflect what the function
produces. I don't think I could guess from the name 'ranged_prompt' (the OP's
choice) that it actually does input. I'd guess it produced a prompt.

Of course for good measure -- less Undefined Behavior, those iostreams
aren't my favorite beasts! -- one should read in a whole line using getline,
and parse it using strtol or strtod as appropriate (not a stringstream)...
 
K

Kai-Uwe Bux

Alf said:
* Kai-Uwe Bux -> Neil Cerutti:

Typos, typos... ;-)

Possibly you meant something like

template< typename T >
T numberFromUser( T min, T max )
{
T n;
bool ok;

do
{
std::cout << "Enter a number from " << min << " to " << max ":
";
std::cin >> n; // cin and cout are synchronized.
ok = (std::cin.good() && min <= n && n <= max);
std::cin.clear();
std::cin.ignore( std::numeric_limits<int>::max(), '\n' );
} while( !ok );
return n;
}

One crucial thing here is the name change, to reflect what the function
produces. I don't think I could guess from the name 'ranged_prompt' (the
OP's
choice) that it actually does input. I'd guess it produced a prompt.

As for the name: it actually prompts.

Anyway, I meant to get rid of the goto without introducing a flag variable.
So here:

template <typename T>
T ranged_prompt(T min, T max)
{
T n;
do {
std::cout << "Enter a number from " << min << " to " << max << ": ";
std::cout.flush();
std::cin >> n;
if (!std::cin || n < min || n > max) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<int>::max(), '\n');
continue;
}
} while ( false );
return n;
}

Now, one could argue that continue is just as bad as a goto. Well, here
goes:

template <typename T>
T ranged_prompt(T min, T max)
{
T n;
do {
std::cout << "Enter a number from " << min << " to " << max << ": ";
std::cout.flush();
std::cin >> n;
} while ( (!std::cin || n < min || n > max)
? std::cin.clear(),
std::cin.ignore(std::numeric_limits<int>::max(), '\n'),
true
: false );
return n;
}

Mission complete: goto-like jumps avoided <g>


Best

Kai-Uwe Bux
 
W

Wolfgang Draxinger

I always find it interesting in which ways coders react on the
mention of "goto". Yes it is quick and dirty, but IMHO sometimes
pragmatism should be favoured over academical correctnes. And
there are some situations in which a "goto" is the most readable
and clear way to go. A good example would be the initialization
of a device driver (ok, those are normally done in C and not
C++). If something goes wrong there you must make sure that you
revert the state of the device on how you found it.

enum subdevice{A, B, C};

bool init_device()
{
sub_device_status pre_A;
sub_device_status pre_B;
sub_device_status pre_C;

// each init_subdevice_ call initializies the
// internal data structures and then reset the
// subdevice

get_device_status(A, &pre_A);
if( !init_subdevice_A() )
goto failed_A;

get_device_status(B, &pre_B);
if( !init_subdevice_B() )
goto failed_B;

get_device_status(C, &pre_C);
if( !init_subdevice_C() )
goto failed_C;

// All subdevices got initialized properly
return true;

failed_C:
set_device_status(C, pre_C);
failed_B:
set_device_status(B, pre_B);
failed_A:
set_device_status(A, pre_A);

return false;
}

Frankly I don't know a cleaner way to write this, but I'm open to
other suggestions. Also there are situations in which one has
deeply nested loops and where splitting in seperate functions is
inapropriate. An example may be the processing of a large
multidimensional array. And even if premature optimization is
considered bad there are some things that one can still do to
enhance performance.

For example to do as few function calls as possible. Each C/C++
function call requires to push stuff on the stack, modify the
stack pointer, then the instruction pointer and probably also
blows the registers. If you google in the NGs which deal with
time critical programming (e.g. interactive computer graphics)
one common rule is to do as few function calls and do as much
data processing within a single function as possible. Of course
I'm not saying: "Put everything in main", but splitting
functions until they are sliced to hundreds of trivial 2 liner
functions in C/C++ is IMHO as bad. If you want to go for this
use a functional programming language, they're designed for
this.

Wolfgang Draxinger
--
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
As for the name: it actually prompts.

Well, but it does more than that, doesn't it?

As I see it that prompt business is a secondary thing.

Anyway, I meant to get rid of the goto without introducing a flag variable.

Note what the flag is used for: to remember the goodness of the stream until
after the stream has been cleared and line emptied.

Which IMHO should be done regardless of success or failure this iteration.

If the flag variable is removed and replaced by a position in the code, then
without using RAII I think there will necessarily be code duplication: the
cleanup actions duplicated for the cases of success and failure for this
iteration.

The simplest RAII would be to use a ScopeGuard object, but that brings in the
whole ScopeGuard header, which is too much to present here.

A Do-It-Yourself RAII-based version might go like this:

class StreamCleanup
{
private:
std::istream& myStream;
StreamCleanup& operator=( StreamCleanup const& ); // None.
public:
StreamCleanup( std::istream& s ): myStream( s ) {}
~StreamCleanup()
{
myStream.clear();
myStream.ignore( std::numeric_limits<int>::max(), '\n' );
}
};

template< typename T >
T numberFromUser( T min, T max )
{
for( ;; )
{
StreamCleanup cleaner( std::cin );
T n;

std::cout
<< "Enter a number from " << min << " to " << max << ": ";
std::cin >> n; // cin and cout are synchronized.
if( std::cin.good() && min <= n && n <= max )
{
return n;
}
}
}

Cheers,

- Alf
 
A

Alf P. Steinbach

* Wolfgang Draxinger:
I always find it interesting in which ways coders react on the
mention of "goto". Yes it is quick and dirty, but IMHO sometimes
pragmatism should be favoured over academical correctnes. And
there are some situations in which a "goto" is the most readable
and clear way to go. A good example would be the initialization
of a device driver (ok, those are normally done in C and not
C++). If something goes wrong there you must make sure that you
revert the state of the device on how you found it.

enum subdevice{A, B, C};

bool init_device()
{
sub_device_status pre_A;
sub_device_status pre_B;
sub_device_status pre_C;

// each init_subdevice_ call initializies the
// internal data structures and then reset the
// subdevice

get_device_status(A, &pre_A);
if( !init_subdevice_A() )
goto failed_A;

get_device_status(B, &pre_B);
if( !init_subdevice_B() )
goto failed_B;

get_device_status(C, &pre_C);
if( !init_subdevice_C() )
goto failed_C;

// All subdevices got initialized properly
return true;

failed_C:
set_device_status(C, pre_C);
failed_B:
set_device_status(B, pre_B);
failed_A:
set_device_status(A, pre_A);

return false;
}

Frankly I don't know a cleaner way to write this, but I'm open to
other suggestions.

class StatusRollback
{
private:
sub_device_status myStatus;
unsigned myDeviceId;
bool iAmCancelled;
public:
StatusRollback( unsigned id ): myDeviceId( id ), iAmCancelled( false )
{
get_device_status( myDeviceId, &myStatus );
}

~StatusRollback()
{
if( !iAmCancelled ) { set_device_status( myDeviceId, &myStatus ); }
}

void cancel() { iAmCancelled = true; }
};

enum SubDeviceId{ deviceA, deviceB, deviceC };

bool InitDevice()
{
StatusRollback rollbackA( deviceA );
if( !init_subdevice_A() ) { return false; }

StatusRollback rollbackB( deviceB );
if( !init_subdevice_B() ) { return false; }

StatusRollback rollbackC( deviceC );
if( !init_subdevice_C() ) { return false; }

rollbackC.cancel();
rollbackB.cancel();
rollbackA.cancel();
return true;
}

It's also more exception safe, although that is presumably not an issue in a
device driver... ;-)
 
D

Dave Rahardja

I always find it interesting in which ways coders react on the
mention of "goto". Yes it is quick and dirty, but IMHO sometimes
pragmatism should be favoured over academical correctnes. And
there are some situations in which a "goto" is the most readable
and clear way to go. A good example would be the initialization
of a device driver (ok, those are normally done in C and not
C++). If something goes wrong there you must make sure that you

"goto" is often frowned upon in C++ because it may transfer control into
another context whose local variables are not properly constructed. It also
encourages "spaghetti code" style programming that is the bane of BASIC
applications.

Most problems that at first appear to need goto's turn out not to need them
once a more complete understanding of the C++ language is applied to the
analysis. However, there are (extremely rare) problems that are best solved
with gotos. Co-routines come to mind.

Escaping from deeply-nested logic statements can sometimes be implemented by
putting the logic in a function and using return to escape, or by enclosing
the block in a do {...} while(false) loop, and using break to escape.

BTW, much to my surprise, a reading of the Standard shows that goto implies
the destruction of auto variables that go out of scope as a result of the
jump. I always thought that goto simply inserted a jump statement in the
object code. I learned something today!

-dr
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,571
Members
45,045
Latest member
DRCM

Latest Threads

Top