471,593 Members | 1,834 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

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

return in void functions

Hi,
I was just wondering, is it good to use return without arguments in a void
function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?

TIA, Max.
Jul 22 '05 #1
27 2496
Maximus wrote:
Hi,
I was just wondering, is it good to use return without arguments in a void
function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?

Some will quibble about that stylistically (preferring a single exit
point), but there's nothing wrong with that technique.

[Of course, you could just as easily have written:

void SetMapLayer()
{
if (Map) layer = LAYER_MAP;
}

]

HTH,
--ag

--
Artie Gold -- Austin, Texas

Jul 22 '05 #2

"Maximus" <ma*******@videotron.ca> wrote in message news:rp********************@weber.videotron.net...
Hi,
I was just wondering, is it good to use return without arguments in a void
function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?

In structured code, you'd want to avoid willy nilly returning from inside functions,
but in this case it probably doesn't make any difference. The scary ones are
things like:

void SomeFunc() {
if(...) {
while( ... ) {
if(...) {
try {
if(...) return
...
Jul 22 '05 #3
"Maximus" <ma*******@videotron.ca> wrote in message
news:rp********************@weber.videotron.net...
Hi,
I was just wondering, is it good to use return without arguments in a void
function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?

TIA, Max.


I don't like your construction for two reasons:

1) It breaks the structured programming "rule" that each function have only
one return point. I put the word "rule" in quotes because I break it myself
anytime I feel like it. But here there is no reason to.

2) It is effectively a double negative. We set layer to LAYER_MAP if !!Map.

Don't you think this is much clearer?

void SetMapLayer()
{
if (Map)
layer = LAYER_MAP;
}

One return point, no double negatives.

Getting back to your original question, I would use return in a void
function if I thought the net effect was to make the code simpler. Example:

void fribble(int thing)
{
if (thing == 0)
return; // explanation of why here
// 15 lines of code here
}

The structured programming version is

void fribble(int thing)
{
if (thing != 0)
{
// 15 lines of code here
}
}

which I think is somewhat less readable, although by no means a serious
mistake.

--
Cy
http://home.rochester.rr.com/cyhome/
Jul 22 '05 #4
"Maximus" <ma*******@videotron.ca> wrote in message
news:rp********************@weber.videotron.net...
Hi,
I was just wondering, is it good to use return without arguments in a void
function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?


This is a matter of 'taste' or 'style'. I prefer to have a
function have only one exit point (not always possible, but
I strive for it.)

I'd write the above as:

void SetMapLayer()
{
if(Map)
layer = LAYER_MAP;
}

which effectively does the same thing.

Also, even if I'd written it as you have, I'd put the 'return'
statement on a separate line:

if( !Map )
return;

layer = LAYER_MAP;

This makes it more quickly obvious (to me at least) that
the 'linear' execution flow is interrupted. I'd probably
also add a comment for more quick visual identification:

if( !Map )
return; /* EXIT POINT */
/* I also like a blank line here */
layer = LAYER_MAP;

Especially if more code is added after the assignment, this
makes the 'return' stand out.

Again, this is all a matter of 'style'. IMO more important that
the actual 'style' you use, is that you use it consistently.

-Mike
Jul 22 '05 #5
Mike Wahler wrote:
"Maximus" <ma*******@videotron.ca> wrote in message
news:rp********************@weber.videotron.net...
Hi,
I was just wondering, is it good to use return without arguments in a void
function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?

This is a matter of 'taste' or 'style'. I prefer to have a
function have only one exit point (not always possible, but
I strive for it.)

I'd write the above as:

void SetMapLayer()
{
if(Map)
layer = LAYER_MAP;
}

which effectively does the same thing.

Also, even if I'd written it as you have, I'd put the 'return'
statement on a separate line:

if( !Map )
return;

layer = LAYER_MAP;

This makes it more quickly obvious (to me at least) that
the 'linear' execution flow is interrupted. I'd probably
also add a comment for more quick visual identification:

if( !Map )
return; /* EXIT POINT */
/* I also like a blank line here */
layer = LAYER_MAP;

Especially if more code is added after the assignment, this
makes the 'return' stand out.

Again, this is all a matter of 'style'. IMO more important that
the actual 'style' you use, is that you use it consistently.

-Mike


It's not just a matter of 'style.' Every extra point of return makes
code much harder to debug, since state-validation code to be executed
before a function returns must be duplicated for each point of return.
Take this advice from someone who has learned the error of his ways:
Keep it down to one exit point whenever possible. The only exception to
this rule should be the throwing of exceptions.

Some languages, like Tcl, do have great support for techniques relying
on multiple points of return. C++ is not such a language.

-Jeff

Jul 22 '05 #6

"Cy Edmunds" <ce******@spamless.rochester.rr.com> wrote in message
news:p2*******************@twister.nyroc.rr.com...
"Maximus" <ma*******@videotron.ca> wrote in message
news:rp********************@weber.videotron.net...
Hi,
I was just wondering, is it good to use return without arguments in a void function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?

TIA, Max.
I don't like your construction for two reasons:

1) It breaks the structured programming "rule" that each function have

only one return point. I put the word "rule" in quotes because I break it myself anytime I feel like it. But here there is no reason to.

2) It is effectively a double negative. We set layer to LAYER_MAP if !!Map.
Don't you think this is much clearer?

void SetMapLayer()
{
if (Map)
layer = LAYER_MAP;
}

One return point, no double negatives.

Getting back to your original question, I would use return in a void
function if I thought the net effect was to make the code simpler. Example:
void fribble(int thing)
{
if (thing == 0)
return; // explanation of why here
// 15 lines of code here
}

The structured programming version is

void fribble(int thing)
{
if (thing != 0)
{
// 15 lines of code here
}
}

which I think is somewhat less readable, although by no means a serious
mistake.

--
Cy
http://home.rochester.rr.com/cyhome/


ok, help me out here folks, cause I don't quite know how to reply just to
this comment, but here's mine

function which take on the form:
if( !condition ) // did caller do something stupid
{
print error
return
}
else
{
lots of code
}

are to be avoided as I've learned. The reason: runtime inefficiency due to
cpu branch prediction behavior.
If I remember correctly, the cpu will pipeline the code which errors and
exits instead of the typical function
code resulting is a few holes in the pipeline when it has to skip the error
and exit code, but worse - all of the
typical function code will have go into the pipeline "late". I might
suggest either restructing the if statement or
ensuring that your compiler fixes it for you and generates optimized object
code.

Please do correct me if I am wrong, I hate storing incorrect knowledge in my
head.

Andrew
Jul 22 '05 #7
In article <e-********************@comcast.com>,
"Andrew" <an****@wallserver.net> wrote:
"Cy Edmunds" <ce******@spamless.rochester.rr.com> wrote in message
news:p2*******************@twister.nyroc.rr.com...
"Maximus" <ma*******@videotron.ca> wrote in message
news:rp********************@weber.videotron.net...
Hi,
I was just wondering, is it good to use return without arguments in a void function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?

TIA, Max.
I don't like your construction for two reasons:

1) It breaks the structured programming "rule" that each function have

