goto label inside of if statement

J

Jax

As with all features, one uses goto if it is better than the
alternatives. And "better" here means primarily more readable and
maintainable code. Breaking out of deep nested loops with goto to near
the beginning or the end of a function is IMO okay, as the relevant
state of the program after the jump is pretty clear in such cases.

The same functionality can be always achieved with reorganizing the
code, possibly splitting it into multiple functions. Whether this makes
the code more readable and maintainable than 'goto' depends on the
circumstances and also on the taste (of the future readers and
maintainers in particular). So there is no clear-cut answer. One thing
is clear, the need for 'goto' is rare, if you find yourself writing a
goto every week then something is probably wrong.

HTH
Paavo

Paavo.... Gareth tells me that your advice is reliable. I have made a note of
it. Thank you!
 
W

W Karas

It's okay to jump across block boundaries with "goto", either into or

out of a block, and it's also okay to jump into block boundaries with a

`switch` (the old "duff's device" relied on that), but you can't



* jump across function boundaries or



* skip the initialization of a variable











That's ingenious.

I'm honored, but you are too kind.
But, heads-up: there once was a version of a Unix shell written in C

with macros and support functions defined to make it look like Pascal.

Heavy emphasis on **was**.



And for example, when I once defined a WITH macro (corresponding to C#

"using"), and even enlisted help from the community with the quality

assurance of that, I thought this was the next best thing since pizza

slices. It used a similar trick internally, using an `if` to introduce a

variable for the `else` part. I have not used that macro since.



* * *



So, how to reasonably exit from a nested scope, without such shenanigans?



Let's consider the common example, a nested loop, with some nested

conditionals thrown in for good measure, plus an "at the end" action:





Code:
#include <iostream>

#include <math.h>       // sqrt

using namespace std;



auto main()

-> int

{

int const max_a         = 12345;

int const max_numbers   = 42;

int n = 0;

int prev_c  = 0;

for( int a = 1; a <= max_a; ++a )

{

for( int b = 1; b < a; ++b )

{

int const c_sq = a*a + b*b;

int const c = int( sqrt( c_sq ) + 0.5 );

if( c*c == c_sq )

{

if( c < prev_c )

{

cout << (n > 0? " " : "") << c;

++n;

if( n == max_numbers )

{

goto finished;

}

}

prev_c = c;

}

}

}

finished:

cout << endl;



cout << "Found " << n << " interesting numbers." << endl;

}





The C++ construct that is a scope that can be exited from some nested

scope, is a function, and in most all cases introducing (refactoring as)

a function is a good solution.



In C++03 that function must be a named function or a function template

instantiation. As a named function it can be at namespace scope or a

member of a local class. In C++11 it can also be an anonymous function,

as a lambda.



The code below shows a C++03-style named function refactoring:





Code:
#include <iostream>

#include <math.h>       // sqrt

using namespace std;



auto list_interesting_numbers(

int const   max_numbers,

int const   max_a         = 12345

)

-> int

{

int n = 0;

int prev_c  = 0;

for( int a = 1; a <= max_a; ++a )

{

for( int b = 1; b < a; ++b )

{

int const c_sq = a*a + b*b;

int const c = int( sqrt( c_sq ) + 0.5 );

if( c*c == c_sq )

{

if( c < prev_c )

{

cout << (n > 0? " " : "") << c;

++n;

if( n == max_numbers )

{

return n;

}

}

prev_c = c;

}

}

}

return n;

}



auto main()

-> int

{

int const n = list_interesting_numbers( 42 );

cout << endl;



cout << "Found " << n << " interesting numbers." << endl;

}





Since the `goto` has been eliminated, the effect of any statement here

is clear just from inspecting the statement itself.



Also, the function is more reusable.



For some special cases of nested loops there are some other clean

alternatives. For example, looping over a rectangular set of positions

can be done with a single logical position variable. But I do not think

that the Pascal solution, of introducing extra boolean "are we finished

yet" variables, checked at every loop iteration, is clean for C++. That

solution was nice for Pascal, and also for C, where a single exit point

(SESE) is important. However, in C++ a single exit point can almost

never be relied on (so that code that does, is misguided), and one has

tools to deal with multiple exit points, namely destructors and `catch`.





Cheers & hth.,



- Alf

