By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,715 Members | 1,815 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,715 IT Pros & Developers. It's quick & easy.

Function return values...

P: n/a

I am trying to adopt a model for calling functions and checking their return
values. I'm following Scott Meyer's recommendation of not over-using
exceptions because of their potential overhead.

Here's the approach I'm currently looking at. I throw exceptions only from
constructors. Destructors, of course, do not throw exceptions. All other
functions return a signed integer. The values are all stored in one large
header file (as 'defines' or 'enums').

* If the return value == 0, the operation was successful and the caller
does not have take any action.
* If the return value 0, the operation was successful but the callee is
trying to tell the caller something and the caller may or may not take
action based on the return value.
* If the return value < 0, the operation was not successful and the caller
needs to handle the error accordingly.

Example:

void caller()
{
RetVal_t retval = createFile();

if (retval >= 0)
{
//
// Success; file was created
// We could return here or continue to probe
//

switch (retval)
{
case 0:
// file was created; no additional information
...

case FILE_CREATED_READ_ONLY:
// file was created but it is read-only
...
}

return;
}

if (retval < 0)
{
//
// We have run into an error that must be handled
//
switch (retval)
{
case DIRECTORY_DOES_NOT_EXIST:
...
case PERMISSION_DENIED:
...
case FILE_ALREADY_EXISTS:
...
}

return;
}
}

Does anyone see problems with this approach? Are there better ways to
handle function return values (especially with deeply nested functions)?

Apr 11 '08 #1
Share this Question
Share on Google+
4 Replies


P: n/a
On 11 Apr., 04:02, "barcaroller" <barcarol...@music.netwrote:
I am trying to adopt a model for calling functions and checking their return
values. *I'm following Scott Meyer's recommendation of not over-using
exceptions because of their potential overhead.

Here's the approach I'm currently looking at. *I throw exceptions only from
constructors. *Destructors, of course, do not throw exceptions. *All other
functions return a signed integer. *The values are all stored in one large
header file (as 'defines' or 'enums').

** If the return value == 0, the operation was successful and the caller
does not have take any action.
** If the return value 0, the operation was successful but the callee is
trying to tell the caller something and the caller may or may not take
action based on the return value.
** If the return value < 0, the operation was not successful and the caller
needs to handle the error accordingly.
[snip example]
>
Does anyone see problems with this approach? *Are there better ways to
handle function return values (especially with deeply nested functions)?
Yes. Use exceptions. In general, exceptions are less errorprone, and
they are often cheaper! The exception is on 32-bit Windows, where the
implementation has a small cost due - I suppose - to historical
reasons.
If you find the exceptions unhandy, you could use an alternative
approach as demonstrated by operator new.

/Peter
Apr 11 '08 #2

P: n/a
On 11 Apr., 04:02, "barcaroller" <barcarol...@music.netwrote:
I am trying to adopt a model for calling functions and checking their return
values. *I'm following Scott Meyer's recommendation of not over-using
exceptions because of their potential overhead.

Here's the approach I'm currently looking at. *I throw exceptions only from
constructors. *Destructors, of course, do not throw exceptions. *All other
functions return a signed integer. *The values are all stored in one large
header file (as 'defines' or 'enums').

** If the return value == 0, the operation was successful and the caller
does not have take any action.
** If the return value 0, the operation was successful but the callee is
trying to tell the caller something and the caller may or may not take
action based on the return value.
** If the return value < 0, the operation was not successful and the caller
needs to handle the error accordingly.
[snip example]
>
Does anyone see problems with this approach? *Are there better ways to
handle function return values (especially with deeply nested functions)?
Yes. Use exceptions. In general, exceptions are less errorprone, and
they are often cheaper! The exception is on 32-bit Windows, where the
implementation has a small cost due - I suppose - to historical
reasons.
If you find the exceptions unhandy, you could use an alternative
approach as demonstrated by operator new.

/Peter
Jun 27 '08 #3

