Small question about return statements...

S

SpiralCorp

int divide (int a, int b)
{
int r;
r=a/b;
return (r);
}
int main ()
{
int result = divide (20,4);
cout << result
return 0;
}
--------------

I understand the use of the return statement as exemplified in the
example above. However, I was wondering what was the proper way for a
function to return more than one value and subsequently store those
values in their own independent variables. For instance, if the above
function were to return two different results, how would I assign them
each to their own variable by means of the return statement?

I figured I could achieve this by declaring the variables in the main
function and then passing them by reference to the function so they
could be changed within (This would work without the use of the return
function ofcourse), but something tells me this method is not exactly
kosher?

What do you guys think, whats the proper way of doing it?
 
S

Scott McPhillips [MVP]

SpiralCorp said:
int divide (int a, int b)
{
int r;
r=a/b;
return (r);
}
int main ()
{
int result = divide (20,4);
cout << result
return 0;
}
--------------

I understand the use of the return statement as exemplified in the
example above. However, I was wondering what was the proper way for a
function to return more than one value and subsequently store those
values in their own independent variables. For instance, if the above
function were to return two different results, how would I assign them
each to their own variable by means of the return statement?

I figured I could achieve this by declaring the variables in the main
function and then passing them by reference to the function so they
could be changed within (This would work without the use of the return
function ofcourse), but something tells me this method is not exactly
kosher?

What do you guys think, whats the proper way of doing it?

It is kosher and is a fine way to return multiple values from a
function. Another way is to define a struct that contains multiple
values, and use the struct as the function's return.
 
S

SpiralCorp

function. Another way is to define a struct that contains multiple
values, and use the struct as the function's return.

Good to know. I'll keep using the reference method as I'm not so
familiar with structs yet, but I'll make a note for when I am. Thanks
for the help.
 
D

Dave Rahardja

int divide (int a, int b)
{
int r;
r=a/b;
return (r);
}
int main ()
{
int result = divide (20,4);
cout << result
return 0;
}
--------------

I understand the use of the return statement as exemplified in the
example above. However, I was wondering what was the proper way for a
function to return more than one value and subsequently store those
values in their own independent variables. For instance, if the above
function were to return two different results, how would I assign them
each to their own variable by means of the return statement?

I figured I could achieve this by declaring the variables in the main
function and then passing them by reference to the function so they
could be changed within (This would work without the use of the return
function ofcourse), but something tells me this method is not exactly
kosher?

What do you guys think, whats the proper way of doing it?

It's kosher, and it's traditionally done, although it's not pretty.

Then there is the "return a struct" where the returned value is actually a
data structure:

struct Retval
{
Retval(int a, int b): a(a), b(b) {}
int a;
int b;
};

Retval fn()
{
return Retval(1, 2);
}

int main()
{
Retval retval = fn();
int my_a = retval.a;
int my_b = retval.b;
}

They're both ugly and kludgy. For a more elegant solution, see boost.tuple. It
lets you write code like this:

#include "boost/tuple.hpp"

using boost::tuples::tuple;
using boost::tuples::make_tuple;
using boost::tuples::tie;

tuple<int, int> fn()
{
return make_tuple(1, 2);
}

int main()
{
int my_a;
int my_b;
tie(my_a, my_b) = fn(); // Assigns values to my_a and my_b
}
 
B

Bo Persson

SpiralCorp said:
int divide (int a, int b)
{
int r;
r=a/b;
return (r);
}
int main ()
{
int result = divide (20,4);
cout << result
return 0;
}
--------------

I understand the use of the return statement as exemplified in the
example above. However, I was wondering what was the proper way for
a function to return more than one value and subsequently store
those values in their own independent variables. For instance, if
the above function were to return two different results, how would
I assign them each to their own variable by means of the return
statement?

It's not really "proper" for a function to return more than value. :)

Usually one function should have one purpose only, and return the result of
it's computations, if any.

If you really need to return two or more connected values, they will often
be stored in a structure anyway, because they belong together. If so, you
can return the structure.
I figured I could achieve this by declaring the variables in the
main function and then passing them by reference to the function so
they could be changed within (This would work without the use of
the return function ofcourse), but something tells me this method
is not exactly kosher?

