What value and type should my functions return?

  • Thread starter dwightarmyofchampions
  • Start date
D

dwightarmyofchampions

Hello. I am currently learning the C++ language, and I'm having
trouble with functions. Generally speaking, should my functions be
returning bool, void, or int? And how is my main function supposed to
handle errors in my function definitions?

As an example, suppose I have a function foo() return true if the
function had no errors and false if an error was encountered, like so:

bool foo()
{
// do some stuff

if (SomeError)
return false;
else
return true;
}

int main()
{
if (!foo())
{
std::cout << "An error occurred in function foo. Program
exiting..."
return 1;
}

// foo returned true, so we can move on

...
return 0;
}

Or I could have foo() have an int return type and return a zero if it
succeeded and a one if an error occurred, like so:

int foo()
{
// do some stuff

if (SomeError)
return 1;
else
return 0;
}

int main()
{
if (foo())
{
std::cout << "An error occurred in function foo. Exiting..."
return 1;
}

// foo returned zero, so execution was successful and we can move on

...

return 0;
}

Or I could have foo() return a void and have no error checking at all:

void foo()
{
// do some stuff

if (SomeError)
{
// do something to fix the error
}

// get to this spot regardless whether an error occurred or not
return;
}
int main()
{
foo();
...
return 0;
}

What is the generally accepted course of action for situations like
this?
 
A

Alf P. Steinbach

* Victor Bazarov:
OOH you say "no generally speaking", OTOH you say "usually"...

Jeff's "no generelly speaking" was about routine result types. The "usually was
about error reporting. I agree with both those.

In
fact there is no single correct recommendation, like "by catching
exceptions". Depends on the QoI of the compiler. C++ exception
handling *can* be expensive. It *can* be much cheaper (quicker)
to return 'false' in case of an incomplete operation while
returning the result in a variable passed by reference, instead of
setting up the 'try-catch' mechanism in the caller. If your
function is called many millions of times, consider the performance.

Of course, there is no "generally" correct course of action. It
has to be measured.

For professional development in a more or less rational work environment,
correctness and clarity is far more important than micro-efficiency.

For a novice learning C++, which the OP describes him/her self as, it is the
only practical criterion.

Focusing on micro-efficiency here yields complexity (which lowers programmer
efficiency and learner's efficiency, wasting time on non-essentials) and
probably, ironically, also increases higher level more serious inefficiency
(e.g. algorithmic inefficiency) due to lack of focus on higher level issues and
the greatly increased cost of addressing such issues, but that is darn difficult
to measure since it's in the hypothetical realm of what if this program was
implemented very differently. It's understandable to focus only on things that
are easy to measure. But it's not good software engineering practice; software
engineering is about all those other things, which is why the main advice about
micro-optimization (known as the laws of optimization) is (1) don't do it, (2)
don't do it yet, and (3) measure, measure, measure, and hey, don't do it yet.

Cheers,

- Alf
 
D

dwightarmyofchampions

So, I guess the general sentiment then, is: returning false = simpler,
faster, and easier to understand, but throwing exceptions = slower,
but also more "proper" and more in-use in professionally-developed
code? That was what I was looking for.
 
A

Alf P. Steinbach

* (e-mail address removed):
So, I guess the general sentiment then, is: returning false = simpler,
Nope.



Nope.


and easier to understand,
Nope.


but throwing exceptions = slower,
Nope.


but also more "proper"
Nope.


and more in-use in professionally-developed code?

Nope (depends very much on other criteria such as established ungood conventions
not easily changed, e.g., exceptions not used at Google).

That was what I was looking for.

Well, it appears you have misunderstood /everything/. :)


Cheers & hth.,

- Alf
 
D

DerTopper

So, I guess the general sentiment then, is: returning false = simpler,
faster, and easier to understand, but throwing exceptions = slower,
but also more "proper" and more in-use in professionally-developed
code? That was what I was looking for.

I'd disagree. From my point of view you should only use error codes as
return values for functions whose failure must be processed
immediately, like an function that fits a line to a number of points,
or a function that loads a picture from a file.

Exceptions, on the other hand, would make more sense for functions
that should "normally" run without problems, like acquiring images
from a camera, or moving some motor-driven stages to certain
positions. If you use exceptions, you can form complex measurement
functions that don't have to be littered with error checking code: If
any single step of the complex measurement function fails, the whole
measurement fails; it doesn't matter which particular step failed
(although the exception should contain information about what went
wrong). Exceptions should be considered as just another method of
error handling that should be used when the particular failing
operation does not matter.

