开发者

Error reporting in a C library

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, onc开发者_运维知识库e 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 ?


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)


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.


If you really want a multithreaded, reentrant, way to report errors, I think you cannot escape, in every lib call, to pass a pointer to a "status" struct . That struct would have among other object state stuff, the result of the call in question.

It's ugly indeed, but not necessarily bad.


Having a "status" argument passed as a pointer may seem a bit ugly, but is accepted in C programming since higher-level error reporting facilities do not exist in the language.

Each bit in the status variable would represent a different error, so if NULL is returned, the caller will have to test whether status & ERR_NOMEM or status & ERR_IO, etc. The error masks can be defined as follows:

#define ERR_NOMEM (1 << 0)
#define ERR_IO    (1 << 1)
...

Setting the appropriate error inside the functions could be done as status |= ERR_IO.

This even gives you the flexibility to indicate more than one error sometimes - the caller can check status & (ERR_NOMEM | ERR_IO) to test whether any of the errors have occurred.


My first thought: why not use stderr, filling it with messages indicating source and cause of the problem? Or, maybe I missed your intention :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