do{switch(0)default:{/*break or continue*/;}/*cleanup*/}while(0);

F

Francois Grieu

I just came into a case where I could use this:

do
{
// local declarations and initializations
switch(0)default:
{
// bunch of if/else; within that:
// - "break" performs cleanup;
// - "continue" skips cleanup.
/*..*/;
}
/*..*/; // cleanup
}
while(0);


[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]


I'm looking for a classical reference on that construct,
like there is one for
<http://www.lysator.liu.se/c/duffs-device.html>


Or perhaps one for the similar:

do
{
// local declarations and initializations
switch(condition)
{
// leaving is done
// - "break" performs cleanup;
// - "continue" skips cleanup.
case 0:
/*..*/;
case 1:
/*..*/;
;
}
/*..*/; // cleanup
}
while(0);



TIA,

Francois Grieu
 
K

Kleuskes & Moos

I just came into a case where I could use this:

   do
   {
   // local declarations and initializations
     switch(0)default:
     {
     // bunch of if/else; within that:
     // - "break" performs cleanup;
     // - "continue" skips cleanup.
     /*..*/;
     }
   /*..*/; // cleanup
   }
   while(0);

[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]

I'm looking for a classical reference on that construct,
like there is one for
<http://www.lysator.liu.se/c/duffs-device.html>

Or perhaps one for the similar:

   do
   {
   // local declarations and initializations
     switch(condition)
     {
     // leaving is done
     // - "break" performs cleanup;
     // - "continue" skips cleanup.
     case 0:
       /*..*/;
     case 1:
       /*..*/;
       ;
     }
   /*..*/; // cleanup
   }
   while(0);

TIA,

   Francois Grieu

I think the correct term is ''kludge'' and, frankly, i prefer gotos to
misappropriating break and continue keywords. But that's just my POV.
 
G

gwowen

[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]

If your algorithm is most naturally/clearly expressed using a 'goto',
use the 'goto' keyword. If you bodge together something that is
LOGICALLY a 'goto' using keywords-that-aren't-goto -- simply to avoid
typing the word 'goto' because you've heard that 'goto' is bad -- then
whatever your reason was for avoiding 'goto' will still apply (only
now you've obfuscated your code for reasons you clearly don't
understand).

Half-remembered saws like "Goto considered harmful" will never beat
clear thinking.
 
A

August Karlstrom

I just came into a case where I could use this:

do
{
// local declarations and initializations
switch(0)default:
{
// bunch of if/else; within that:
// - "break" performs cleanup;
// - "continue" skips cleanup.
/*..*/;
}
/*..*/; // cleanup
}
while(0);

I must be missing something. Why can't you just do something like this?

cleanup = 0;
if (c1) {
...
cleanup = 1;
} else if (c2) {

}...

}
if (cleanup) {

}


August
 
K

Kenny McCormack

[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]

If your algorithm is most naturally/clearly expressed using a 'goto',
use the 'goto' keyword. If you bodge together something that is
LOGICALLY a 'goto' using keywords-that-aren't-goto -- simply to avoid
typing the word 'goto' because you've heard that 'goto' is bad -- then
whatever your reason was for avoiding 'goto' will still apply (only
now you've obfuscated your code for reasons you clearly don't
understand).

Half-remembered saws like "Goto considered harmful" will never beat
clear thinking.

Well, the obvious come-back to this is: What if I work in an environment
(i.e., company) where use of 'goto' is banned? Then you are suggesting that
I get a new job?
 
K

Kleuskes & Moos

gwowen   said:
[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]
If your algorithm is most naturally/clearly expressed using a 'goto',
use the 'goto' keyword.  If you bodge together something that is
LOGICALLY a 'goto' using keywords-that-aren't-goto -- simply to avoid
typing the word 'goto' because you've heard that 'goto' is bad -- then
whatever your reason was for avoiding 'goto' will still apply (only
now you've obfuscated your code for reasons you clearly don't
understand).
Half-remembered saws like "Goto considered harmful" will never beat
clear thinking.

Well, the obvious come-back to this is: What if I work in an environment
(i.e., company) where use of 'goto' is banned?  Then you are suggestingthat
I get a new job?

If you can, since obviously the management is _real_ shitty.

Otherwise heed the advice of Karlstrom, below or hide the goto's in a
bunch of macro's and sell them as innovative new approach to local
exception handing. The suits will never know the difference.
 
F

Francois Grieu

I must be missing something. Why can't you just do something like this?

cleanup = 0;
if (c1) {
...
cleanup = 1;
} else if (c2) {

}...

}
if (cleanup) {

}

