Date: Mon, 1 May 2023 14:53:44 +0100
When it comes to breaking out of nested loops, some programmers use
the 'goto' keyword , but nowadays I think the following strategy is
more prevalent:
bool mega_break = false;
for ( unsigned i = 0u; i < x; ++i )
{
for ( unsigned j = 0u; j < y; ++j )
{
for ( unsigned k = 0u; k < z; ++k )
{
if ( something )
{
mega_break = true;
break;
}
}
if (mega_break) break;
}
if (mega_break) break;
}
Over the years I've seen a few different proposals for how to deal
with breaking out of nested loops. The proposals I've read over the
years have had stuff like the following:
break 3; // break out of this loop, then break out of outer,
then again break out of outer
and also:
continue 3; // break out of this loop, then break out of the
outer, then continue the outer
I don't think I've ever seen a proposal though that deals in scopes
rather than loops. Take the following code for example:
void Func(void)
{
{
lock_guard mylock(my_mutex);
some_global_object.DoSomething();
{
lock_guard mylock2(my_other_mutex);
some_other_global_object.DoAnotherThing();
{
lock_guard mylock3(alien_mutex);
alien_object.KickWatchdog();
}
some_other_global_object.ReportWatchdogKicked():
}
some_global_object.DoTheSecondThing();
}
// Do some other important stuff
}
In the above code we have nested scopes but no loops. If we had an
'__escape' keyword we could escape out of the nested scopes (and
appropriately call destructors when doing so -- thereby unlocking the
mutexes in the above example). The syntax could be as follows:
__escape( [number] , [nothing / continue / break] );
1st operand : positive integer >= 1 (must be an integer literal)
2nd operand : one of 3 possibilities : 'nothing' / 'continue' / 'break'
If the 2nd operand is omitted then the default is 'nothing'.
So let's say you want to escape out of 5 scopes, you would do:
__escape(5);
Note that the following code is ill-formed:
int constexpr monkey = 2;
__escape( monkey );
The 1st operand must be an integer literal -- not merely a compile
time constant, so the compiler shall terminate compilation and issue a
diagnostic.
If you want to escape out of 3 scopes, you do:
__escape(3);
The above is equivalent to:
__escape(3, nothing);
The word 'nothing' here indicates that once you've escaped back three
scopes, you do nothing. The alternative is to 'continue' or to
'break'. Let's say that you want to escape out of 4 scopes, and then
once you find yourself four scopes back in the code, you want to
'continue' the loop:
__escape(4, continue);
Or you want to escape out of 3 scopes, and then once you find yourself
three scopes back in the code, you want to break out of the loop (or
break out of the switch statement):
__escape(3, break);
Furthermore, for the sake of debugging and avoiding adding bugs to
code, there could be a third operand to the __escape operator. The
third operand is the count of how many scopes you expect there to be
from the beginning of the function to where the __escape operator is
encountered. So if you were expecting 5 scopes, you would write:
__escape(3, break, 5);
This third operand would be useful where you have a very big, very
complicated function with many nested loops and nested scopes. If you
have a function that's over 150 lines long, and you've to scroll up
and down to the top and bottom of it, then if you make a change to the
top of the function and then select all of the below code in the text
editor and reduce the indentation by one level, you could change the
count of scopes. This in particular could happen if more than one
person is working on the same code. So let's say you were expecting 5
scopes from where the function began to where the __escape operator
was encountered, but now after the function has been altered by a
colleague of yours, there are 4 scopes instead of 5. Well, when the
compiler encounters the '__escape(3,break,5)' statement, it shall
terminate compilation and issue a diagnostic to say that the expected
number of scopes is wrong. This would drastically reduce the
possibility of introducing a bug.
the 'goto' keyword , but nowadays I think the following strategy is
more prevalent:
bool mega_break = false;
for ( unsigned i = 0u; i < x; ++i )
{
for ( unsigned j = 0u; j < y; ++j )
{
for ( unsigned k = 0u; k < z; ++k )
{
if ( something )
{
mega_break = true;
break;
}
}
if (mega_break) break;
}
if (mega_break) break;
}
Over the years I've seen a few different proposals for how to deal
with breaking out of nested loops. The proposals I've read over the
years have had stuff like the following:
break 3; // break out of this loop, then break out of outer,
then again break out of outer
and also:
continue 3; // break out of this loop, then break out of the
outer, then continue the outer
I don't think I've ever seen a proposal though that deals in scopes
rather than loops. Take the following code for example:
void Func(void)
{
{
lock_guard mylock(my_mutex);
some_global_object.DoSomething();
{
lock_guard mylock2(my_other_mutex);
some_other_global_object.DoAnotherThing();
{
lock_guard mylock3(alien_mutex);
alien_object.KickWatchdog();
}
some_other_global_object.ReportWatchdogKicked():
}
some_global_object.DoTheSecondThing();
}
// Do some other important stuff
}
In the above code we have nested scopes but no loops. If we had an
'__escape' keyword we could escape out of the nested scopes (and
appropriately call destructors when doing so -- thereby unlocking the
mutexes in the above example). The syntax could be as follows:
__escape( [number] , [nothing / continue / break] );
1st operand : positive integer >= 1 (must be an integer literal)
2nd operand : one of 3 possibilities : 'nothing' / 'continue' / 'break'
If the 2nd operand is omitted then the default is 'nothing'.
So let's say you want to escape out of 5 scopes, you would do:
__escape(5);
Note that the following code is ill-formed:
int constexpr monkey = 2;
__escape( monkey );
The 1st operand must be an integer literal -- not merely a compile
time constant, so the compiler shall terminate compilation and issue a
diagnostic.
If you want to escape out of 3 scopes, you do:
__escape(3);
The above is equivalent to:
__escape(3, nothing);
The word 'nothing' here indicates that once you've escaped back three
scopes, you do nothing. The alternative is to 'continue' or to
'break'. Let's say that you want to escape out of 4 scopes, and then
once you find yourself four scopes back in the code, you want to
'continue' the loop:
__escape(4, continue);
Or you want to escape out of 3 scopes, and then once you find yourself
three scopes back in the code, you want to break out of the loop (or
break out of the switch statement):
__escape(3, break);
Furthermore, for the sake of debugging and avoiding adding bugs to
code, there could be a third operand to the __escape operator. The
third operand is the count of how many scopes you expect there to be
from the beginning of the function to where the __escape operator is
encountered. So if you were expecting 5 scopes, you would write:
__escape(3, break, 5);
This third operand would be useful where you have a very big, very
complicated function with many nested loops and nested scopes. If you
have a function that's over 150 lines long, and you've to scroll up
and down to the top and bottom of it, then if you make a change to the
top of the function and then select all of the below code in the text
editor and reduce the indentation by one level, you could change the
count of scopes. This in particular could happen if more than one
person is working on the same code. So let's say you were expecting 5
scopes from where the function began to where the __escape operator
was encountered, but now after the function has been altered by a
colleague of yours, there are 4 scopes instead of 5. Well, when the
compiler encounters the '__escape(3,break,5)' statement, it shall
terminate compilation and issue a diagnostic to say that the expected
number of scopes is wrong. This would drastically reduce the
possibility of introducing a bug.
Received on 2023-05-01 13:53:53