在C库中报告错误

时间:2022-01-27 16:58:02

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

我正在寻找一种可靠的方法来报告C库中的错误。考虑一个队列的简单示例:

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

对于这个例子返回NULL是无效的所以返回它来表示错误是有意义的。但

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

    使用errno或其他全局对象不是我想做的事情(也许函数可以从多个线程调用)。

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

    我想让客户端提供一些可以在调用后检查的“状态”对象,但是这会使API非常难看。

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

你对这个问题有什么看法?如何以一种干净的方式报告错误?

5 个解决方案

#1


5  

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)

如果没有明显的“错误返回”,那么您的输出值应该不是返回值。错误可以是errno,其他自定义的详细错误值(*cough* HRESULT),如果函数成功,则是true/false,或者是其他一些有用的信息(数据的长度,如果错误,则是-1)

#2


5  

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.

建议#1——使用自定义错误。我知道你暗示过你不愿意使用它。我猜想您可能担心errno会在多线程环境中遭到破坏,但是我希望每个线程都应该有自己的errno存储。以下链接http://compute.cnr.berkeley.edu/cgi-bin/man-cgi?errno+3表示这很容易做到。

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.

至于构造自定义errno的结构,您总是可以将errno分为两部分……模块号和模块错误代码。

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.

如果在您的库中发现错误,您可以将errno设置为所需的值。如果有多个模块,它可以帮助识别问题区域。当然,如果您的库要与使用此方法的其他库一起使用,那么需要某种形式的自定义errno的同步。

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.

建议#2——让您的例程在成功时返回0,在返回时返回一个自定义的非零值,并让其中一个参数成为您想要设置的值的指针。但是,如果您有使用这种方法的深度调用树,那么记录它们可能会很麻烦。

Hope this helps.

希望这个有帮助。

#3


3  

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.

如果您真的想要一个多线程的、可重入的、报告错误的方法,我认为您不能在每个lib调用中转义,传递一个指向“状态”结构体的指针。在其他对象状态中,这个结构体会有,这个调用的结果。

It's ugly indeed, but not necessarily bad.

它确实很丑,但不一定是坏的。

#4


3  

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.

将“status”参数作为指针传递可能有点难看,但在C编程中是可以接受的,因为语言中不存在高级的错误报告工具。

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:

状态变量中的每个位代表一个不同的错误,因此如果返回NULL,调用者将必须测试status & ERR_NOMEM还是status & ERR_IO等。

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

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

在函数中设置适当的错误可以作为状态|= 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.

这甚至为您提供了指示多个错误的灵活性——调用者可以检查状态& (ERR_NOMEM | ERR_IO),以测试是否发生了任何错误。

#5


-3  

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

我的第一个想法是:为什么不使用stderr,在其中填充表明问题来源和原因的消息?或者,也许我没注意到你的意图

#1


5  

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)

如果没有明显的“错误返回”,那么您的输出值应该不是返回值。错误可以是errno,其他自定义的详细错误值(*cough* HRESULT),如果函数成功,则是true/false,或者是其他一些有用的信息(数据的长度,如果错误,则是-1)

#2


5  

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.

建议#1——使用自定义错误。我知道你暗示过你不愿意使用它。我猜想您可能担心errno会在多线程环境中遭到破坏,但是我希望每个线程都应该有自己的errno存储。以下链接http://compute.cnr.berkeley.edu/cgi-bin/man-cgi?errno+3表示这很容易做到。

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.

至于构造自定义errno的结构,您总是可以将errno分为两部分……模块号和模块错误代码。

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.

如果在您的库中发现错误,您可以将errno设置为所需的值。如果有多个模块,它可以帮助识别问题区域。当然,如果您的库要与使用此方法的其他库一起使用,那么需要某种形式的自定义errno的同步。

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.

建议#2——让您的例程在成功时返回0,在返回时返回一个自定义的非零值,并让其中一个参数成为您想要设置的值的指针。但是,如果您有使用这种方法的深度调用树,那么记录它们可能会很麻烦。

Hope this helps.

希望这个有帮助。

#3


3  

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.

如果您真的想要一个多线程的、可重入的、报告错误的方法,我认为您不能在每个lib调用中转义,传递一个指向“状态”结构体的指针。在其他对象状态中,这个结构体会有,这个调用的结果。

It's ugly indeed, but not necessarily bad.

它确实很丑,但不一定是坏的。

#4


3  

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.

将“status”参数作为指针传递可能有点难看,但在C编程中是可以接受的,因为语言中不存在高级的错误报告工具。

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:

状态变量中的每个位代表一个不同的错误,因此如果返回NULL,调用者将必须测试status & ERR_NOMEM还是status & ERR_IO等。

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

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

在函数中设置适当的错误可以作为状态|= 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.

这甚至为您提供了指示多个错误的灵活性——调用者可以检查状态& (ERR_NOMEM | ERR_IO),以测试是否发生了任何错误。

#5


-3  

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

我的第一个想法是:为什么不使用stderr,在其中填充表明问题来源和原因的消息?或者,也许我没注意到你的意图