7
votes

If I have a function that returns some sort of pointer, I check for errors by setting the return to NULL on error.

char *foo(void) {
  //If stuff doesn't go okay
  return NULL;
}

char *bar = foo();
if(!bar) return 1;

This works great because I know in such cases that I will never intend to return NULL.

However, sometimes I will have functions that return integers (In this case, I am reading ints from a configuration file). However, the problem is that there is now no way to check the returned value for errors because any value (including 0) might be genuine.

A couple of workarounds:

  1. Include an error code parameter in the function
  2. Return an error code and include an int pointer as a parameter

The problem with both of these is that I have a set of functions that all do the same thing but for different types and I want to maintain a regular interface so that they can be used in the same way.

Is there another solution that doesn't involve changing the interface to the function? What is the most common way of dealing with this situation?

CHOSEN SOLUTION

Thank you for all your thoughts and answers on this.

In the end I decided that if a function is intended to return some data, an error can only be returned through an error parameter. Otherwise, the error is returned directly.

I chose this root because generally I found that when returning more complex forms of data, the number of potential errors were almost always greater than 1. This meant that using NULL as the only source of error data was impractical anyway as it meant there was no way to determine what the error actually was. With functions returning data as an int, it also became impossible to distinguish multiple different error codes from valid data.

The same is not true, of course, for functions that aren't really returning any data in which case I am free to use the return value as an error code.

I also like the fact that the above pattern makes a clear distinction between functions that return data and functions that don't.

6

6 Answers

4
votes

There's the horrid solution of a global variable - analogous to errno. Not recommended.

You might use a function to retrieve the error condition from the last call to one of the functions in your set - again, not recommended. This is not thread-safe, and it is likely that you'd share errors across functions - rather than being able to interrogate for the last error from the function you used (though that could be fixed by an appropriate design).

You might decide that all your functions return a status which is testable, and the value is returned via a pointer argument. This gives you consistency across all functions, with a change in the usage pattern. You would consistently write:

if ((rc = your_function(in1, in2, &out)) != 0)
    ...handle error...
else
    ...use returned value...

This is probably the least obnoxious solution.

The other main alternative, of passing a pointer to an error structure or pointer to a pointer to an error structure is apt to lead to resource management issues. It can be made to work if the error structure is a fixed size so that the resource management problem goes away.

Error_t err;

int result = your_function(in1, in2, &err);
if (err.code != 0)
    ...handle error...
else
    ...use returned value...

You cannot test the error condition in the same line as the function is called, which some would regard as a nuisance. It matters most when you're in the third 'else if' clause of a sequence of operations - then you have to introduce a new level of nesting where the error code as return value does not require that. In its favour, the information in the error structure can be more comprehensive than a single error number which might lead to better diagnostics.

You could use a hybrid solution - an error return code plus an error parameter to contain the extra information for improved diagnostics. It is seldom chosen, though, AFAIK.

3
votes

You could just sacrifice the largest negative or positive number for an int such as INT_MAX. Just make a const MY_INT_ERROR_VALUE and compare to it.

1
votes

There are several ways to do what you want, but all of them will make you have a non-homogeneous interface (or at list a non-homogeneous way to test for errors).

If you can't change the signature of your function you could use a global variable that your function sets when an error happens, but I think this solution would be dirtier than changing the interface.

Yet another solution could be that of simulating exceptions using a jump or a system signal, but still I wouldn't suggest this (this is also not portable).

1
votes

How about this:

void *foo(void *res)
{
    // cast the void *res to whatever type the result would be
    if (/* successfull */) 
        return res;

    /* error */
    return NULL;
}

This way you will have the same signature for all those functions, and also you can easily check for errors.

0
votes

my preference, when writing code in a language that lacks exception handling, is to always use the return value as an error indicator.

int get_value(int *value)
{
    if ( input_ok )
        *value = input;
        return 0;
    return -1;
}

this does not seem practical since it forces you to use intermediate variables to store function results, but it proved useful so many times to catch errors and handle them correctly (you can never rely on user input).

also, you should note that having a special value to show an error is not a good idea: you never know what will happen with your code. if later you want to add a new feature and unfortunately the special value is useful for this feature, what will you do ? write a new function with another special error value ? what if the input should cover the whole range of the return type ? if this is part of a bigger project, how can you make sure that every programmer that may use your function is aware of the special value ?

so err on the safe side: don't use a special value and use a dedicated error handling path.

note that you can reverse the above code and write int get_value(int *error);

-1
votes

1. Use NaNs, like floating coprocessors. Pick a value, which may never used as result, to indicate error.

# define VALUE_OF_FAIL (-99999)

So then:

char foo(void) {   
  // If stuff doesn't go okay   
  return VALUE_OF_FAIL; 
}

char bar = foo(); 
if (bar == VALUE_OF_FAIL) return 1;

Again, you have to guarantee that the result of a regular computation will be never be VALUE_OF_FAIL. Pick a number from the periphery of the integer range. You may define more fail codes, but then you should write a function wich checks a variable for fail.

2. Organize your computing code to a class, then add isValid() method:

class FooBar {

  public FooBar() {  // constructor
    this.fail = false;   // initialize here...
  }

  char foo(void) {  // the computation method

    this.fail = false;  // ...or here

    // If stuff doesn't go okay   
    this.fail = true;  // or error code
    return 0; // any value
  }

  bool failed() {
    return this.fail; 
  }

}

// outside the class    
fb = new FooBar();
char bar = fb->foo(); 
if (fb->failed()) return 1;