473,385 Members | 2,015 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

Blocks and Their Use

I have a question regarding *use* of blocks.

In Plauger's THE STANDARD C LIBRARY in xfmtval.c in Chapter 6 I noticed in
function _Fmtval() that after some processing of 50 lines or so he creates a
block

char *_Fmtval( ... )
{

/* .. 50 lines or so of code ... */

{ /* build string in buf under control of fmt */
char *end, *s;
const char *g;
size_t i, ns;

for (s = buf; *fmt; ++fmt, s+= strlen(s))
/* ... */

}
return (buf);

} /* end function _Fmtval */

We all know this is legitimate, but I admit I have not seen this technique
employed before. Plauger also uses this technique in setlocal.c in the same
chapter.

Is this technique recommended, or are there caveats? I can see some C Coding
Guidelines excluding its use, if not specifically then by implication,
perhaps.

Having just added some code to an existing file in a void function, I
elected to employ this techique. It takes the form:

if ( error )
{
/* report error */
return;
}

if ( another_error )
{
/* report this error */
return;
}

/* ... a few more error checks ... */

/* no errors here, start processing */
{
unsigned char c;
/* ... more definitions ... */

/*... processing ... */
}

Which I think works well because the variables defined in the block are
necessary only if the errors looked for above are not present.

Has anyone any constructive comments about this technique?

--
Martin

Mar 25 '07 #1
34 1466
"Martin" <martin.o_brien@[no-spam]which.netwrites:
I have a question regarding *use* of blocks.

In Plauger's THE STANDARD C LIBRARY in xfmtval.c in Chapter 6 I noticed in
function _Fmtval() that after some processing of 50 lines or so he creates a
block

char *_Fmtval( ... )
{

/* .. 50 lines or so of code ... */

{ /* build string in buf under control of fmt */
char *end, *s;
const char *g;
size_t i, ns;

for (s = buf; *fmt; ++fmt, s+= strlen(s))
/* ... */

}
return (buf);

} /* end function _Fmtval */

We all know this is legitimate, but I admit I have not seen this
technique employed before. Plauger also uses this technique in
setlocal.c in the same chapter.

Is this technique recommended, or are there caveats? I can see some
C Coding Guidelines excluding its use, if not specifically then by
implication, perhaps.
After reading the above, it wasn't at all clear to me what you meant
by "this technique". Reading the code after that that uses the
technique, I see that you're referring to introducing a block for the
purpose of declaring local variables that aren't needed by the
preceding code. (The subject "Blocks and Their Use" should have clued
me in!)

Yes, that's a perfectly valid technique, and I see nothing wrong with
it stylistically. Note that it doesn't necessarily save you any
memory; you might assume that the variables in the block won't be
allocated until and unless you enter the block, but that's not
guaranteed, and a compiler is free to allocate all of a function's
local variables (including ones in inner blocks) on entry to the
function. (Except for VLAs, I suppose.) But it does make it clear
that those variables are only used within the block, which makes the
code easier to understand.

Try moving the declarations of end, s, g, i, and ns to the beginning
of the function, above the "50 lines or so of code". Without
carefully reading all 50 lines, you can't be sure where those
variables are used.

I was about to express surprise that the code recomputes strlen(s) on
each iteration of the loop. It's a common newbie error to do
something like:

for (i = 0; i < strlen(s); i ++) {
/* ... code that uses s[i] ... */
}

The problem is that strlen() has to scan the entire string, making the
loop O(N**2). Saving the value of strlen(s) in a variable would make
it O(N), a significant improvement.

But of course P.J. Plauger didn't make such a newbie mistake.
strlen(s) *has* to be recomputed on each iteration, because it changes
on each iteration. The "s += strlen(s)" is simply a clever way to
advance the pointer s up to the next '\0' character.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Mar 25 '07 #2
Martin wrote:
I have a question regarding *use* of blocks.
<snip>
>
Having just added some code to an existing file in a void function, I
elected to employ this techique. It takes the form:

if ( error )
{
/* report error */
return;
}

if ( another_error )
{
/* report this error */
return;
}

/* ... a few more error checks ... */

/* no errors here, start processing */
{
unsigned char c;
/* ... more definitions ... */

/*... processing ... */
}

Which I think works well because the variables defined in the block are
necessary only if the errors looked for above are not present.

Has anyone any constructive comments about this technique?
It is a perfectly valid technique for containing the scope of variables.
It is also a strong hint that the code in the block might be better off
in its own function.

--
Ian Collins.
Mar 25 '07 #3
On Sun, 25 Mar 2007 21:15:59 +0100, "Martin"
<martin.o_brien@[no-spam]which.netwrote in comp.lang.c:
I have a question regarding *use* of blocks.

In Plauger's THE STANDARD C LIBRARY in xfmtval.c in Chapter 6 I noticed in
function _Fmtval() that after some processing of 50 lines or so he creates a
block

char *_Fmtval( ... )
{

/* .. 50 lines or so of code ... */

{ /* build string in buf under control of fmt */
char *end, *s;
const char *g;
size_t i, ns;

for (s = buf; *fmt; ++fmt, s+= strlen(s))
/* ... */

}
return (buf);

} /* end function _Fmtval */

We all know this is legitimate, but I admit I have not seen this technique
employed before. Plauger also uses this technique in setlocal.c in the same
chapter.