P: n/a
On 11 avr, 04:02, "barcaroller" <barcarol...@music.netwrote:
I am trying to adopt a model for calling functions and
checking their return values. I'm following Scott Meyer's
recommendation of not over-using exceptions because of their
potential overhead.
That's only relevant is the error condition is likely to occur
very often. The general rule is to use return codes for errors
which might be handled locally, by the calling function,
exceptions for those which can certainly only be handled at a
very high level (e.g. by aborting a request or a transaction),
and assertions for errors which "cannot happen" if the rest of
the code (in the process---you don't normally abort because a
communicating process has an error) is correct. This rule can
be modified by other considerations: functions which "construct"
an object (constructors, but also user defined conversion
operators, or operators like + or -, which return a new object)
will normally use exceptions even for errors which could be
handled locally; partially, of course, because return codes
aren't available to them, but also because it is such a large
advantage to not have to deal with the possibility of an invalid
object. Finally, there are a very few cases where it makes
sense to maintain the error status in the object, for later
testing---iostream is the obvious example, but this is often the
policy taken for IEEE floating point as well. Typically, this
is only used when you have an object which can pass from a valid
state to an invalid state at any time, so the client has to
check the object state and be ready to deal with an invalid
object anyway.
Here's the approach I'm currently looking at. I throw
exceptions only from constructors. Destructors, of course, do
not throw exceptions. All other functions return a signed
integer.
That sounds a bit brutal. First, there are (or should be) a lot
of functions which simply cannot fail. And there are
doubtlessly failures (e.g. insufficient resources) which mean
that you cannot continue the operator---you must abort the
request, transaction, or whatever. And even in other cases, you
might want to return a value, or use some sort of expanded error
status.
The values are all stored in one large header file (as
'defines' or 'enums').
Which creates enormous coupling.
* If the return value == 0, the operation was successful and the caller
does not have take any action.
* If the return value 0, the operation was successful but the callee is
trying to tell the caller something and the caller may or may not take
action based on the return value.
* If the return value < 0, the operation was not successful and the caller
needs to handle the error accordingly.
Which is perhaps a valid model for some functions (although I
cannot think of any off hand). Normally, however, if the
function does something, rather than returning a value, and uses
a return code, there should be an enum for the errors which can
be produced by that function.
Example:
void caller()
{
RetVal_t retval = createFile();

if (retval >= 0)
{
//
// Success; file was created
// We could return here or continue to probe
//
switch (retval)
{
case 0:
// file was created; no additional information
...
case FILE_CREATED_READ_ONLY:
// file was created but it is read-only
...
}
return;
}
if (retval < 0)
{
//
// We have run into an error that must be handled
//
switch (retval)
{
case DIRECTORY_DOES_NOT_EXIST:
...
case PERMISSION_DENIED:
...
case FILE_ALREADY_EXISTS:
...
}
return;
}
}
You really don't want returns nested like that. In this case,
you could use an if/else, but frankly:
switch ( createFile() )
would seem just as appropriate.

For functions for which this idiom is appropriate. A function
like createFile will likely want to return some sort of handle
or identifier, so you can access the newly created file. In
this case, I'd probably use the fallible idiom, although both
Windows and Unix seem to prefer a sentinal handle for error in
general, with a separate function (GetLastError, errno) to
recover further information. (The original Barton and Nackman
Fallible didn't support information about the error, but my own
does: http://kanze.james.neuf.fr/code-en.html, component
Fallible in the Basic subsystem.
Does anyone see problems with this approach?
It's far too rigid.
Are there better ways to handle function return values
(especially with deeply nested functions)?
If you know that the error can only be handled at a much higher
level, of course, you use exceptions. That's what they're there
for. If you're not sure, I'd go with returning a Fallible, with
a wrapper function which converts the error into an exception
for those who can't handle it locally.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Jun 27 '08 #4

P: n/a

Sorry, I should have specified that I of course also use functions that
return void, bool, size_t, pointers, etc. Also, the createfile() function
is purely fictional; I used it as an example only. I use fstream for file
operations.

The method I suggested above is only for (complex) functions that need to
indicate the status of the operation they were asked to perform. C++
getters/setters, for example, are not such functions.

Jun 27 '08 #5

This discussion thread is closed

Replies have been disabled for this discussion.