It works, when you need to do that. But don't do it, if you don't have to.
What do you guys think, whats the proper way of doing it?

The proper way is not having to do it at all. :)



Bo Persson
 
G

Grizlyk

SpiralCorp said:
int divide (int a, int b)
{
int r;

uninitialized variable is wrong way, at least set "r" to zero
int r=0;
r=a/b;
return (r);
}
int main ()
{
int result = divide (20,4);
cout << result
return 0;
}
--------------

I understand the use of the return statement as exemplified in the
example above. However, I was wondering what was the proper way for a
function to return more than one value and subsequently store those
values in their own independent variables. For instance, if the above
function were to return two different results, how would I assign them
each to their own variable by means of the return statement?

If your function change several variables make class as data structure +
functions for them.
 
D

Dave Rahardja

It's not really "proper" for a function to return more than value. :)

Usually one function should have one purpose only, and return the result of
it's computations, if any.

If you really need to return two or more connected values, they will often
be stored in a structure anyway, because they belong together. If so, you
can return the structure.

Some functions intrinsically return more than one value that is not easily
encapsulated in a class. For example, the following function declaration often
appears in embedded systems:

enum ErrorCode { ERROR_OK, ERROR_DEVICE_ERROR /* ... */ };

ErrorCode getBufferContents(
void* buffer, // in: buffer to hold the data
unsigned long maxLength, // in: max. bytes that can fit
// in the buffer
unsigned long* bytesRetrieved // out: number of bytes retrieved
unsigned long* bytesRemaining // out: number of bytes remaining
// in the device's internal
// buffer
);

void fn()
{
static const unsigned long bufferLength = 100;
static unsigned char buffer[bufferLength];

unsigned long bytesRetrieved = 0;
unsigned long bytesRemaining = 0;
ErrorCode errorCode = getBufferContents(buffer, bufferLength,
&bytesRetrieved, &bytesRemaining);

if (errorCode == ERROR_OK)
{
// Use data
}
}

You can technically return one struct, but ofter it's easier to break the
items out so they can be used quickly.

I know, I know, return a std::vector, use exceptions, etc. However, in
embedded systems speed is essential, dynamic memory allocation (due to vector)
or anomalous execution times (due to exception handling) are undesirable, and
this method works wonderfully.

-dr
 
L

Lionel B

It's not really "proper" for a function to return more than value. :)

Usually one function should have one purpose only, and return the result of
it's computations, if any.

If you really need to return two or more connected values, they will often
be stored in a structure anyway, because they belong together. If so, you
can return the structure.

Some functions intrinsically return more than one value that is not easily
encapsulated in a class. For example, the following function declaration often
appears in embedded systems:

enum ErrorCode { ERROR_OK, ERROR_DEVICE_ERROR /* ... */ };

ErrorCode getBufferContents(
void* buffer, // in: buffer to hold the data
unsigned long maxLength, // in: max. bytes that can fit
// in the buffer
unsigned long* bytesRetrieved // out: number of bytes retrieved
unsigned long* bytesRemaining // out: number of bytes remaining
// in the device's internal
// buffer
);

void fn()
{
static const unsigned long bufferLength = 100;
static unsigned char buffer[bufferLength];

unsigned long bytesRetrieved = 0;
unsigned long bytesRemaining = 0;
ErrorCode errorCode = getBufferContents(buffer, bufferLength,
&bytesRetrieved, &bytesRemaining);

if (errorCode == ERROR_OK)
{
// Use data
}
}

That looks very much like C code. The C++ equivalent would probably be to
have the function take references rather than pointers... which you seem to
have dismissed in a previous reply in this thread as "not pretty". How is
the above code prettier?
You can technically return one struct, but ofter it's easier to break the
items out so they can be used quickly.

I know, I know, return a std::vector, use exceptions, etc.

Um, surely std::vector would not be appropriate here - as you are
obviously aware of, as in a previous reply you mentioned using
boost::tuple (std::pair would be ok too, I guess).