Is this technique recommended, or are there caveats? I can see some C Coding
Guidelines excluding its use, if not specifically then by implication,
perhaps.

Having just added some code to an existing file in a void function, I
elected to employ this techique. It takes the form:

if ( error )
{
/* report error */
return;
}

if ( another_error )
{
/* report this error */
return;
}

/* ... a few more error checks ... */

/* no errors here, start processing */
{
unsigned char c;
/* ... more definitions ... */

/*... processing ... */
}

Which I think works well because the variables defined in the block are
necessary only if the errors looked for above are not present.

Has anyone any constructive comments about this technique?
I use the technique of adding a block merely to open a local scope
most often when I am maintaining older code, my own or somebody
else's.

There is, unfortunately, a lot of older C code with very large
functions and all the variables used anywhere in the function defined
at the top.

In fact, I have seen C functions with dozens of cases in a switch
statement, all written in line, where some counter variable,
invariably named 'i', is used in two or three of the cases, and
defined way up at the top of the file.

The alternative is quite simply to put each case handler in its own
scope:

case '1':
{
/* define variables if needed */
break;
}

Opening a new scope allows you to make sure that you can define new
variables for added code in the middle of a function and helps you
avoid accidentally modifying the value of an existing on, especially
when it is not easy to see all the places where it might be used.

On the other hand, I never a block just to define a local scope in new
code. The code can always be structured better.

In your particular example, which could be a simplification, you could
open that block with an else or else if.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
Mar 25 '07 #4
"Martin" <martin.o_brien@[no-spam]which.netwrote:

# Is this technique recommended, or are there caveats? I can see some C Coding
# Guidelines excluding its use, if not specifically then by implication,
# perhaps.

It's useful if you don't want to scan lots of code to make sure some
code you're inserting doesn't step on existing code.

Also useful if you have a real macro processor and you're building up
the code in diverse locations.

--
SM Ryan http://www.rawbw.com/~wyrmwif/
Leave it to the Catholics to destroy existence.
Mar 25 '07 #5

"Martin" <martin.o_brien@[no-spam]which.netwrote in message
[block scope variables] Which I think works well because the variables
defined in the block are necessary only if the errors looked for above are
not present.

Has anyone any constructive comments about this technique?
There is a limit to the number of scopes a programmer can cope with. We
already have global, file scope, and function scope variables. Adding
another scope would be too much, except that globals are typically used so
rarely that we can discount them. However when you have a hierarchy of
blocks each with different variables, the code can become unreadable.
I am against block scope variables because they encourage hacking rather
than thinking of the function as a unit, as well as on account of their
potential for introducing too many scopes. However I am not militantly
against them in short leaf blocks.

--
Free games and programming goodies.
http://www.personal.leeds.ac.uk/~bgy1mm
Mar 25 '07 #6

"SM Ryan" <wy*****@tango-sierra-oscar-foxtrot-tango.fake.orgwrote in
message
"Martin" <martin.o_brien@[no-spam]which.netwrote:

# Is this technique recommended, or are there caveats? I can see some C
Coding
# Guidelines excluding its use, if not specifically then by implication,
# perhaps.

It's useful if you don't want to scan lots of code to make sure some
code you're inserting doesn't step on existing code.
My point exactly.

Mar 25 '07 #7
In article <ea*********************@bt.com>,
>There is a limit to the number of scopes a programmer can cope with. We
already have global, file scope, and function scope variables. Adding
another scope would be too much, except that globals are typically used so
rarely that we can discount them. However when you have a hierarchy of
blocks each with different variables, the code can become unreadable.
I can't deny that you may find that the case, but examples from other
languages such as Lisp show that it's just a matter of opinion,
somewhat influenced by the syntax of the language in question.

I find that, for code of equal length, introducing sub-blocks often
improves readability by making the scope of variables explicit. On
the other hand, the amount of vertical whitespace it introduces in
most C styles works in the opposite direction.

-- Richard
--
"Consideration shall be given to the need for as many as 32 characters
in some alphabets" - X3.4, 1963.
Mar 26 '07 #8
On Sun, 25 Mar 2007 15:16:15 -0700, in comp.lang.c , Keith Thompson
<ks***@mib.orgwrote:
>you might assume that the variables in the block won't be
allocated until and unless you enter the block, but that's not
guaranteed, and a compiler is free to allocate all of a function's
local variables (including ones in inner blocks) on entry to the
function.
Really? Pathological case:

void foo()
{
double x = 4.5;
{
int x = 3;
}
{
char x[4]={"foo"};
}
}

Or is it a case of "it can, provided there are no side-effects"?

--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
Mar 26 '07 #9
Mark McIntyre wrote:
On Sun, 25 Mar 2007 15:16:15 -0700, in comp.lang.c , Keith Thompson
<ks***@mib.orgwrote:

>>you might assume that the variables in the block won't be
allocated until and unless you enter the block, but that's not
guaranteed, and a compiler is free to allocate all of a function's
local variables (including ones in inner blocks) on entry to the
function.


Really? Pathological case:

void foo()
{
double x = 4.5;
{
int x = 3;
}
{
char x[4]={"foo"};
}
}

Or is it a case of "it can, provided there are no side-effects"?
The compiler is free to allocate the *storage* (typically space on the
stack). It could allocate space for sizeof(int)+4 bytes, or 4 bytes.

