Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error reporting in a C library

Tags:

I am looking for a robust way to report errors in a C library. Consider the simple example of a queue:

struct queue *q = malloc(sizeof(*q));
if (NULL == q) {
    /* malloc failed. now what ? */
    return NULL; /* maybe ? */
}

Okay, so for that example returning NULL isn't otherwise valid so it makes sense to return it to signal an error. But

void *get_data()
{
    /* stuff */

    /* Error detected. NULL is a valid return, now what ? */

    /* stuff */
}

What's more, once we signal an error, how to signal what is the error ? I have thought about it and don't have a satisfying solution.

  • Using errno or some other global object isn't something I would like to do (perhaps the functions may be called from multiple threads etc).

  • I thought of making the client supply some "status" object that can be inspected after the call, but that would make the API quite ugly.

So what's your take on the subject ? How do you report errors in a clean way ?

like image 803
nc3b Avatar asked Jul 01 '11 13:07

nc3b


2 Answers

int get_data(void **ptr)

If there are no obvious 'error returns', then maybe your output value should not be the return value. The error could either be an errno, some other custom detailed error value (*cough* HRESULT), just true/false for if the function succeeded, or some other bit of useful information (the length of the data, or -1 if error)

like image 162
Random832 Avatar answered Oct 03 '22 23:10

Random832


I have a couple of suggestions.

Suggestion #1 -- Use custom errnos. I know that you indicated that you would prefer not to use that. I gather that you are concerned that errno will be clobbered in a multi-threaded environment, but I would expect that each thread should have its own storage for errno. The following link http://compute.cnr.berkeley.edu/cgi-bin/man-cgi?errno+3 suggests that it is rather easy to do.

As far as structuring the custom errno's, you can always divide the errno up into two portions ... a module number and an module error code.

eg.
#define MODULE_NAME_error_code ((MODULE_NUMBER << 16) | (error_code))

In case of detected errors in your library, you can then set the errno to a desired value. If there are multiple modules, it can help identify the problem area. Of course, if your library is to be used with others that use this method, then some form of synchronization of custom errno's is required.

Suggestion #2 -- Let your routines return 0 on success with a custom non-zero value on return and let one of the parameters be a pointer to the value you want to set. Errors can be detected easily; however documenting them may be troublesome if you have deep call trees that use this methodology.

Hope this helps.

like image 42
Sparky Avatar answered Oct 03 '22 23:10

Sparky