Function return values...

B

barcaroller

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)?
 
L

Looney

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)?

you should really rethink about throwing exceptions out of
constructors
as that pretty much means that whenever an object of you class is
created , even implictly
you need a try catch around it as it can potentially fail, which is
not good, try initialize your objects to a default state,
if the class can not be initialized to a use able state, then u can
allways have a member boolean variable which the client code can query
if
the object got initialised correctly so the client can use it.
remember compilers can use the constructor for implicit conversions
too so you need exception handling there as well.

Also as a recommendation you may not need enums and defines for every
function u can just use bool,
only in special cases returning an enum might ever add anything, as
just returning a bool can simplfy your client code lots and make it
more maintainable
Also remember u can allways provide overloads which take in an enum
variable reference as well
so
enum your_enum_name
{
do_not_care,
test1,
test2
};

bool createFile();
bool createFile(your_enum_name& val);// now if the client code wants
that info pass in a

void caller()
{
//the code structure is so much more clearer , if u need more
information from your
//function calls u can all ways pass an argument to create
file function to retrieve that info
// this way not all your client code would end up so coupled
to these enums
if (createFile())
{}
else
{}

your_enum_name fetch_info;
if (createFile(fetch_info))// can allways have an argument of
your enum so if your client code wants that info it pass in an
argument explicitly
{}
else
{}

}

also try stay away from of #defines they are evil ( I think that's tip
form Scott Meyer's too).

Just my 2 cents
 
S

Saeed Amrollahi

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)?

Hi

I think, above error handling strategy, is a mindset from C
programming. I don't think
using adequate exception handling has potential overhead. Exception
class hierarchy, RAII,
catching exception objects by reference, try/throw/catch, ... are
better idioms than returning error code.
When I encounter problems in using exception handling, I will refer to
Chapter 14 of TC++PL and the following link:
http://parashift.com/c++-faq-lite/exceptions.html#faq-17.12

Regards,
S. Amrollahi
 
P

peter koch

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
 
J

James Kanze

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

barcaroller

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.
 

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,999
Messages
2,570,243
Members
46,836
Latest member
login dogas

Latest Threads

Top