--
Ian Collins.
Mar 26 '07 #10
In article <9t********************************@4ax.com>,
Mark McIntyre <ma**********@spamcop.netwrote:
>On Sun, 25 Mar 2007 15:16:15 -0700, in comp.lang.c , Keith Thompson
<ks***@mib.orgwrote:
>>you might assume that the variables in the block won't be
allocated until and unless you enter the block, but that's not
guaranteed, and a compiler is free to allocate all of a function's
local variables (including ones in inner blocks) on entry to the
function.

Really? Pathological case:

void foo()
{
double x = 4.5;
{
int x = 3;
}
{
char x[4]={"foo"};
}
}
could easily be compiled the same as this, with all local variables allocated
at the top:

void foo()
{
double x_the_first;
int x_the_second;
char x_the_third[4];

x_the_first = 4.5;
{
x_the_second = 3;
}
{
strcpy(x_the_third, "foo");
}
}

--
Alan Curry
pa****@world.std.com
Mar 26 '07 #11
Mark McIntyre <ma**********@spamcop.netwrites:
On Sun, 25 Mar 2007 15:16:15 -0700, in comp.lang.c , Keith Thompson
<ks***@mib.orgwrote:
>>you might assume that the variables in the block won't be
allocated until and unless you enter the block, but that's not
guaranteed, and a compiler is free to allocate all of a function's
local variables (including ones in inner blocks) on entry to the
function.

Really? Pathological case:

void foo()
{
double x = 4.5;
{
int x = 3;
}
{
char x[4]={"foo"};
}
}

Or is it a case of "it can, provided there are no side-effects"?
What problem does this create?

Scope and storage duration are two different things. Assuming that,
let's say, double is 8 bytes and int is 4 bytes, an implemention could
allocate 16 bytes on entry to foo(), or 12 bytes if it chooses to
overlay storage the parallel blocks. Each variable has its own
address (the addresses of the int and the char[4] may or may not be
the same), and any reference to x will resolve the correct object for
the scope in which the reference appears.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Mar 26 '07 #12
Mark McIntyre wrote:
On Sun, 25 Mar 2007 15:16:15 -0700, in comp.lang.c , Keith Thompson
<ks***@mib.orgwrote:
>>you might assume that the variables in the block won't be
allocated until and unless you enter the block, but that's not
guaranteed, and a compiler is free to allocate all of a function's
local variables (including ones in inner blocks) on entry to the
function.

Really? Pathological case:

void foo()
{
double x = 4.5;
{
int x = 3;
}
{
char x[4]={"foo"};
}
}
What about it? The compiler can allocate space for a double, an int,
and a char[] on function entry, using the first for the first `x`,
the second for the second, and the third for the third.

Or it could, on a suitable machine, allocate space for a double and
for an int-or-char[4], using the second for both nested `x`s.

[Or it could allocate no space at all and implement the function with
a `mov pc, r14` or your local flavour of return instruction ...]

--
The second Jena user conference! http://hpl.hp.com/conferences/juc2007/
"I just wonder when we're going to have to sit down and re-evaluate /Sahara/
our decision-making paradigm."

Hewlett-Packard Limited registered office: Cain Road, Bracknell,
registered no: 690597 England Berks RG12 1HN

Mar 27 '07 #13
On Mon, 26 Mar 2007 16:36:20 -0700, in comp.lang.c , Keith Thompson
<ks***@mib.orgwrote:
>Mark McIntyre <ma**********@spamcop.netwrites:
>On Sun, 25 Mar 2007 15:16:15 -0700, in comp.lang.c , Keith Thompson
<ks***@mib.orgwrote:
>>>you might assume that the variables in the block won't be
allocated until and unless you enter the block, but that's not
guaranteed, and a compiler is free to allocate all of a function's
local variables (including ones in inner blocks) on entry to the
function.

Or is it a case of "it can, provided there are no side-effects"?
I guess your answer was "yes"...
>let's say, double is 8 bytes and int is 4 bytes, an implemention could
allocate 16 bytes on entry to foo(), or 12 bytes if it chooses to
overlay storage the parallel blocks.
I believe the point you're making is that the variable *names* don't
exist in the compiled code so as long as the compiler correctly
resolves each reference to the right object, it can allocate storage
at whatever point suits it best - even at programme startup should it
so desire. Obiviously such allocations schemes would be suboptimal in
many environments.

--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
Mar 27 '07 #14
Well, as I expected, I got a lot of informed responses. Thank you.

It seems that the use of blocks does not guarantee a saving in terms of
allocation of variables - one of the reasons I found them attractive and
*assumed* was a reason that Plauger uses them.

However, another reason I found attrractive for the use of blocks was the
containment of variables defined within the block, to that block, useful in
code maintenance as some respondents have pointed out.

Ian Collins mentioned that using a block is a "strong hint that the code in
the block might be better off in its own function" - although true, it also
means that if variables are used within the block from an outer block, the
function would have to take them in as arguments.

My main question concerned my code. Basically, I am not fond of having a
situation where you do this:

if ( /* no error */ )
{
ret_val = SUCCESS;
/* lots of code */
}
else
{
ret_val = FAIL;
}

return ret_val;

I think this is better written:

if ( an error )
{
return FAIL;
}

/* now we can process, having obviated any errors */
then
return SUCCESS;
My original post actually has several situations:

if ( error_1 )
return error_1_code;