Are you actually two different people? ;)
However, in
embedded systems speed is essential, dynamic memory allocation (due to
vector) or anomalous execution times (due to exception handling) are
undesirable, and this method works wonderfully.

I imagine a half-decent optimising compiler would eliminate any overheads
in the struct/boost::tuple/std::pair solutions (ok, that may not be a good
argument for embedded systems). Also, I'm not sure exceptions would be
necessary/appropriate, depending on how you view an "error
condition". But you might just as well return your error code as
part of your struct/tuple.
 
G

Grizlyk

Dave said:
I know, I know, ... use exceptions, etc. However, in
embedded systems speed is essential

I think "embedded" property of system is not a cause here, because "error
during transfer" often is one of examples when error is one of correct
states, awaiting state of transfer and not suitable for C++ exceptions.

High-level functions that only return correct data can throw, but low-level
functions must expect the situation and to resolve the errors can, for
example, declare recovering interface to up level.
 
D

Dave Rahardja

That looks very much like C code. The C++ equivalent would probably be to
have the function take references rather than pointers... which you seem to
have dismissed in a previous reply in this thread as "not pretty". How is
the above code prettier?

Yes, the example is very much C code. I personally prefer the tuple
implementation for its convenience (i.e. it can be a struct (tuple) if you
want, or fill in individual variables (tie) if you want, or a mixture of
both).

I guess having worked in the embedded world for so long has caused me to
prematurely optimize at the very low level. ;-) Shame on me.
Um, surely std::vector would not be appropriate here - as you are
obviously aware of, as in a previous reply you mentioned using
boost::tuple (std::pair would be ok too, I guess).

Are you actually two different people? ;)

I used to have multiple personalities, but we're fine now. ;-)

I'm the software architecture lead on a reasonably large project, and I have
to constantly switch between very high-level architectural concerns and high
frequency, low-level bit-twiddling behavior that can cripple the system with
inefficiency. I've managed to stay pure to good OOA/OOD principles over the
vast majority of the design, but every now and then Mr. Hyde pops out and I
want to optimize! Bad Mr. Hyde.
I imagine a half-decent optimising compiler would eliminate any overheads
in the struct/boost::tuple/std::pair solutions (ok, that may not be a good
argument for embedded systems). Also, I'm not sure exceptions would be
necessary/appropriate, depending on how you view an "error
condition". But you might just as well return your error code as
part of your struct/tuple.

You are correct.

As a side note, Using C++ exceptions in a real-time system (like mine is)
makes me uneasy because of its timing concerns and its ambiguous semantics
(with respect to my application domain). In my project, every error is
expected by definition, and any unexpected error (exception) is
short-circuited to either unexpected() or terminate(), which causes the system
to reset to a safe state.

-dr
 
A

Alan Johnson

Dave said:
They're both ugly and kludgy. For a more elegant solution, see boost.tuple. It
lets you write code like this:

#include "boost/tuple.hpp"

using boost::tuples::tuple;
using boost::tuples::make_tuple;
using boost::tuples::tie;

tuple<int, int> fn()
{
return make_tuple(1, 2);
}

int main()
{
int my_a;
int my_b;
tie(my_a, my_b) = fn(); // Assigns values to my_a and my_b
}

While this is one of the more elegant solutions, I still find it a bit
"kludgy". Specifically the variables that receive the return values
have to be default constructed, which is annoying for types that do
something non-trivial in their constructor (or types that don't even
have a default constructor).

Unfortunately I think this might be the closest we can get with a pure
library solution.
 
D

Dave Rahardja

While this is one of the more elegant solutions, I still find it a bit
"kludgy". Specifically the variables that receive the return values
have to be default constructed, which is annoying for types that do
something non-trivial in their constructor (or types that don't even
have a default constructor).

Unfortunately I think this might be the closest we can get with a pure
library solution.

Actually, you can do this:

int main()
{
tuple<int, int> result = fn();
int my_a = result.get<0>;
int my_b = result.get<1>;
}

With the appropriate using statements, etc.

-dr
 
A

Alan Johnson

Dave said:
Actually, you can do this:

int main()
{
tuple<int, int> result = fn();
int my_a = result.get<0>;
int my_b = result.get<1>;
}