only
one return point. I put the word "rule" in quotes because I break it

myself
anytime I feel like it. But here there is no reason to.

2) It is effectively a double negative. We set layer to LAYER_MAP if

!!Map.

Don't you think this is much clearer?

void SetMapLayer()
{
if (Map)
layer = LAYER_MAP;
}

One return point, no double negatives.

Getting back to your original question, I would use return in a void
function if I thought the net effect was to make the code simpler.

Example:

void fribble(int thing)
{
if (thing == 0)
return; // explanation of why here
// 15 lines of code here
}

The structured programming version is

void fribble(int thing)
{
if (thing != 0)
{
// 15 lines of code here
}
}

which I think is somewhat less readable, although by no means a serious
mistake.

--
Cy
http://home.rochester.rr.com/cyhome/


ok, help me out here folks, cause I don't quite know how to reply just to
this comment, but here's mine

function which take on the form:
if( !condition ) // did caller do something stupid
{
print error
return
}
else
{
lots of code
}

are to be avoided as I've learned. The reason: runtime inefficiency due to
cpu branch prediction behavior.


While that may be true on whatever platform you learned on, different
CPU's predict branches differently.But this is completely outside the
scope of the C++ language. Besides, since the compiler is free to
optimize as it sees fit, as long as it doesn't change the actual meaning
of the code, it could very well reverse the order of the "if {...}" and
"else {...}" sections of code upon seeing that the condition is "not"-ed.
If I remember correctly, the cpu will pipeline the code which errors and
exits instead of the typical function
code resulting is a few holes in the pipeline when it has to skip the error
and exit code, but worse - all of the
typical function code will have go into the pipeline "late". I might
suggest either restructing the if statement or
ensuring that your compiler fixes it for you and generates optimized object
code.

Please do correct me if I am wrong, I hate storing incorrect knowledge in my
head.


You may be right for a particular platform, but you are most
assuredly wrong for many of the platforms out there.

Jul 22 '05 #8
Hello,

Jeff Schwab <je******@comcast.net> writes:
It's not just a matter of 'style.' Every extra point of return makes
code much harder to debug, since state-validation code to be executed
before a function returns must be duplicated for each point of return.


This does not sound very severe. It is a matter of seconds to search for
every occurrence of the word "return" in a function and insert a common
line before that. I think conceptually code like

if(whatever)
return;

is very clear and clean. It just means "Do not apply the rest of the
function if condition _whatever_ is met".

Have a good 2004,
Chris Dams
Jul 22 '05 #9

"Jeff Schwab" <je******@comcast.net> skrev i en meddelelse
news:W9********************@comcast.com...
Mike Wahler wrote:
"Maximus" <ma*******@videotron.ca> wrote in message
news:rp********************@weber.videotron.net...
Hi,
I was just wondering, is it good to use return without arguments in a voidfunction as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?