if (error_2_
return error_2_code;

/* a couple more */

/* now process having error checked above */
{
int var_1; /* these variables not needed if errors detected above
*/
char var_2;
/* processing */
}

Pardon the pseudo-C approach but I'm sure you know what I'm trying to
demonstrate. In fact, the processing I'm doing is all inside a case
statement in a rather large switch(). I'm maintaining it, by the way, it's
not the way I would have done it. I have made the case statement a block to
further contain the code (prior to Jack Klein's suggestion).

In fact, Jack mentioned "In your particular example, which could be a
simplification, you could
open that block with an else or else if" but I'm not really sure what you
mean, Jack.

---
Martin

Mar 27 '07 #15
Martin wrote:
Well, as I expected, I got a lot of informed responses. Thank you.

It seems that the use of blocks does not guarantee a saving in terms of
allocation of variables - one of the reasons I found them attractive and
*assumed* was a reason that Plauger uses them.

However, another reason I found attrractive for the use of blocks was the
containment of variables defined within the block, to that block, useful in
code maintenance as some respondents have pointed out.

Ian Collins mentioned that using a block is a "strong hint that the code in
the block might be better off in its own function" - although true, it also
means that if variables are used within the block from an outer block, the
function would have to take them in as arguments.
True, the cost of any change has to be weighed against the benefits.
My main question concerned my code. Basically, I am not fond of having a
situation where you do this:

if ( /* no error */ )
{
ret_val = SUCCESS;
/* lots of code */
}
else
{
ret_val = FAIL;
}

return ret_val;

I think this is better written:

if ( an error )
{
return FAIL;
}

/* now we can process, having obviated any errors */
then
return SUCCESS;
This problem has been solved in C99, where you can mix variable
declarations and statements.

--
Ian Collins.
Mar 27 '07 #16
Ian Collins <ia******@hotmail.comwrites:
Martin wrote:
[...]
>My main question concerned my code. Basically, I am not fond of having a
situation where you do this:

if ( /* no error */ )
{
ret_val = SUCCESS;
/* lots of code */
}
else
{
ret_val = FAIL;
}

return ret_val;

I think this is better written:

if ( an error )
{
return FAIL;
}

/* now we can process, having obviated any errors */
then
return SUCCESS;
Where the "now we can process" presumably includes the declaration of
any objects needed for that processing; in C90, this requires a block.
This problem has been solved in C99, where you can mix variable
declarations and statements.
Partly, but even in C99, if you don't introduce a block, the scope of
each variable still extends from its declaration to the end of the
function. More generally, the scope extends from the declaration to
the end of the enclosing block (which may be the entire body of the
function).

If you want parallel scopes, you still need blocks:

if (something) {
/* declare variables here */
do_something ...;
}
else {
/* declare other variables here */
do_something_else ...;
}

The compiler isn't obligated to overlay the two sets of variables, but
it's a reasonable and easy optimization.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Mar 28 '07 #17
Chris Dollin <ch**********@hp.comwrites:
Mark McIntyre wrote:
[...]
>Really? Pathological case:

void foo()
{
double x = 4.5;
{
int x = 3;
}
{
char x[4]={"foo"};
}
}

What about it? The compiler can allocate space for a double, an int,
and a char[] on function entry, using the first for the first `x`,
the second for the second, and the third for the third.

Or it could, on a suitable machine, allocate space for a double and
for an int-or-char[4], using the second for both nested `x`s.
[...]

What do you mean by "on a suitable machine"? The only
machine-specific thing I can think of that would affect this would be
an alignment constraint; even then there could easily be a partial
overlap.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Mar 28 '07 #18
Martin wrote:
>
.... snip ...
>
My main question concerned my code. Basically, I am not fond of
having a situation where you do this:

if ( /* no error */ )
{
ret_val = SUCCESS;
/* lots of code */
}
else
{
ret_val = FAIL;
}
return ret_val;
I think this is better handled as:

if (error) ret_val = FAIL;
else {
ret_val = success;
/* lotsa code */
/* which may include "ret_val = FAIL\" overides */
}
return ret_val;

Which incorporates several guidelines. One is single point of
exit. Another is that the controlling condition is easily visible
from the indentation. A third is that the short condition comes
first, easing finding the controlling condition via the
indentation.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>

--
Posted via a free Usenet account from http://www.teranews.com

Mar 28 '07 #19
In article <ln************@nuthaus.mib.org>,
Keith Thompson <ks***@mib.orgwrote:
>If you want parallel scopes, you still need blocks:

if (something) {
/* declare variables here */
do_something ...;
}
else {
/* declare other variables here */
do_something_else ...;
}

The compiler isn't obligated to overlay the two sets of variables, but
it's a reasonable and easy optimization.
Even with this:

/* declare variables here */
/* declare other variables here */
if (something) {
do_something using only first variables ...;
}
else {
do_something_else using only second variables ...;
}

a compiler can use the same memory for both sets of variables. The
analysis done by modern compilers makes this too an easy optimisation.

-- Richard
--
"Consideration shall be given to the need for as many as 32 characters
in some alphabets" - X3.4, 1963.
Mar 28 '07 #20
"CBFalconer" <cb********@yahoo.comha scritto nel messaggio
news:46***************@yahoo.com...
I think this is better handled as:

if (error) ret_val = FAIL;
else {
ret_val = success;
/* lotsa code */
/* which may include "ret_val = FAIL\" overides */
}
return ret_val;

Which incorporates several guidelines. One is single point of
exit.
In such cases, why

if (error) return FAIL;
/* lotsa code */
return SUCCESS; /*or anything else*/

is so bad? (I know that there are people who don't like writing "return"
(quotation marks excluded) more than once within a function body (even if I
disagree, I can see the point of that, in the case of "normal" working),
but, in the case of error handling, I can't see how a premature return is
worse than having all the function's normal operation within an else block.)
Mar 29 '07 #21
Army1987 wrote:
"CBFalconer" <cb********@yahoo.comha scritto nel messaggio
>I think this is better handled as:

if (error) ret_val = FAIL;
else {
ret_val = success;
/* lotsa code */
/* which may include "ret_val = FAIL\" overides */
}
return ret_val;

Which incorporates several guidelines. One is single point of
exit.

In such cases, why

if (error) return FAIL;
/* lotsa code */
return SUCCESS; /*or anything else*/

is so bad? (I know that there are people who don't like writing
"return" (quotation marks excluded) more than once within a
function body (even if I disagree, I can see the point of that,
in the case of "normal" working), but, in the case of error
handling, I can't see how a premature return is worse than
having all the function's normal operation within an else block.)
Did I say it was totally evil? At any rate, for one thing while
reading "lotsa code" you can't inherently see that execution
depends on passing the error test. For another, it is much easier
to trap the return point.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>

--
Posted via a free Usenet account from http://www.teranews.com

Mar 30 '07 #22
Army1987 said:
[...] why

if (error) return FAIL;
/* lotsa code */
return SUCCESS; /*or anything else*/

is so bad?
Well, it isn't really so terribly bad.
(I know that there are people who don't like writing
"return" (quotation marks excluded) more than once within a function
body (even if I disagree, I can see the point of that, in the case of
"normal" working), but, in the case of error handling, I can't see how
a premature return is worse than having all the function's normal
operation within an else block.)

Well, you've put your finger on something rather important here - the
concept of "normal working". When you're just bailing out because of an
error that means you can't possibly go on, then (as long as you've
cleaned up after yourself, so to speak) an early return is no big deal,
and the only reason one might want to avoid it is mere style, in that
it simplifies control flow if everything aims towards one exit point
and therefore some people find it easier to follow the code.

But where it really, really matters is in code like this:

double ctx2174_frob_foo(foo *f, bar *b, baz *z, quux *q)
{
int i, j, k, l, m, n, o, p;
double foo_rate = 1.0;
double d = get_init_d_from_db(f->red, b->arney);

if(d < 0)
{
push_error(__FILE__, __LINE__, "ctx2174_frob_foo", "bad d");
return d;
}

for(i = b->a->barracus->lowentry;
i < q->u->ality->street;
i += z->e->bedee)
{
j = f->rank + b->ill[i]->bob;
d += b->ubble[i] * f->roth[i][j / 2] * q->uestion;
foo_rate *= d / b->arrack->room[j];
if(z->ygote[i] foo_rate)
{
for(k = 0; k / j f->eed->the->world; k += i)
{
if(k + 4 < 3 / j)
{
push_error(__FILE__, __LINE__,
"ctx2174_frob_foo", "bad k/j ratio");
return d * j;
}
if(k * j foo_rate / 3.14159)
{
f->lipside->folksy[i] *= get_mod_x_from_db(z->ymurgy, j);
if(f->lipside->folksy[i] / d < b->arrack->room[i] / 9)
{
i++;
continue;
}
f->rank += 2;
if(f->rank < b->ill[i]->b->ill)
{
z->o->o->keeper += d;
return foo_rate;
}
}
}
if(d * f->rank < b->alder->dash && f->orte < b->attledress)
{
return foo_rate / d;
}
}
for(l = 0, m = z->yx;
l < m;
l += b->ill[i]->joe, m += b->ill[i]->bob)
{
d += z->ygote[i] * f->lipside->folksy[l];
if(get_mod_x_from_db(z->ymurgy, l) m)
{
f->lipside->rocky[l] = get_mod_x_from_db(z->ymurgy, m);
return foo_rate * f->eed->the->fish;
}
else if(f->antasy->foot->ball[i] < q->ual->ityof->life)
{
f->lipside->rap[i] = get_mod_y_from_db(z->yzygy, m - l);
break;
}
else if(f->orei->gnle->gion[i] < b->ill[i]->jean)
{
f->lipside->country[i] = get_mod_x_from_db(z->orch, l + --m);
}
foo_rate += f->ast->andl->oose[l];
do
{
n = 17 * q->uinine[i][m] *
get_magic_number((b->ilbo + f->rodo) / 2);
if(n < rune_caster(i, m))
{
return f->eed->the->tor->toise * 6;
}
} while(n m);
}
}

for(i = ....

etc etc. Imagine another ten or twelve similar loops, each packed to the
gunwhales with other loops, each seemingly random in structure and yet
crammed with business logic (and if ever there were an oxymoron
contest, "business logic" would definitely be my entry), with returns -
some of them seemingly unrelated in nature to the others - scattered
liberally through the loops, and a final

return foo_rate + q->uive->ring->wrec[k];
}

to wind things up neatly at the end.

That is the kind of code that put me off multiple returns (and,
incidentally, also turned me off break and continue as loop-diddling
constructs).

Curiously, of all the industries I've worked in, insurance seems to be
the biggest culprit for this kind of code. I don't know what it is
about insurance, but second place definitely goes to banks, which might
be a clue of some kind.

Perhaps those who share my view about multiple returns are those who
have spent at least some of their career in the banking/insurance
world, and those who don't aren't? Just a thought.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Mar 30 '07 #23
Richard Heathfield wrote:
>
Perhaps those who share my view about multiple returns are those who
have spent at least some of their career in the banking/insurance
world, and those who don't aren't? Just a thought.
In my case, the allergy was cased by trying to fix up smelly embedded
code with multiple returns that failed to clean up after its self.

--
Ian Collins.
Mar 30 '07 #24
On Fri, 30 Mar 2007 07:43:05 +0000, Richard Heathfield
<rj*@see.sig.invalidwrote:
>Army1987 said:
>[...] why

if (error) return FAIL;
/* lotsa code */
return SUCCESS; /*or anything else*/

is so bad?

Well, it isn't really so terribly bad.
>(I know that there are people who don't like writing
"return" (quotation marks excluded) more than once within a function
body (even if I disagree, I can see the point of that, in the case of
"normal" working), but, in the case of error handling, I can't see how
a premature return is worse than having all the function's normal
operation within an else block.)


Well, you've put your finger on something rather important here - the
concept of "normal working". When you're just bailing out because of an
error that means you can't possibly go on, then (as long as you've
cleaned up after yourself, so to speak) an early return is no big deal,
and the only reason one might want to avoid it is mere style, in that
it simplifies control flow if everything aims towards one exit point
and therefore some people find it easier to follow the code.

But where it really, really matters is in code like this:

double ctx2174_frob_foo(foo *f, bar *b, baz *z, quux *q)
{
int i, j, k, l, m, n, o, p;
double foo_rate = 1.0;
double d = get_init_d_from_db(f->red, b->arney);

if(d < 0)
{
push_error(__FILE__, __LINE__, "ctx2174_frob_foo", "bad d");
return d;
}

for(i = b->a->barracus->lowentry;
i < q->u->ality->street;
i += z->e->bedee)
{
j = f->rank + b->ill[i]->bob;
d += b->ubble[i] * f->roth[i][j / 2] * q->uestion;
foo_rate *= d / b->arrack->room[j];
if(z->ygote[i] foo_rate)
{
for(k = 0; k / j f->eed->the->world; k += i)
{
if(k + 4 < 3 / j)
{
push_error(__FILE__, __LINE__,
"ctx2174_frob_foo", "bad k/j ratio");
return d * j;
Here we mask the programmer's error and merrily trudge along in the
hopes that returning d * j somehow makes things okay. Happy, happy,
happy!

What exactly does push_error() do? Log an error message to a file that
the user is supposed to monitor on a daily (or hourly) basis? And if
there are any entries in this error log, should the user get on the
phone to the programmer and report that there's something "wrong"
going on? And should the user, despite these errors, feel comfortable
in knowing that the program is still running?

More likely than not, the program will exhibit some sort of undefined
behavior. It would be better, IMHO, to change that to:

assert(k + 4 >= 3 / j);

The big fat error message you get when running your test cases and the
assert that fires off, or worse yet when your users get that same, big
fat error message when running your program and the assert fires off,
nips the problem in the bud and allows you to ascertain what is the
real problem. If you're really good, you'd define your own macro, and
use it instead of assert, e.g.:

MY_ASSERT(k + 4 >= 3 / j);

MY_ASSERT dumps all sorts of information that allows you to track down
*your* bug--hopefully at verification time! MY_ASSERT could even do
the duty of push_error *and* restart the program, with no less down
time than what the requirements specify.

Best regards
--
jay
Mar 30 '07 #25
jaysome said:
On Fri, 30 Mar 2007 07:43:05 +0000, Richard Heathfield
<rj*@see.sig.invalidwrote:
<snip>
>>When you're just bailing out because of
an error that means you can't possibly go on, then (as long as you've
cleaned up after yourself, so to speak) an early return is no big
deal, and the only reason one might want to avoid it is mere style, in
that it simplifies control flow if everything aims towards one exit
point and therefore some people find it easier to follow the code.

But where it really, really matters is in code like this:
<ghastly illustrative code fragment snipped>
>>
if(k + 4 < 3 / j)
{
push_error(__FILE__, __LINE__,
"ctx2174_frob_foo", "bad k/j ratio");
return d * j;

Here we mask the programmer's error and merrily trudge along in the
hopes that returning d * j somehow makes things okay. Happy, happy,
happy!
You'd be amazed.
What exactly does push_error() do?
It doesn't really matter, actually, for the purposes of the example. The
point is that the code, despite containing no goto statements, is still
a mess of spaghetti - the control flow is all over the floor.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Mar 30 '07 #26

"Richard Heathfield" <rj*@see.sig.invalidha scritto nel messaggio
news:_v*********************@bt.com...
double ctx2174_frob_foo(foo *f, bar *b, baz *z, quux *q)
{
int i, j, k, l, m, n, o, p;
double foo_rate = 1.0;
double d = get_init_d_from_db(f->red, b->arney);

if(d < 0)
{
push_error(__FILE__, __LINE__, "ctx2174_frob_foo", "bad d");
return d;
}

for(i = b->a->barracus->lowentry;
i < q->u->ality->street;
i += z->e->bedee)
{
j = f->rank + b->ill[i]->bob;
d += b->ubble[i] * f->roth[i][j / 2] * q->uestion;
foo_rate *= d / b->arrack->room[j];
if(z->ygote[i] foo_rate)
{
for(k = 0; k / j f->eed->the->world; k += i)
{
if(k + 4 < 3 / j)
{
push_error(__FILE__, __LINE__,
"ctx2174_frob_foo", "bad k/j ratio");
return d * j;
}
if(k * j foo_rate / 3.14159)
{
f->lipside->folksy[i] *= get_mod_x_from_db(z->ymurgy, j);
if(f->lipside->folksy[i] / d < b->arrack->room[i] / 9)
{
i++;
continue;
}
f->rank += 2;
if(f->rank < b->ill[i]->b->ill)
{
z->o->o->keeper += d;
return foo_rate;
}
}
}
if(d * f->rank < b->alder->dash && f->orte < b->attledress)
{
return foo_rate / d;
}
}
for(l = 0, m = z->yx;
l < m;
l += b->ill[i]->joe, m += b->ill[i]->bob)
{
d += z->ygote[i] * f->lipside->folksy[l];
if(get_mod_x_from_db(z->ymurgy, l) m)
{
f->lipside->rocky[l] = get_mod_x_from_db(z->ymurgy, m);
return foo_rate * f->eed->the->fish;
}
else if(f->antasy->foot->ball[i] < q->ual->ityof->life)
{
f->lipside->rap[i] = get_mod_y_from_db(z->yzygy, m - l);
break;
}
else if(f->orei->gnle->gion[i] < b->ill[i]->jean)
{
f->lipside->country[i] = get_mod_x_from_db(z->orch, l + --m);
}
foo_rate += f->ast->andl->oose[l];
do
{
n = 17 * q->uinine[i][m] *
get_magic_number((b->ilbo + f->rodo) / 2);
if(n < rune_caster(i, m))
{
return f->eed->the->tor->toise * 6;
}
} while(n m);
}
}

for(i = ....

etc etc. Imagine another ten or twelve similar loops, each packed to the
gunwhales with other loops, each seemingly random in structure and yet
crammed with business logic (and if ever there were an oxymoron
contest, "business logic" would definitely be my entry), with returns -
some of them seemingly unrelated in nature to the others - scattered
liberally through the loops, and a final

return foo_rate + q->uive->ring->wrec[k];
}
LOL. ROTFL. It made me laugh to tears for 5 minutes or so. Did you write it
to show the point (if so, you really have *way* too much time on your
hands)? Did you copy actual existing code and just replace identifiers? (Oh
my God. It sounds absurd but I fear that's not really impossible.)
Mar 30 '07 #27
Army1987 said:
>
"Richard Heathfield" <rj*@see.sig.invalidha scritto nel messaggio
news:_v*********************@bt.com...
<gerharsterly code example snipped>
>Imagine another ten or twelve similar loops, each packed to
the gunwhales with other loops, each seemingly random in structure
and yet crammed with business logic (and if ever there were an
oxymoron contest, "business logic" would definitely be my entry),
with returns - some of them seemingly unrelated in nature to the
others - scattered liberally through the loops, and a final

return foo_rate + q->uive->ring->wrec[k];
}

LOL. ROTFL. It made me laugh to tears for 5 minutes or so. Did you
write it to show the point (if so, you really have *way* too much time
on your hands)? Did you copy actual existing code and just replace
identifiers? (Oh my God. It sounds absurd but I fear that's not really
impossible.)
A shorter example, whilst it would have been much quicker to provide,
could not have conveyed the sheer control flow horror which faces
programmers in some code shops. Whilst the code was not in fact copied
and hacked, it could easily have been, were it not for NDAs and
confidentiality clauses and so on. If you were to substitute businessy
identifier names into that example, it would not have looked out of
place in quite a few of the systems I've worked on. And of course it's
impossible to persuade most project leads that it's worth taking the
time to rewrite it properly.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Mar 30 '07 #28
Richard Heathfield wrote:
>
.... snip ...
>
It doesn't really matter, actually, for the purposes of the example.
The point is that the code, despite containing no goto statements,
is still a mess of spaghetti - the control flow is all over the floor.
I can't believe you went to the trouble of creating that horror for
the article, so it must have existed somewhere. Condolences,
although that sort of thing is probably what keeps you in business.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>

--
Posted via a free Usenet account from http://www.teranews.com

Mar 30 '07 #29
Richard Heathfield wrote:
>
.... snip ...
>
Perhaps those who share my view about multiple returns are those who
have spent at least some of their career in the banking/insurance
world, and those who don't aren't? Just a thought.
Well, I have had sessions in banking, insurance, municipal taxes,
and payroll handling. But those are the exceptions, rather than
the rule. There may be something to your conjecture.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>

--
Posted via a free Usenet account from http://www.teranews.com

Mar 30 '07 #30
Thanks for your response Chuck. And my thanks to all other respondents too.
Chuck Falconer wrote:
Which incorporates several guidelines. One is single point of exit.
I have heard about this single point of exit guideline and I can't say I'm
totally convinced. My take on this is simple. If *all* you do in the code at
the point of each error is

ret_val = FAIL;

and no other processing occurs except

return ret_val;

which is possibly many lines away in the code, then I see no reason why that
can't be replaced with

return FAIL;

then any 'else' blocks can be removed and the code back-dented accordingly.

In my particular case the code I had to deal with I transformed to the form:

if (error_1) { report error_1 condition; return FAIL; }
if (error_2) { report error_2 condition; return FAIL; }
if (error_3) { report error_3 condition; return FAIL; }
if (error_4) { report error_4 condition; return FAIL; }

{ var declarations
processing with knowledge error conditions dealt with
}
return SUCCESS;

The reporting of each of the error conditions is handled differently in the
code I am modifying - a different function call for each one. The errors are
also detected in different ways. The question is, how would you re-write
this using the "single point of exit guideline". Not very elegantly, IMO.
Chuck Falconer wrote:
the controlling condition is easily visible from the indentation
In this case, which controlling condition?
Chuck Falconer wrote:
A third is that the short condition comes
first, easing finding the controlling condition via the
indentation.
Again, I don't think in this case it would apply.

---
Martin

Mar 30 '07 #31
CBFalconer wrote, On 30/03/07 14:49:
Richard Heathfield wrote:
... snip ...
>Perhaps those who share my view about multiple returns are those who
have spent at least some of their career in the banking/insurance
world, and those who don't aren't? Just a thought.

Well, I have had sessions in banking, insurance, municipal taxes,
and payroll handling. But those are the exceptions, rather than
the rule. There may be something to your conjecture.
You get the same mess other industries where money or government
regulations (or both) are involved.

I don't mind multiple returns when the function is small enough to
understand, and for some 5 line functions they make it a lot clearer
IMHO, but otherwise they can be a real pain, especially when mixed up in
other control structures.
--
Flash Gordon
Mar 30 '07 #32
Martin wrote:
Thanks for your response Chuck. And my thanks to all other respondents too.

Chuck Falconer wrote:
>>Which incorporates several guidelines. One is single point of exit.

I have heard about this single point of exit guideline and I can't say I'm
totally convinced. My take on this is simple. If *all* you do in the code at
the point of each error is

ret_val = FAIL;

and no other processing occurs except

return ret_val;

which is possibly many lines away in the code, then I see no reason why that
can't be replaced with

return FAIL;
But what happens if the function might have to clean up before it returns?

If you can't see the entire function on on screen, it's too long.
---
Martin
Your sig is broken, the delimiter should be "-- "

--
Ian Collins.
Mar 30 '07 #33
CBFalconer wrote:
Richard Heathfield wrote:
>>
... snip ...
>>
It doesn't really matter, actually, for the purposes of the example.
The point is that the code, despite containing no goto statements,
is still a mess of spaghetti - the control flow is all over the floor.

I can't believe you went to the trouble of creating that horror for
the article, so it must have existed somewhere. Condolences,
although that sort of thing is probably what keeps you in business.
Looking more closely it is actually much better structured than the
real life horrors I have seen, not to mention the identifiers. So
I guess you have too much time on your hands, and got caught up in
the structuring assistance of your editor.

--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>

--
Posted via a free Usenet account from http://www.teranews.com

Mar 30 '07 #34
CBFalconer said:
Looking more closely it is actually much better structured than the
real life horrors I have seen, not to mention the identifiers.
Sorry about that. I was going for realism, but I didn't want to spend
*too* much time on it.
So
I guess you have too much time on your hands,
As I explained earlier, a shorter example would not have been adequate.
In any case, I'm a fast typist and it doubled as a coffee break. :-)
and got caught up in
the structuring assistance of your editor.
Um, what structuring assistance? I use vim.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.
Mar 31 '07 #35

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

20
by: Hung Jung Lu | last post by:
Hi, I know people have talked about it before, but I am still really confused from reading the old messages. When I talk about code blocks, I am not talking about Lisp/Ruby/Perl, so I am not...
2
by: BStorm | last post by:
I ran into a maddening bug that I finally tracked down to Microsoft's Data Access Blocks. It is in the SQL Helper UpdateDataSet method as follows: Microsoft Code: #region UpdateDataset public...
2
by: Roshawn Dawson | last post by:
Hi, Does code contained in <% %> blocks cause postbacks to the server? Thanks, Roshawn
29
by: John Rivers | last post by:
Hello, What good reason there is for not allowing methods in ASPX pages I can't imagine, but here is how to get around that limitation: (START) <body MS_POSITIONING="FlowLayout"> <form...
14
by: J.S. | last post by:
In a Windows Form application, which is the better method to concatenate large blocks of code? 1. Reading the text from text files. 2. Adding the text to the VB file itself? Thanks! J.S. ...
3
by: craig | last post by:
I was just wondering if anyone else may have incorporated the original Microsoft Exception Management Application Block (EMAB) or Data Access Application Block (DAAB) into one of their applications...
2
by: SparkPlug | last post by:
Given the following as true... 1. the importance of good software design & architecture, and learning good habits early on - even right at the beginning of a development career 2. the lack of...
26
by: brenocon | last post by:
Hi all -- Compared to the Python I know and love, Ruby isn't quite the same. However, it has at least one terrific feature: "blocks". Whereas in Python a "block" is just several lines of...
3
by: Andy B | last post by:
I need to search an xml element for blocks of text. The start of the text block will have a 5 digit number in it and i then need to read until the next 5 digit number. After this, I need to put...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.