This is a situation where Pascal's nested functions where could be very advantageous. The nested function has access to the containing function's scope, so it is equivalent to a named block where "return" is a break from anylevel of nesting. Perhaps nested function support should be added to C++ (and C) since the hidden overhead (hidden pointer to containing function's scope) is straight-forward.

I believe the Standard already has one precedent for recommending a standard practice, namely, reserve symbols starting with underscore ( _ ) for standard library header files. Maybe this is a case where another such recommendation is needed. Perhaps that labels should begin with EXIT_BLOCK_ if and only if they are used exclusively to do a break from with a nested block.That has advantages over my tricky macros.
 
A

Alf P. Steinbach

* W Karas:
This is a situation where Pascal's nested functions where could be very
advantageous. The nested function has access to the containing function's
scope, so it is equivalent to a named block where "return" is a break from
any level of nesting. Perhaps nested function support should be added to
C++ (and C) since the hidden overhead (hidden pointer to containing
function's scope) is straight-forward.

The following is valid C++, as of C++11:


Code:
#include <iostream>
#include <math.h>       // sqrt
using namespace std;

auto main()
-> int
{
int const max_a         = 12345;
int const max_numbers   = 42;

int n = 0;
[&]() -> void
{
int prev_c  = 0;
for( int a = 1; a <= max_a; ++a )
{
for( int b = 1; b < a; ++b )
{
int const c_sq = a*a + b*b;
int const c = int( sqrt( c_sq ) + 0.5 );
if( c*c == c_sq )
{
if( c < prev_c )
{
cout << (n > 0? " " : "") << c;
++n;
if( n == max_numbers )
{
return;
}
}
prev_c = c;
}
}
}
}();
cout << endl;

cout << "Found " << n << " interesting numbers." << endl;
}

Exactly how it implements references to local variables is up to the
compiler, but a smart compiler would use a pointer to the stack frame.
Unlike Pascal's nested function a C++ lambda can alternatively capture
by value.

I would however use a named function instead of an inline lambda.


Cheers & hth.,

- Alf
 
W

W Karas

* W Karas:
This is a situation where Pascal's nested functions where could be very
advantageous. The nested function has access to the containing function's
scope, so it is equivalent to a named block where "return" is a break from
any level of nesting. Perhaps nested function support should be added to
C++ (and C) since the hidden overhead (hidden pointer to containing
function's scope) is straight-forward.



The following is valid C++, as of C++11:





Code:
#include <iostream>

#include <math.h>       // sqrt

using namespace std;



auto main()

-> int

{

int const max_a         = 12345;

int const max_numbers   = 42;



int n = 0;

[&]() -> void

{

int prev_c  = 0;

for( int a = 1; a <= max_a; ++a )

{

for( int b = 1; b < a; ++b )

{

int const c_sq = a*a + b*b;

int const c = int( sqrt( c_sq ) + 0.5 );

if( c*c == c_sq )

{

if( c < prev_c )

{

cout << (n > 0? " " : "") << c;

++n;

if( n == max_numbers )

{

return;

}

}

prev_c = c;

}

}

}

}();

cout << endl;



cout << "Found " << n << " interesting numbers." << endl;

}



Exactly how it implements references to local variables is up to the

compiler, but a smart compiler would use a pointer to the stack frame.

Unlike Pascal's nested function a C++ lambda can alternatively capture

by value.

If a hypothetical nested function in C++ were (implicitly or explicitly) inline, then assuming good optimization, any overhead issues would be moot.
I would however use a named function instead of an inline lambda.

If the (named) function substituted for the block were declared inline, then the "problem" would be the need to make explicit the block's dependence on local variables in the parameter list. Good optimization would hopefullymake object level overhead issues moot in this case too.
 
W

W Karas

Please fix the quoting before posting.

I have not found an alternative to using the groups.google.com web portal for usenet. I don't have enough time to properly follow this group, much less correct the mess that Google makes when quoting the original post. Is it best to simply delete all or most of the original post one is responding to when using the Google portal?
 
R

red floyd

I have not found an alternative to using the groups.google.com web portal for usenet. I don't have enough time to properly follow this group, much less correct the mess that Google makes when quoting the original post. Is it best to simply delete all or most of the original post one is responding to when using the Google portal?

Use eternal-september.org
 

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

No members online now.

Forum statistics

Threads
474,099
Messages
2,570,626
Members
47,237
Latest member
David123

Latest Threads

Top