I *CAN*. But I'd rather not. I know that most if not all the compilers I
use will not notice that the "cleanup" variable can be suppressed, and
knowing that an extra variable exists and code is here to set and test
it is causing me brain pain. I'd rather use two gotos (or a single goto
and a deep nesting of ifs, which often will do the job), and get
basically the "right" executable.

If I rationalize: on low-end platforms (PIC..) that I often use, RAM
size is a few hundred bytes and program space a few kbytes, so every
byte counts. Even on relatively powerful CPUs (Arm, x86) and when memory
is not an issue, using one more register may force the shift of
something into memory rather than in a register, or/and a little extra
code could abruptly reduce cache efficiency, and in turn slow down
things considerably. I bet I could construct an articial demo where the
suggested change increases execution time/power draw quite perceptibly,
say by 30%.

Francois Grieu
 
F

Francois Grieu

[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]

If your algorithm is most naturally/clearly expressed using a 'goto',
use the 'goto' keyword. If you bodge together something that is
LOGICALLY a 'goto' using keywords-that-aren't-goto -- simply to avoid
typing the word 'goto' because you've heard that 'goto' is bad -- then
whatever your reason was for avoiding 'goto' will still apply (only
now you've obfuscated your code for reasons you clearly don't
understand).

Half-remembered saws like "Goto considered harmful" will never beat
clear thinking.

I'm not the kind to *blindly* obey a coding recipe. I'm really looking
at developing idioms that make the code clearer, without leaving the
simplicity and portability of C for these other languages with
try/catch. I do prefer:

do
{
/*..*/
if (errorcondition1)
break;
/*..*/
if (errorcondition2)
break;
/*..*/
if (errorcondition3)
break;
/*..*/
}
while(0);

to the alternatives using goto

{
/*..*/
if (errorcondition1)
goto lerr;
/*..*/
if (errorcondition2)

/*..*/
if (errorcondition3)
goto lerr;
/*..*/
lerr:;
}

[one real drawback of this one is that you can't cut&paste that idiom
twice in the same function without renaming the label, for C has no way
to make labels local to a bloc]


or deep nesting
{
/*..*/
if (!errorcondition1)
{
/*..*/
if (!errorcondition2)
{
/*..*/
if (!errorcondition3)
{
/*..*/
}
}
}
}

or a variable to remember that there has been an error, which makes the
source code verbose and the executable perceptibly bigger and (rarely
perceptibly) less efficient. I just can't stand that idea.


Sometime, the "dummy do break while" idiom is just not enough, so I'm
considering "dummy do switch break continue while".


Francois Grieu
 
K

Kleuskes & Moos

I *CAN*. But I'd rather not. I know that most if not all the compilers I
use will not notice that the "cleanup" variable can be suppressed, and
knowing that an extra variable exists and code is here to set and test
it is causing me brain pain. I'd rather use two gotos (or a single goto
and a deep nesting of ifs, which often will do the job), and get
basically the "right" executable.

If I rationalize: on low-end platforms (PIC..) that I often use, RAM
size is a few hundred bytes and program space a few kbytes, so every
byte counts. Even on relatively powerful CPUs (Arm, x86) and when memory
is not an issue, using one more register may force the shift of
something into memory rather than in a register, or/and a little extra
code could abruptly reduce cache efficiency, and in turn slow down
things considerably. I bet I could construct an articial demo where the
suggested change increases execution time/power draw quite perceptibly,
say by 30%.

   Francois Grieu

If such finegrained control is needed, wouldn't you be better off
using assembly? Most uC targeted compilers i know of mix the two quite
well.

From what i read portability isn't a great concern, since you're quite
specific on the platform, so that can't be the issue.
 
T

tom st denis

gwowen   said:
[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]
If your algorithm is most naturally/clearly expressed using a 'goto',
use the 'goto' keyword.  If you bodge together something that is
LOGICALLY a 'goto' using keywords-that-aren't-goto -- simply to avoid
typing the word 'goto' because you've heard that 'goto' is bad -- then
whatever your reason was for avoiding 'goto' will still apply (only
now you've obfuscated your code for reasons you clearly don't
understand).
Half-remembered saws like "Goto considered harmful" will never beat
clear thinking.

Well, the obvious come-back to this is: What if I work in an environment
(i.e., company) where use of 'goto' is banned?  Then you are suggestingthat
I get a new job?

Potentially yes [all else being weighed in] I'd start looking for a
new job. At the point where management felt the need to micromanage
my syntax I'd consider it a hostile work environment.