This is a matter of 'taste' or 'style'. I prefer to have a
function have only one exit point (not always possible, but
I strive for it.)

I'd write the above as:

void SetMapLayer()
{
if(Map)
layer = LAYER_MAP;
}

which effectively does the same thing.

Also, even if I'd written it as you have, I'd put the 'return'
statement on a separate line:

if( !Map )
return;

layer = LAYER_MAP;

This makes it more quickly obvious (to me at least) that
the 'linear' execution flow is interrupted. I'd probably
also add a comment for more quick visual identification:

if( !Map )
return; /* EXIT POINT */
/* I also like a blank line here */
layer = LAYER_MAP;

Especially if more code is added after the assignment, this
makes the 'return' stand out.

Again, this is all a matter of 'style'. IMO more important that
the actual 'style' you use, is that you use it consistently.

-Mike


It's not just a matter of 'style.' Every extra point of return makes
code much harder to debug, since state-validation code to be executed
before a function returns must be duplicated for each point of return.
Take this advice from someone who has learned the error of his ways:
Keep it down to one exit point whenever possible. The only exception to
this rule should be the throwing of exceptions.

Some languages, like Tcl, do have great support for techniques relying
on multiple points of return. C++ is not such a language.

-Jeff


Actually, C++ has a very nice feature for this - the deterministic execution
of destructors. This one feature is also the reason that the "one exit rule"
in C++ does not have to be taken to serious. In other languages, I would be
far more careful to have only one exit point.

Kind regards
Peter
Jul 22 '05 #10
Peter Koch Larsen wrote:
"Jeff Schwab" <je******@comcast.net> skrev i en meddelelse
news:W9********************@comcast.com...
Mike Wahler wrote:
"Maximus" <ma*******@videotron.ca> wrote in message
news:rp********************@weber.videotron.net ...
Hi,
I was just wondering, is it good to use return without arguments in a
void
function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?
This is a matter of 'taste' or 'style'. I prefer to have a
function have only one exit point (not always possible, but
I strive for it.)

I'd write the above as:

void SetMapLayer()
{
if(Map)
layer = LAYER_MAP;
}

which effectively does the same thing.

Also, even if I'd written it as you have, I'd put the 'return'
statement on a separate line:

if( !Map )
return;

layer = LAYER_MAP;

This makes it more quickly obvious (to me at least) that
the 'linear' execution flow is interrupted. I'd probably
also add a comment for more quick visual identification:

if( !Map )
return; /* EXIT POINT */
/* I also like a blank line here */
layer = LAYER_MAP;

Especially if more code is added after the assignment, this
makes the 'return' stand out.

Again, this is all a matter of 'style'. IMO more important that
the actual 'style' you use, is that you use it consistently.

-Mike


It's not just a matter of 'style.' Every extra point of return makes
code much harder to debug, since state-validation code to be executed
before a function returns must be duplicated for each point of return.
Take this advice from someone who has learned the error of his ways:
Keep it down to one exit point whenever possible. The only exception to
this rule should be the throwing of exceptions.

Some languages, like Tcl, do have great support for techniques relying
on multiple points of return. C++ is not such a language.

-Jeff

Actually, C++ has a very nice feature for this - the deterministic execution
of destructors. This one feature is also the reason that the "one exit rule"
in C++ does not have to be taken to serious. In other languages, I would be
far more careful to have only one exit point.

Kind regards
Peter


Destructors don't have access to all of the autmatic state inside the
function. I suppose you could define all variables at the top of the
function and pass references into the constructor of a local
"print-on-destruction" object, but that would be a major pain in the neck.

This is *not* a minor problem. I have wasted many hours because of it.
Jul 22 '05 #11
On Fri, 02 Jan 2004 11:34:18 -0500, Jeff Schwab wrote:
Actually, C++ has a very nice feature for this - the deterministic execution
of destructors. This one feature is also the reason that the "one exit rule"
in C++ does not have to be taken to serious. In other languages, I would be
far more careful to have only one exit point.


Destructors don't have access to all of the autmatic state inside the
function. I suppose you could define all variables at the top of the
function and pass references into the constructor of a local
"print-on-destruction" object, but that would be a major pain in the neck.

This is *not* a minor problem. I have wasted many hours because of it.


I'm not following you here, care to explain? I sense design problems, but
I'm not sure.

M4

Jul 22 '05 #12
Martijn Lievaart wrote:
On Fri, 02 Jan 2004 11:34:18 -0500, Jeff Schwab wrote:

Actually, C++ has a very nice feature for this - the deterministic execution
of destructors. This one feature is also the reason that the "one exit rule"
in C++ does not have to be taken to serious. In other languages, I would be
far more careful to have only one exit point.


Destructors don't have access to all of the autmatic state inside the
function. I suppose you could define all variables at the top of the
function and pass references into the constructor of a local
"print-on-destruction" object, but that would be a major pain in the neck.

This is *not* a minor problem. I have wasted many hours because of it.

I'm not following you here, care to explain? I sense design problems, but
I'm not sure.

M4