With the appropriate using statements, etc.

-dr

Now you have superfluous copy construction instead. Perhaps:

tuple<T1, T2> result = fn() ;
T1 & a = result.get<0>() ;
T2 & b = result.get<1>() ;

Though this is still annoyingly verbose. This seems like something the
language should take care of for me. Something like:

[T1, T2] fn()
{
return [T1(), T2()] ;
}

int main()
{
[T1 a, T2 b] = fn() ;
}
 
B

BobR

SpiralCorp said:
What do you guys think, whats the proper way of doing it?
>>
>>It is kosher and is a fine way to return multiple values from a
function. Another way is to define a struct that contains multiple
values, and use the struct as the function's return.
- - >>Scott McPhillips [VC++ MVP]

Good to know. I'll keep using the reference method as I'm not so
familiar with structs yet, but I'll make a note for when I am. Thanks
for the help.

'struct' and 'class' are very useful tools. I'll give you a short example:

#include <iostream>

struct Thing{ // same as "class Thing{ public:"
int Num;
char Ch;
}; // note semicolon at end

void Func( Thing &Th ){
Th.Num = 42;
Th.Ch = 'Z';
return;
}

int main(){
Thing Mine;
Func( Mine );
std::cout<<"Mine: Num="<<Mine.Num<<" Ch="
<<Mine.Ch<<std::endl;

int A( Mine.Num );
char B( Mine.Ch );
std::cout<<"int A="<<A<<" char B="<<B<<std::endl;

return 0;
}

That's only the very tip of the iceberg. <G>
Bob R
POVrookie
 
G

Greg Herlihy

Or just 'return a/b;'

None of these implementations are satisfactory because none answer the
question: what is divide()'s intended behavior when b == 0?

Several possible ways for divide() to handle divide-by-zero:

A) does not handle division-by-zero. (Callers must ensure b != 0)

int divide( int a, int b )
{
assert( b != 0 );
...

B) throws exception

int divide( int a, int b )
{
if ( b == 0 )
throw std::invalid_argument("divide by zero");
...

C) evaluates to 0 (emulates PowerPC behavior)

int divide( int a, int b )
{
if (b == 0)
return 0;
...

Currently, divide()'s de-facto behavior for division-by-zero is undefined.
But without an assert documenting divide's b != 0 precondition, there is no
way of being certain whether the undefined behavior in this case reflects an
intentional design decision or an accidental oversight.

Greg
 
S

SpiralCorp

SpiralCorp said:
What do you guys think, whats the proper way of doing it? >>
It is kosher and is a fine way to return multiple values from a
function. Another way is to define a struct that contains multiple
values, and use the struct as the function's return.
- - >>Scott McPhillips [VC++ MVP]
Good to know. I'll keep using the reference method as I'm not so
familiar with structs yet, but I'll make a note for when I am. Thanks
for the help.

#include <iostream>

struct Thing{ // same as "class Thing{ public:"
int Num;
char Ch;
}; // note semicolon at end

void Func( Thing &Th ){
Th.Num = 42;
Th.Ch = 'Z';
return;
}

int main(){
Thing Mine;
Func( Mine );
std::cout<<"Mine: Num="<<Mine.Num<<" Ch="
<<Mine.Ch<<std::endl;

int A( Mine.Num );
char B( Mine.Ch );
std::cout<<"int A="<<A<<" char B="<<B<<std::endl;

return 0;
}

That's only the very tip of the iceberg. <G>
Bob R
POVrookie


Indeed they are. I'm reading up on them now. And man this group is
responsive, its nice to see so many useful answers.

By the way, to everyone correcting the code up there it was just a
random example for regular use of the return statement. It was copied
from cplusplus.com's tutorial on functions.
Though this is still annoyingly verbose. This seems like something the
language should take care of for me. Something like:

[T1, T2] fn()
{
return [T1(), T2()] ;

}

int main()
{
[T1 a, T2 b] = fn() ;

}

Heh, this is exactly what I was trying to do. It really should be a
language feature in my opinion, its a very reasonable situation.
 

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,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top