As to the general theme, the only real uses for goto's are 1) cleanup
[exception handling] and 2) getting out of deeply nested loops
cleanly. Any other use is probably a misuse [there will be other
cleaner ways to achieve the same thing]. The usual rhetoric about
"gotos are bad" is just that. In the same way I can write

(a == 4) && somefunc();

instead of

if (a == 4) {
somefunc();
}

While the former is less desirable it doesn't mean that && is a bad
operator, it means that you can use it in ways that aren't ideal.
Same with most other keywords and operators.

Tom
 
K

Keith Thompson

gwowen said:
[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]

If your algorithm is most naturally/clearly expressed using a 'goto',
use the 'goto' keyword. If you bodge together something that is
LOGICALLY a 'goto' using keywords-that-aren't-goto -- simply to avoid
typing the word 'goto' because you've heard that 'goto' is bad -- then
whatever your reason was for avoiding 'goto' will still apply (only
now you've obfuscated your code for reasons you clearly don't
understand).

Half-remembered saws like "Goto considered harmful" will never beat
clear thinking.

But break and continue aren't *just* disguised gotos. They're
tightly restricted gotos. They branch only to a specific point in
the code (the end of a certain construct), and unlike gotos they
can't branch to an earlier point in the code.

If you really want unmaintainable spaghetti code, backward gotos
are the way to do it.