Why yes, Omniscient One! The design problem was the use of blocks of
code having multiple exit points. Most of my typical debug cycle is
spent putting "cerr" lines (or gdb break points) at strategic places in
the code. Tripling the number of exit points per function almost
triples the time it takes to debug a problem.

When I throw an exception in a non-trivial program, I include the file
name and line number as part of the exception, so I know exactly where I
need to start looking when an exception is thrown. Trying to track an
issue back to root cause is far more difficult in spaghetti code than in
code that avoids mid-block return statements. This should not be news
to you.

-Jeff

Jul 22 '05 #13
I guess that after all the crap you got from everybody, by now you know
that:

1. It is perfectly valid to use 'return;' in a void function.
2. You piss a lot of people by doing it.

:)

Maximus wrote:
Hi,
I was just wondering, is it good to use return without arguments in a void
function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?

TIA, Max.

Jul 22 '05 #14

"Jorge Rivera" <jo*****@rochester.rr.com> wrote in message
news:Pl****************@twister.nyroc.rr.com...
I guess that after all the crap you got from everybody, by now you know that:

1. It is perfectly valid to use 'return;' in a void function.
2. You piss a lot of people by doing it.

:)

Maximus wrote:
Hi,
I was just wondering, is it good to use return without arguments in a void function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?

TIA, Max.


An easier way to piss a lot of people off is by top posting
Jul 22 '05 #15
Well as I can see it is not in all cases, I learned that within a small
block of codes return can be used but not in a larger one, since it gives
more than one exit point, thus harder to debug. Thanks for that verry
pleasent post though.

Max.

"Jorge Rivera" <jo*****@rochester.rr.com> wrote in message
news:Pl****************@twister.nyroc.rr.com...
I guess that after all the crap you got from everybody, by now you know
that:

1. It is perfectly valid to use 'return;' in a void function.
2. You piss a lot of people by doing it.

:)

Maximus wrote:
Hi,
I was just wondering, is it good to use return without arguments in a void function as following:

void SetMapLayer()
{
if( !Map ) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technic?

TIA, Max.

Jul 22 '05 #16
Maximus wrote:
Is it good to use return without arguments
in a void function as following:

void SetMapLayer(void) {
if(!Map) return;
layer = LAYER_MAP;
}

It works well but it is considered a good programming technique?


Map& Map::SetLayer(void) {
if (this->Map)
this->layer = LAYER_MAP;
return *this;
}

Avoid void functions.
A function should *always* return a value
so that you can use it in an expression.
In this case, simply return a [non const] reference to the Map object.

Never write functions which modify global variables.
I'll assume that you are implementing a member function here
and that you reference data member Map to decide whether or not
to set data member layer to the value of constant LAYER_MAP.

Instead of using a "setter" function, you might try

class Map {
public:
class Layer_t {
// . . .
Layer_t& operator=(const Layer_t&) {
// . . .
}
};
private:
// representation
static const
Layer_t LAYER_MAP;
Layer_t layer;
bool Map;
public:
// functions
const
Layer_t& Layer(void) const {
return layer;
}
Layer_t& Layer(void) {
return layer;
}
// . . .
};

Then, you can write:

Map m, map;
// . . .
m.Layer() = map.Layer();

for example.

Jul 22 '05 #17
On Fri, 02 Jan 2004 15:30:43 -0500, Jeff Schwab wrote:
Why yes, Omniscient One! The design problem was the use of blocks of
code having multiple exit points. Most of my typical debug cycle is
spent putting "cerr" lines (or gdb break points) at strategic places in
the code. Tripling the number of exit points per function almost
triples the time it takes to debug a problem.

When I throw an exception in a non-trivial program, I include the file
name and line number as part of the exception, so I know exactly where I
need to start looking when an exception is thrown. Trying to track an
issue back to root cause is far more difficult in spaghetti code than in
code that avoids mid-block return statements. This should not be news
to you.


You'll have a hard time tracing exceptions with this design. Why not
define a trace object, that prints in constructor and destructor? Much
easier to use and works with multiple returns and exceptions. It's what I
do if I need this.

I never was a fan of not allowing multiple exit points, but exceptions
embedded it in the language, so I guess we better get used to it. I know I
don't have any problem with it.

Obviously, multiple exit points, like any language feature can be used
and mis-used. I concur that they can introduce problems, but only if
mis-used. My personal guideline in this is that the code should read like
a story. If not, you have a maintenance problem at least. So this is not
really related to multiple exit points, but to obfuscated coding.

M4

Jul 22 '05 #18
Martijn Lievaart wrote:
On Fri, 02 Jan 2004 15:30:43 -0500, Jeff Schwab wrote:

Why yes, Omniscient One! The design problem was the use of blocks of
code having multiple exit points. Most of my typical debug cycle is
spent putting "cerr" lines (or gdb break points) at strategic places in
the code. Tripling the number of exit points per function almost
triples the time it takes to debug a problem.

When I throw an exception in a non-trivial program, I include the file
name and line number as part of the exception, so I know exactly where I
need to start looking when an exception is thrown. Trying to track an
issue back to root cause is far more difficult in spaghetti code than in
code that avoids mid-block return statements. This should not be news
to you.

You'll have a hard time tracing exceptions with this design. Why not
define a trace object, that prints in constructor and destructor? Much
easier to use and works with multiple returns and exceptions. It's what I
do if I need this.


So, each time you want to debug a function with multiple return points,
you must:

1) Define a class with member references to all of the function's local
variables; at least, the ones you want to trace.

