multiple return paths question

C

cimple

I've always made sure never to have multiple return paths in a function
because i thought it produced bigger code (and for readability). I
remember Lippman from Inside the Object Model saying something about
this. For every return point in a function, destructor calls for every
object created on the stack needs to be called. But when i tested this,
i saw no increase in executable size in release builds. Is my compiler
(VS7.0, Win2k) just optimizing or was my original assumption incorrect?

For example:

void Foo()
{
Object x;
Object y;
Object z;

if (x == y)
return;

if (x == z)
return;

if (y == z)
return;
}

Doesn't the above Foo bloat into this:

void Foo()
{
Object x;
Object y;
Object z;

if (x == y)
{
x.~x();
y.~y();
z.~z();
return;
}

if (x == z)
{
x.~x();
y.~y();
z.~z();
return;
}

if (y == z)
{
x.~x();
y.~y();
z.~z();
return;
}

x.~x();
y.~y();
z.~z();
return;
}

Hence, never produce functions with multiple return paths?

Thank you in advance.
 
M

mlimber

cimple said:
I've always made sure never to have multiple return paths in a function
because i thought it produced bigger code (and for readability). I
remember Lippman from Inside the Object Model saying something about
this. For every return point in a function, destructor calls for every
object created on the stack needs to be called. But when i tested this,
i saw no increase in executable size in release builds. Is my compiler
(VS7.0, Win2k) just optimizing or was my original assumption incorrect?

For example:

void Foo()
{
Object x;
Object y;
Object z;

if (x == y)
return;

if (x == z)
return;

if (y == z)
return;
}

Doesn't the above Foo bloat into this:

void Foo()
{
Object x;
Object y;
Object z;

if (x == y)
{
x.~x();
y.~y();
z.~z();
return;
}

if (x == z)
{
x.~x();
y.~y();
z.~z();
return;
}

if (y == z)
{
x.~x();
y.~y();
z.~z();
return;
}

x.~x();
y.~y();
z.~z();
return;
}

Hence, never produce functions with multiple return paths?

Thank you in advance.

First, I don't see why a compiler couldn't do it differently. Second,
according to _C++ Coding Standards_ by Sutter and Alexandrescu, the
single entry/single exit philosophy doesn't apply well to C++:
"Historically, some coding standards have required that each function
have exactly one exit, meaning one return statement. Such a requirement
is obsolete in languages that support exceptions and destructors, where
functions typically have numerous implicit exits. Instead, follow
standards like Item 5 [in the same book] that directly promote simpler
and shorter functions that are inherently easier to understand and to
make error-safe."

Cheers! --M
 
A

Alf P. Steinbach

* mlimber:
according to _C++ Coding Standards_ by Sutter and Alexandrescu, the
single entry/single exit philosophy doesn't apply well to C++:
"Historically, some coding standards have required that each function
have exactly one exit, meaning one return statement. Such a requirement
is obsolete in languages that support exceptions and destructors, where
functions typically have numerous implicit exits. Instead, follow
standards like Item 5 [in the same book] that directly promote simpler
and shorter functions that are inherently easier to understand and to
make error-safe."

In the SESE/SEME debates in clc++m Andrei has mostly been a majority of
one... ;-)

I don't think he's wrong, but I don't think he's right.

There are different approaches, and they're just different, not mutually
exclusive (except within the same code) such that one is better in any
absolute sense, in the sense of being the preferred style. If I had to
draw a distinction it would be that SEME works fine for expert
programmers and is much favored by novices, while SESE works fine for
novices and is much favored by the mediocre. But of what practical
utility that observation is, and whether it is correct, well.
 
C

cimple

Alright i just looked it up in my Lippman book. In section 6.1 Object
Construction and Destruction.

"..It gets slightly more confusing when there are multiple exits from a
block or function. The destructor must be placed at each exit point at
which the object is "alive"...[given an example similar to the one
above] In this example, the destructor for "point" [in our case x, y,
and z] must be generated prior to the return statement at the three
exit points..."

He goes on to say...

"In general, place an object as close as possible to the code segment
actually using it. Doing this can save you unncessary object creation
and destruction..This may seem self-evident, but many Pascal and C
programmers using C++ still place all their objects at the beginning of
a function or a local block."


I've looked at a lot of code from both beginners and "experts" and very
rarely do either stick to single exit point philosophy.
 
A

Alf P. Steinbach

* cimple:
Alright i just looked it up in my Lippman book. In section 6.1 Object
Construction and Destruction.

I think there must be something wrong with your quoting or the quotes
only make sense in some outer context not evident here, because...

"..It gets slightly more confusing when there are multiple exits from a
block or function. The destructor must be placed at each exit point at
which the object is "alive"...[given an example similar to the one
above] In this example, the destructor for "point" [in our case x, y,
and z] must be generated prior to the return statement at the three
exit points..."

.... a destructor is not generated, except in the sense of
inline-expanded, at different places in the code (could Lippman be
talking about destructor _calls_?), and ...

He goes on to say...

"In general, place an object as close as possible to the code segment
actually using it. Doing this can save you unncessary object creation
and destruction..This may seem self-evident, but many Pascal and C
programmers using C++ still place all their objects at the beginning of
a function or a local block."

.... declaring objects as close as possible to the code segment using
them is not incompatible with declaring them at the beginning of a local
block, except visual layout for one rather uncommon indentation style.

I've looked at a lot of code from both beginners and "experts" and very
rarely do either stick to single exit point philosophy.

If you just count returns and perhaps throws, breaks, whatever, that
would appear to be the case.

If you analyze the meaning of the code it's different.

Early returns for failed preconditions is common (SEME), with the meat
of the function in SESE style, except sometimes breaks from nested loops
and functions that are really just value or action selectors. It's a
matter of using the right tool for the right job. SESE/SEME debates, at
least above the novice nevel, are not about whether it can be a good
idea to force an early exit on some failed precondition, say (it _is_ a
good idea), but whether the preference for SESE or SEME in the meat of
the function provides the most clear, readable code, to which the answer
is SESE if you're used to that, and SEME if you're used to that... ;-)
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top