If you don't know which method (exceptions vs. error codes) you should
use, try to code either version and look which seems more naturally
for the problem at hand. If both look good, you should probably better
stick to error codes, as this is makes your code readable for a
greater audience: the rule is to use only those features of a language
that are actually needed to solve the problem efficiently.

Regards,
Stuart
 
J

James Kanze

So, I guess the general sentiment then, is: returning false =
simpler, faster, and easier to understand, but throwing
exceptions = slower, but also more "proper" and more in-use in
professionally-developed code? That was what I was looking
for.

No. The general sentiment is that it depends. Depending on the
type of error, returning an error status or throwing an
exception is to be prefered. There is no "general" rule, other
than the one that exceptions are for exceptional cases (but
since what's exceptional depends on the type of error, and the
application, that rule really comes back to the preceding).
There are even some cases where other solutions are to be
prefered, see e.g. std::istream.

When in doubt, the return value is probably to be prefered. If
the client code needs an exception, it's easy to map a return
code to an exception. If the client code needs a return code,
getting one from an exception requires a bit more work.

There is one thing that I think is generally agreed on: if the
function returns int, the semantics of the function should be to
calculate an integral value, and this should be reflected in the
name, e.g. elementCount(). An int is not an indicator as to
whether an operation succeeded or failed. Similarly, if a
function returns bool, the semantics of the function should be
those of a predicate, and this should be reflected in the name,
e.g. isValid(). In both cases, the function normally shouldn't
have side effects. If the function is to return a status code
reflecting the success of an operation, it should return a
status code, e.g.:
enum StatusCode { failed, succeeded } ;
StatusCode doSomething() ;
so the user can write:
if ( doSomething() == failed ) ...
or
if ( doSomething() == succeeded ) ...
A function which returns a bool (or worse, an int) to indicate
whether it succeeded or not is ambiguous: does true mean it
succeeded, or that it failed. Returning failed or succeeded
isn't.
 
A

Alf P. Steinbach

* Yannick Tremblay:
bool foo_rc();

int main()
{
bool error_flag = false;
for(int i = 0; i < 1000000 ; ++i)
{
if(!foo_rc())
{
error_flag = true;
break;
}
}

if(error_flag)
{
// handle error
return -1;
}
return 0;
}


void foo_except();

int main()
{
try
{
for(int i = 0; i < 1000000; ++i)
{
foo_except();
}
}
catch(...)
{
// handle error
return -1;
}
return 0;
}

Seems to me like a nice example that exception code "can" be faster than
return status based code. In an example like the above, there's a spare
100000 if() that get executed but is not needed in the exception
version. Of course, the effect changes if the try-catch really needs
to be inside the for loop

No, that's not "of course".

It depends on how the compiler implements exception handling.

For the so called data approach there's no overhead for normal case code.

but that would be a bit braindead me think.
So seems like in a case like that, exceptions lead to faster and more
readable code.

As always, regarding efficiency, if it matters then measure.

But efficiency is (usually) simply not very important wrt. choosing exceptions
or something else.


Cheers,

- Alf
 
J

Jorgen Grahn

There is one thing that I think is generally agreed on: if the
function returns int, the semantics of the function should be to
calculate an integral value, and this should be reflected in the
name, e.g. elementCount(). An int is not an indicator as to
whether an operation succeeded or failed.

Agreed. I have maintained C++ code which returned -1 for failure and 0
for success (like parts of the POSIX API does). Horrible -- you expect
it for things which return either a count or an error, but not for
pure predicates.
Similarly, if a
function returns bool, the semantics of the function should be
those of a predicate, and this should be reflected in the name,
e.g. isValid(). In both cases, the function normally shouldn't
have side effects. If the function is to return a status code
reflecting the success of an operation, it should return a
status code, e.g.:
enum StatusCode { failed, succeeded } ;
StatusCode doSomething() ;
so the user can write:
if ( doSomething() == failed ) ...
or
if ( doSomething() == succeeded ) ...
A function which returns a bool (or worse, an int) to indicate
whether it succeeded or not is ambiguous: does true mean it
succeeded, or that it failed.

I disagree. If the function is suitably named, it's fairly clear what
is good and what is bad outcome:

if(find(something)) ... // found it
if(!add(something, somewhere)) ... // it didn't get added
Returning failed or succeeded
isn't.

Slightly better maybe, but is it worth it? It reminds me of all the
project-specific boolean types people used to invent for C back in the
days: BOOL, BOOLEAN, Bool, SXGSGTR_BOOL, ... Every time you looked at
a new piece of code, you'd find a new, slightly different
implementation, and if you were really lucky they could coexist in one
program ...

/Jorgen
 

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,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top