2) Find a cozy place somewhere before any of the function's return
points, but after all the traced variables have been defined. IME, this
is often impossible. Here is a simplistic case that reflects the common
practice of defining some variable, returning if a check succeeds, and
otherwise defining some other variables:

template< typename T >
void sort_two_objects( T a, T b )
{
bool already_sorted = a < b;

// A Trace object defined here cannot trace the value of copy_of_a,
// even if the return statement is not used.

if( already_sorted )
{
return;
}

// A Trace object defined here will not be destructed if the return
// statement is used.

T copy_of_a = a;

a = b;
b = copy_of_a;
}
I never was a fan of not allowing multiple exit points, but exceptions
embedded it in the language, so I guess we better get used to it. I know I
don't have any problem with it.
Exceptions do provide "extra" exit points, but they are not the same as
return statements. Exceptions carry information back up the call stack
about what went wrong, and why. Often, the information obtained in a
high-level catch block is sufficient, and there is no need to look at
the lower-level code at all. Even when there is such a need, since my
exceptions always include the line number and file number of the
corresponding throw statement, I know exactly which exit point to watch.
Obviously, multiple exit points, like any language feature can be used
and mis-used. I concur that they can introduce problems, but only if
mis-used. My personal guideline in this is that the code should read like
a story. If not, you have a maintenance problem at least. So this is not
really related to multiple exit points, but to obfuscated coding.
I've heard that theory before. The fact is that programs are not
stories. When I'm debugging a large piece of code, I have no desire to
start at the beginning and proceed to the point of the problem. The
time required for such an approcach is linear with the number of lines
leading to the point of the problem. By doing instead a binary search
for the problem, debug time is significantly reduced. This approach is
eased greatly by the absence of spurious return statements. Any extra
return statement is what I would call "obfuscated code."

-Jeff

M4


Jul 22 '05 #19
On Sat, 03 Jan 2004 10:23:14 -0500, Jeff Schwab wrote:
You'll have a hard time tracing exceptions with this design. Why not
define a trace object, that prints in constructor and destructor? Much
easier to use and works with multiple returns and exceptions. It's what I
do if I need this.
So, each time you want to debug a function with multiple return points,
you must:

1) Define a class with member references to all of the function's local
variables; at least, the ones you want to trace.


No, use one trace class to trace the program flow. When I want to log
variables, I use a different (though related) class.
2) Find a cozy place somewhere before any of the function's return
points, but after all the traced variables have been defined. IME, this
is often impossible. Here is a simplistic case that reflects the common
practice of defining some variable, returning if a check succeeds, and
otherwise defining some other variables:

template< typename T >
void sort_two_objects( T a, T b )
{ TRACE(sort_two_objects);
bool already_sorted = a < b;

// A Trace object defined here cannot trace the value of
copy_of_a, // even if the return statement is not used.
see below

if( already_sorted )
{
return;
}

// A Trace object defined here will not be destructed if the
return // statement is used.

T copy_of_a = a;
LOG("copy_of_a=" << a);

a = b;
b = copy_of_a;
}


Why do it any other way? Yes multiple exit points can make ad-hoc
cerr-debugging harder, but one should not do that anyhow. Writing the
TRACE and LOG macros is pretty trivial and make life much easier.
I never was a fan of not allowing multiple exit points, but exceptions
embedded it in the language, so I guess we better get used to it. I
know I don't have any problem with it.


Exceptions do provide "extra" exit points, but they are not the same as
return statements. Exceptions carry information back up the call stack
about what went wrong, and why. Often, the information obtained in a
high-level catch block is sufficient, and there is no need to look at
the lower-level code at all. Even when there is such a need, since my
exceptions always include the line number and file number of the
corresponding throw statement, I know exactly which exit point to watch.


That is true, but I don't see what the problem with multiple exit points
is if you're going to rerun the program under the debugger anyhow.

The only real advantage I can see of avoiding multiple exitpoints is that
one can put a breakpoint on the common exit. In practice, I don't miss it.
Obviously, multiple exit points, like any language feature can be used
and mis-used. I concur that they can introduce problems, but only if
mis-used. My personal guideline in this is that the code should read
like a story. If not, you have a maintenance problem at least. So this
is not really related to multiple exit points, but to obfuscated
coding.


I've heard that theory before. The fact is that programs are not
stories. When I'm debugging a large piece of code, I have no desire to
start at the beginning and proceed to the point of the problem. The
time required for such an approcach is linear with the number of lines
leading to the point of the problem. By doing instead a binary search
for the problem, debug time is significantly reduced. This approach is
eased greatly by the absence of spurious return statements. Any extra
return statement is what I would call "obfuscated code."


