return in void functions

A

Andrew Koenig

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.
 
J

Jeff Schwab

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




see below




LOG("copy_of_a=" << 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.




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.




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
 
T

Thomas Matthews

Jeff said:
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
 
J

Jeff Schwab

Thomas said:
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
 
M

Martijn Lievaart

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
 
J

Jeff Schwab

Martijn said:
OK, agree to disagree, but...




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.
 
E

E. Robert Tisdale

Andrew said:
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?
 
H

Howard

E. Robert Tisdale said:
Andrew said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top