I'm not saying that using a dummy "do { .. } while (0);" just to
be able to use "break" is a *great* idea (I agree it's a kludge),
but there are valid arguments in favor of it.

Another approach for the goto-phobic is to use a call-once function
so you can return from it.

Perhaps if C had a few more non-goto control structures (*named*
break and continue, and perhaps something to terminate a block),
such kludges wouldn't be needed.
 
S

Stefan Ram

Kenneth Brody said:
Perhaps he should read "'Goto Considered Harmful' Considered Harmful"?

»Let X be an N x N matrix of integers. Write a program
that will print the number of the first all-zero row of
X, if any.«

untested:

#include <stdio.h>

extern int const * const * const X;
extern int const N;

static inline int is_all_zero
( int const * const row, int const cols )
{ result = 1;
for( register int col = 0; col != cols; ++col )
if( m[ col ]){ result = 0; col = cols; } /* I prefer »break«
or »return« here, but I wanted to show that a version without
jumps is not that much more complicated. */
return result; }

static inline int first_all_zero_row_number_of
( int const * const * const m,
int const rows,
int const cols )
{ int result = -1;
for( int row = 0; row != rows; ++row )
if( is_all_zero( m[ row ]))
{ result = row; row = rows; } /* I prefer »break« or »return« here,
but I wanted to show that a version without jumps is not that
much more complicated. */
return result; }

int main( void )
{ int const first = first_all_zero_row_number_of( X, N, N );
if( first >= 0 )printf( "%d\n", first ); }
 
B

Ben Bacarisse

»Let X be an N x N matrix of integers. Write a program
that will print the number of the first all-zero row of
X, if any.«

untested:
static inline int is_all_zero
( int const * const row, int const cols )
{ result = 1;
for( register int col = 0; col != cols; ++col )
if( m[ col ]){ result = 0; col = cols; } /* I prefer »break«
or »return« here, but I wanted to show that a version without
jumps is not that much more complicated. */
return result; }

You forced the use of a result variable and an assignment to the loop
control variable by insisting on a for loop. I think this is more
natural as a while loop:

int col = 0;
while (col != cols && m[col]) col++;
return col == cols;
static inline int first_all_zero_row_number_of
( int const * const * const m,
int const rows,
int const cols )
{ int result = -1;
for( int row = 0; row != rows; ++row )
if( is_all_zero( m[ row ]))
{ result = row; row = rows; } /* I prefer »break« or »return« here,
but I wanted to show that a version without jumps is not that
much more complicated. */
return result; }

And I'd do the same here.
 
P

Phil Carmody

Keith Thompson said:
gwowen said:
[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]

If your algorithm is most naturally/clearly expressed using a 'goto',
use the 'goto' keyword. If you bodge together something that is
LOGICALLY a 'goto' using keywords-that-aren't-goto -- simply to avoid
typing the word 'goto' because you've heard that 'goto' is bad -- then
whatever your reason was for avoiding 'goto' will still apply (only
now you've obfuscated your code for reasons you clearly don't
understand).

Half-remembered saws like "Goto considered harmful" will never beat
clear thinking.

But break and continue aren't *just* disguised gotos. They're
tightly restricted gotos. They branch only to a specific point in
the code (the end of a certain construct), and unlike gotos they
can't branch to an earlier point in the code.

Continue can. And does. Always.

Phil
 
K

Keith Thompson

Phil Carmody said:
Keith Thompson said:
gwowen said:
[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]

If your algorithm is most naturally/clearly expressed using a 'goto',
use the 'goto' keyword. If you bodge together something that is
LOGICALLY a 'goto' using keywords-that-aren't-goto -- simply to avoid
typing the word 'goto' because you've heard that 'goto' is bad -- then
whatever your reason was for avoiding 'goto' will still apply (only
now you've obfuscated your code for reasons you clearly don't
understand).

Half-remembered saws like "Goto considered harmful" will never beat
clear thinking.

But break and continue aren't *just* disguised gotos. They're
tightly restricted gotos. They branch only to a specific point in
the code (the end of a certain construct), and unlike gotos they
can't branch to an earlier point in the code.

Continue can. And does. Always.

C99 6.8.6.2 (emphasis added):

A continue statement causes a jump to the loop-continuation
portion of the smallest enclosing iteration statement; that is,
*to the end* of the loop body.

The (optional) subsequent branch to the top of the loop is the normal
operation of the loop itself, not a result of the continue statement.

This is particularly relevant for a continue in a do-while loop, which
causes the condition at the bottom to be re-evaluated.
 
P

Phil Carmody

Keith Thompson said:
Phil Carmody said:
Keith Thompson said:
[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]

If your algorithm is most naturally/clearly expressed using a 'goto',
use the 'goto' keyword. If you bodge together something that is
LOGICALLY a 'goto' using keywords-that-aren't-goto -- simply to avoid
typing the word 'goto' because you've heard that 'goto' is bad -- then
whatever your reason was for avoiding 'goto' will still apply (only
now you've obfuscated your code for reasons you clearly don't
understand).

Half-remembered saws like "Goto considered harmful" will never beat
clear thinking.

But break and continue aren't *just* disguised gotos. They're
tightly restricted gotos. They branch only to a specific point in
the code (the end of a certain construct), and unlike gotos they
can't branch to an earlier point in the code.

Continue can. And does. Always.

Yes, this is wrong. But I think only 1/3 wrong.
C99 6.8.6.2 (emphasis added):

A continue statement causes a jump to the loop-continuation
portion of the smallest enclosing iteration statement; that is,
*to the end* of the loop body.

The (optional) subsequent branch to the top of the loop is the normal
operation of the loop itself, not a result of the continue statement.

Hmmm, I think I disagree. It is for a do {} while, certainly. However,
in the absense of a definition of "loop-continuation portion", For me
it's obviously expression_3 for a for loop (which is at the top) and
the condition for a while loop (which is at the top). Their
explication introduces more contradiction than explanation. So, to
their "that is", I'd say "no, that isn't". It would have been better
if they'd just used the final clause. Were I to be pedantic, I'd take
issue with your '(optional)' is 2/3 wrong, as it's not optional in
whiles and fors, only in dos.
This is particularly relevant for a continue in a do-while loop, which
causes the condition at the bottom to be re-evaluated.

Yup, you definitely got me there - good call. I dunno why, I was just
in a 'for' train of thought, and a bit blinkered.

Phil
 
L

luser- -droog

[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]
If your algorithm is most naturally/clearly expressed using a 'goto',
use the 'goto' keyword.  If you bodge together something that is
LOGICALLY a 'goto' using keywords-that-aren't-goto -- simply to avoid
typing the word 'goto' because you've heard that 'goto' is bad -- then
whatever your reason was for avoiding 'goto' will still apply (only
now you've obfuscated your code for reasons you clearly don't
understand).
Half-remembered saws like "Goto considered harmful" will never beat
clear thinking.

Perhaps he should read "'Goto Considered Harmful' Considered Harmful"?

     http://www.ecn.purdue.edu/ParaMount/papers/rubin87goto.pdf

Or Knuth's "Structured programming with go to statements", in the
collection
<<Literate Programming>>.
 
S

Stefan Ram

Francois Grieu said:
or deep nesting
{
/*..*/
if (!errorcondition1)
{
/*..*/
if (!errorcondition2)
{
/*..*/
if (!errorcondition3)
{
/*..*/

It is somewhat sad that often the existence of functions
is ignored, leading to monolithic code such as seen above.
(Also in all of the code examples of the preceding post
that are not quoted here.)

In the program below, instead, each step is assigned to a
function, often a function with a meaningful name. The deep
nesting from above would occur again, if one would nest the
function blocks at the site where a function is called.

The following example code reads, sorts, ands prints a list of ints.
But it is shown here for the way it handles error conditions,
including proper clean-up code for all resource allocated.
The functions defined below return error codes as a non-zero value.

To read the program, it is best to start at the bottom at »main«.
The actual »main« program with the sequence »read, sort,
print« is the function »main2«.

The following code neither uses explicit jump instructions
nor does it contain deeply nested blocks.

#include <stdio.h> /* scanf, printf */
#include <stdlib.h> /* EXIT_SUCCESS */
#include "simclist-1.4.3/simclist.h" /* list_... */

int appendvalue( list_t * const list, int *looping )
{ int value;
if( 1 != scanf( "%d", &value ))return 1;
if( value >= 0 )
{ if( list_append( list, &value )< 0 )return 2; }
else *looping = 0;
return 0; }

int read( list_t * const list )
{ int looping = 1; while( looping )
{ if( appendvalue( list, &looping ))return 1; }
return 0; }

int sort( list_t * const list )
{ if( list_attributes_comparator( list,
list_comparator_int32_t ))return 1;
else if( list_sort( list, -1 ))return 2;
else return 0; }

int print( list_t * const list )
{ if( list_iterator_start( list )<= 0 )
return 1;
{ while( list_iterator_hasnext( list ))
{ void * next = list_iterator_next( list );
if( next )
{ if( printf( "%d\n", *( int * )next )< 0 )return 2; }
else break; }
if( !list_iterator_stop( list ))return 3; }
return 0; }

int main2( list_t * const list )
{ if( !list_attributes_copy( list, list_meter_int32_t, 1 ))
{ if( read( list ))return 1;
else if( sort( list ))return 2;
{ printf("Sorted values:\n");
if( print( list ))return 3; }}
return 0; }

int main1( list_t * const list )
{ int result;
if( list_init( list ))result = 1;
else
{ if( main2( list ))result = 2;
list_destroy( list ); }
return result; }

int main( void )
{ list_t list;
return main1( &list )? EXIT_FAILURE : EXIT_SUCCESS; }
 
S

Stefan Ram

Keith Thompson said:
But break and continue aren't *just* disguised gotos.

If one would say that »break« and »continue« were
just disguised »goto« instructions, one also might
go so far as to say that that »if« and »while«
or function calls are disguised »goto« instructions,
and - last, not least - »switch« statements:

while( pc != 99 )
{ switch( pc++ )
{ case 0: ...; break;
case 1: ...; break;
case 2: ...; break; ... }}

Above, a »goto« becomes »pc = ...;«.
 
B

Ben Bacarisse

luser- -droog said:
On Jul 22, 7:49 am, Francois Grieu<[email protected]>  wrote:
[Notes: This is 'only' a way to avoid gotos. It places
restrictions on the use of other do/while/for/switch.
The second brace could be before default, but I find
the dummy switch idiom more recognizable that way.]
If your algorithm is most naturally/clearly expressed using a 'goto',
use the 'goto' keyword.  If you bodge together something that is
LOGICALLY a 'goto' using keywords-that-aren't-goto -- simply to avoid
typing the word 'goto' because you've heard that 'goto' is bad -- then
whatever your reason was for avoiding 'goto' will still apply (only
now you've obfuscated your code for reasons you clearly don't
understand).
Half-remembered saws like "Goto considered harmful" will never beat
clear thinking.

Agreed. Also, when you meet anyone who comes out with either "goto
considered harmful" or who dismissed the idea out of hand, ask them what
Dijkstra's argument actually *is* (i.e. ask them what it is they are
defending or rejecting). You will be surprised by how many people have
a strong view of this paper without knowing what's in it.

Only if you want to see how not to counter the argument.
Or Knuth's "Structured programming with go to statements", in the
collection
<<Literate Programming>>.

Which states:

"This study focuses largely on two issues: (a) improved syntax for
iterations and error exits, making it possible to write a larger class
of programs clearly and efficiently without goto statements; (b)
a methodology of program design, beginning with readable and correct,
but possibly inefficient programs that are systematically transformed
if necessary into efficient and correct, but possibly less readable
code."

In other words it is not written as any sort of refutation of Dijkstra's
paper but aims to take the debate forward by finding ways to mitigate
the dangers posed by "the unbridled use of the goto" (Dijkstra's
phrase).

Later on Knuth writes:

"some readers [...] are convinced that abolition of go to statements
is merely a fad. and they may see this title and think, 'Aha! Knuth is
rehabilitating the go to statement, and we can go back to our old ways
of programming again.'"

This is crucial. You can't understand Dijkstra's paper unless you put
it in context. I remember the "old ways" of spaghetti code but most
recent programmers do not because, in essence, Dijkstra won the
argument; almost no one advocates the unbridled use of the goto anymore.
 

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
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top