I'm not talking about complete programs being stories, I'm talking about
pieces of code, most probably a function. If you do that, the complete
program is easy to read and debugging gets much easier. So your argument
about binary search is not relevant, that is how one should always debug.

If the program is hard to read, it is hard to debug. If multiple exit
points make a function harder to read, they are misused. I still don't see
why multiple exit points would be a problem here.

M4

Jul 22 '05 #20

"E. Robert Tisdale" <E.**************@jpl.nasa.gov> wrote in message
news:3F**************@jpl.nasa.gov...
Avoid void functions.
A function should *always* return a value
so that you can use it in an expression.


That's one view. Another is that a procedure with with side effects should
*never* return a value, so that you are not tempted to use it in an
expression and get into trouble with order-of-evaluation ambiguities.
Jul 22 '05 #21
Martijn Lievaart wrote:
On Sat, 03 Jan 2004 10:23:14 -0500, Jeff Schwab wrote:

You'll have a hard time tracing exceptions with this design. Why not
define a trace object, that prints in constructor and destructor? Much
easier to use and works with multiple returns and exceptions. It's what I
do if I need this.


So, each time you want to debug a function with multiple return points,
you must:

1) Define a class with member references to all of the function's local
variables; at least, the ones you want to trace.

No, use one trace class to trace the program flow. When I want to log
variables, I use a different (though related) class.

2) Find a cozy place somewhere before any of the function's return
points, but after all the traced variables have been defined. IME, this
is often impossible. Here is a simplistic case that reflects the common
practice of defining some variable, returning if a check succeeds, and
otherwise defining some other variables:

template< typename T >
void sort_two_objects( T a, T b )
{


TRACE(sort_two_objects);

bool already_sorted = a < b;

// A Trace object defined here cannot trace the value of
copy_of_a, // even if the return statement is not used.

see below

if( already_sorted )
{
return;
}

// A Trace object defined here will not be destructed if the
return // statement is used.

T copy_of_a = a;

LOG("copy_of_a=" << a);

a = b;
b = copy_of_a;
}

Why do it any other way? Yes multiple exit points can make ad-hoc
cerr-debugging harder, but one should not do that anyhow. Writing the
TRACE and LOG macros is pretty trivial and make life much easier.

I never was a fan of not allowing multiple exit points, but exceptions
embedded it in the language, so I guess we better get used to it. I
know I don't have any problem with it.


Exceptions do provide "extra" exit points, but they are not the same as
return statements. Exceptions carry information back up the call stack
about what went wrong, and why. Often, the information obtained in a
high-level catch block is sufficient, and there is no need to look at
the lower-level code at all. Even when there is such a need, since my
exceptions always include the line number and file number of the
corresponding throw statement, I know exactly which exit point to watch.

That is true, but I don't see what the problem with multiple exit points
is if you're going to rerun the program under the debugger anyhow.

The only real advantage I can see of avoiding multiple exitpoints is that
one can put a breakpoint on the common exit. In practice, I don't miss it.

Obviously, multiple exit points, like any language feature can be used
and mis-used. I concur that they can introduce problems, but only if
mis-used. My personal guideline in this is that the code should read
like a story. If not, you have a maintenance problem at least. So this
is not really related to multiple exit points, but to obfuscated
coding.


I've heard that theory before. The fact is that programs are not
stories. When I'm debugging a large piece of code, I have no desire to
start at the beginning and proceed to the point of the problem. The
time required for such an approcach is linear with the number of lines
leading to the point of the problem. By doing instead a binary search
for the problem, debug time is significantly reduced. This approach is
eased greatly by the absence of spurious return statements. Any extra
return statement is what I would call "obfuscated code."

I'm not talking about complete programs being stories, I'm talking about
pieces of code, most probably a function. If you do that, the complete
program is easy to read and debugging gets much easier. So your argument
about binary search is not relevant, that is how one should always debug.

If the program is hard to read, it is hard to debug. If multiple exit
points make a function harder to read, they are misused. I still don't see
why multiple exit points would be a problem here.

M4

I think we're just not going to agree on this one. I don't care for
your TRACE and LOG macro approach, and I can see that you hate the idea
of giving up the ability to exit a function wherever you see fit. I
congratulate you on having found techniques that work for you, but they
certainly are not appropriate for me. I still recommend avoiding
multiple return points wherever possible, with the exception of... well,
exceptions. :)

-Jeff

Jul 22 '05 #22
Jeff Schwab wrote:
It's not just a matter of 'style.' Every extra point of return makes
code much harder to debug, since state-validation code to be executed
before a function returns must be duplicated for each point of return.
Take this advice from someone who has learned the error of his ways:
Keep it down to one exit point whenever possible. The only exception to
this rule should be the throwing of exceptions.

Some languages, like Tcl, do have great support for techniques relying
on multiple points of return. C++ is not such a language.

-Jeff


Not necessarily. There is a style of coding in which the execution
returns out of a function "as soon as possible":
void My_Function(void)
{
if (/* first boundary condition fails */)
return;
if (/* next boundary condition fails */)
return;
// ...
return;
}

Although these conditions could be logically ANDed together,
when there are many, it becomes a stylistic or readability nightmare.
The above style reduces the indentation problem of:
void My_Second_Function(void)
{
if (/* first condition is valid */)
{
if (/* second condition is valid */)
{
if (/* third condition is valid */)
{
// etc.
}
}
}
return;
}

And there is the style at my work which uses a boolean
variable for success:
void My_Third_Function(void)
{
bool failure(false);

failure = /* first boundary conditional test */;
if (!failure)
{
// execute some code.
failure = /* another test */;
}
if (!failure)
{
// and so on.
}
return;
}

I personally prefer the exit as soon as possible rule,
but this is a religous or style issue.

--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.raos.demon.uk/acllc-c++/faq.html
Other sites:
http://www.josuttis.com -- C++ STL Library book
http://www.sgi.com/tech/stl -- Standard Template Library

Jul 22 '05 #23
Thomas Matthews wrote:
Jeff Schwab wrote:
It's not just a matter of 'style.' Every extra point of return makes
code much harder to debug, since state-validation code to be executed
before a function returns must be duplicated for each point of return.
Take this advice from someone who has learned the error of his ways:
Keep it down to one exit point whenever possible. The only exception
to this rule should be the throwing of exceptions.

Some languages, like Tcl, do have great support for techniques relying
on multiple points of return. C++ is not such a language.

-Jeff

Not necessarily. There is a style of coding in which the execution
returns out of a function "as soon as possible":
void My_Function(void)
{
if (/* first boundary condition fails */)
return;
if (/* next boundary condition fails */)
return;
// ...
return;
}


That looks nice from a distance, but I've regretted writing that sort of
code in the past.
Although these conditions could be logically ANDed together,
when there are many, it becomes a stylistic or readability nightmare.
Only if done poorly. Code can get a good deal shorter by replacing "if
/*...*/ return" with "and":

void my_function( )
{
if( /* fist condition */
and /* second condition */
and /* third condition */ )
{
// ...
}
}
The above style reduces the indentation problem of:
void My_Second_Function(void)
{
if (/* first condition is valid */)
{
if (/* second condition is valid */)
{
if (/* third condition is valid */)
{
// etc.
}
}
}
return;
}
If a separate block is needed for each "if" statement, then a function
like this is performing a many different actions, according to the
results of many different tests. Such code often can be improved by
refactoring into several different functions or objects.
And there is the style at my work which uses a boolean
variable for success:
void My_Third_Function(void)
{
bool failure(false);

failure = /* first boundary conditional test */;
if (!failure)
{
// execute some code.
failure = /* another test */;
}
if (!failure)
{
// and so on.
}
return;
}
That's heinous.
I personally prefer the exit as soon as possible rule,
but this is a religous or style issue.


Yes, apparently so. :)

-Jeff

Jul 22 '05 #24
On Sat, 03 Jan 2004 11:42:15 -0500, Jeff Schwab wrote:
I think we're just not going to agree on this one. I don't care for
OK, agree to disagree, but...
your TRACE and LOG macro approach, and I can see that you hate the idea
of giving up the ability to exit a function wherever you see fit. I
congratulate you on having found techniques that work for you, but they
certainly are not appropriate for me. I still recommend avoiding


Why are the they not apropriate for you? The TRACE macro is much easier
than manually adding print statements and less errorprone.

M4

Jul 22 '05 #25
Martijn Lievaart wrote:
On Sat, 03 Jan 2004 11:42:15 -0500, Jeff Schwab wrote:

I think we're just not going to agree on this one. I don't care for

OK, agree to disagree, but...

your TRACE and LOG macro approach, and I can see that you hate the idea
of giving up the ability to exit a function wherever you see fit. I
congratulate you on having found techniques that work for you, but they
certainly are not appropriate for me. I still recommend avoiding

Why are the they not apropriate for you? The TRACE macro is much easier
than manually adding print statements and less errorprone.

M4

In your opinion. Certainly not in mine.

Jul 22 '05 #26
Andrew Koenig wrote:
E. Robert Tisdale wrote:
Avoid void functions.
A function should *always* return a value
so that you can use it in an expression.


That's one view. Another is that
a procedure with with side effects should *never* return a value
so that you are not tempted to use it in an expression
and get into trouble with order-of-evaluation ambiguities.


Avoid [writing] functions with side effects.
Avoid [writing] functions that modify global variables.
Pass by value, const reference or const pointer.

Don't implement this:

void foo(myClass& myObject);

Implement this:

myClass foo(void);

instead so that you can write:

const myClass x = foo();

instead of:

myClass x;
foo(x);

Programs which modify state variables (state machines)
are notoriously difficult to analyze and maintain.
The state determines the flow of execution
which, in turn, determines state
which, in turn, etc.
What you end up with is "spaghetti code".
(If you trace the thread of execution through the code
with a pencil, the trace resembles a plate of spaghetti.)
Structured programming helps but
the C++ computer programming language includes features
(i.e. functions can return objects of any type by value)
that allow programmers to write programs
that are virtually *stateless*.
The C++ programmer simply writes functions
which return the value of expressions
containing lower level functions.
Once the lower level functions have been tested and proven correct,
it is usually a simple matter to prove that the function
which uses them is correct. This is sometimes called
functional programming or, more generally, applicative programming.

I believe that the reason these features were included
in the C++ computer programming language
was to encourage good programming practice and I try to use them.

Your approach appears to me to be more "ad hoc".
Your rationale doesn't appear to be rooted in good computer science
but in anecdotes from personal experience using the language.
Do you have a coherent strategy that you use to write C++ programs
that other C++ programmers can read, understand and maintain?
If so, how is it different from the "strategy" employed
by the spaghetti coders of yesterday and today?

Jul 22 '05 #27

"E. Robert Tisdale" <E.**************@jpl.nasa.gov> wrote in message
news:3F**************@jpl.nasa.gov...
Andrew Koenig wrote:
E. Robert Tisdale wrote:
Avoid void functions.
A function should *always* return a value
so that you can use it in an expression.
That's one view. Another is that
a procedure with with side effects should *never* return a value
so that you are not tempted to use it in an expression
and get into trouble with order-of-evaluation ambiguities.


Avoid [writing] functions with side effects.
Avoid [writing] functions that modify global variables.


Better yet, avoid global variables. If there *are* global variables, and
they need to be modified, It would be hard to avoid doing so in a function
though, eh...?
Pass by value, const reference or const pointer.

Don't implement this:

void foo(myClass& myObject);

Implement this:

myClass foo(void);

instead so that you can write:

const myClass x = foo();

instead of:

myClass x;
foo(x);
Fine, if all you want to do with x if foo. But most times you have to keep
objects around for a while. So how would your pass-by-reference and
never-have-void-functions rules handle the problem of using an object more
than just that once? I've got all kinds of objects that have to stay around
for a while: my dsp object, my application object, my effect object, my
editor object, my settings object, etc. Any one of these might have to
interact with another of these due to a change from just about any source:
the host program, the user, the hardware, a timer, etc. Personally, in the
above example, I'd write:

myClass x;
x.foo();

(Assuming that the primary purpose of foo was to manipulate x, of course.)

Programs which modify state variables (state machines)
are notoriously difficult to analyze and maintain.
The state determines the flow of execution
which, in turn, determines state
which, in turn, etc.
What you end up with is "spaghetti code".
(If you trace the thread of execution through the code
with a pencil, the trace resembles a plate of spaghetti.)
All programs are state machines, really. You can hide the state information
behind your wall of nested function calls and constructors, but your program
is still a state machine. And being a state machine does not make it
"spaghetti code". What makes it spaghetti code is lack of logical design.
Structured programming helps but
the C++ computer programming language includes features
(i.e. functions can return objects of any type by value)
that allow programmers to write programs
that are virtually *stateless*.
The C++ programmer simply writes functions
which return the value of expressions
containing lower level functions.
Once the lower level functions have been tested and proven correct,
it is usually a simple matter to prove that the function
which uses them is correct. This is sometimes called
functional programming or, more generally, applicative programming.
Nice idea, (also called "black box" programming, correct?) but not always
possible in practice. There are often complex interactions between objects
whose only relation to each other is through the object that is using them,
and it is that interaction that introduces the complexity. The lower-level
objects cannot be held responsible for the state of other low-level objects.
Only the higher-level object can do it, and to do so often requires changes
to the low-level object designs that was not thought of when writing them
and "proving" their correctness.

I believe that the reason these features were included
in the C++ computer programming language
was to encourage good programming practice and I try to use them.

Me, too. It's just not always possible. Occasionally, despite myself, I
find myself writing multiple return statements in one function, simply
because it is the most straight-forward way to accomplish the task.
(Especially in the real-time environments I work in now, where speed is
essential.)
Your approach appears to me to be more "ad hoc".
Your rationale doesn't appear to be rooted in good computer science
but in anecdotes from personal experience using the language.
Do you have a coherent strategy that you use to write C++ programs
that other C++ programmers can read, understand and maintain?
If so, how is it different from the "strategy" employed
by the spaghetti coders of yesterday and today?


Hey, who wants "other C++ programmers" to maintain our code? We need the
job security of total code obfuscation! :-)

-Howard


Jul 22 '05 #28

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

23 posts views Thread by REH | last post: by
14 posts views Thread by zeroDontSpamtype | last post: by
10 posts views Thread by geoffblanduk_nospam | last post: by
18 posts views Thread by skishorev | last post: by
4 posts views Thread by msolem | last post: by
45 posts views Thread by noridotjabi | last post: by
18 posts views Thread by Pedro Pinto | last post: by
reply views Thread by XIAOLAOHU | last post: by
reply views Thread by Anwar ali | last post: by

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.