sintaxis - Informe de errores en una biblioteca C
principales librerias en lenguaje c (5)
Mi primer pensamiento: ¿por qué no utilizar stderr, llenándolo con mensajes que indiquen la fuente y la causa del problema? O tal vez me perdí tu intención :)
Estoy buscando una forma robusta de informar errores en una biblioteca de C. Considere el ejemplo simple de una cola:
struct queue *q = malloc(sizeof(*q));
if (NULL == q) {
/* malloc failed. now what ? */
return NULL; /* maybe ? */
}
De acuerdo, entonces, para ese ejemplo, devolver NULL
no es válido por lo que tiene sentido devolverlo para señalar un error. Pero
void *get_data()
{
/* stuff */
/* Error detected. NULL is a valid return, now what ? */
/* stuff */
}
Además, una vez que señalizamos un error, ¿cómo indicar cuál es el error? Lo he pensado y no tengo una solución satisfactoria.
Usar
errno
u otro objeto global no es algo que me gustaría hacer (quizás las funciones se puedan invocar desde múltiples hilos, etc.).Pensé en hacer que el cliente proporcionara un objeto de "estado" que se pueda inspeccionar después de la llamada, pero eso haría que la API fuera bastante fea.
Entonces, ¿cuál es su opinión sobre el tema? ¿Cómo informa errores de una manera limpia?
Si realmente quiere una forma multihilo, reentrante, para reportar errores, creo que no puede escapar, en cada llamada de lib, pasar un puntero a una estructura de "estado". Esa estructura tendría entre otras cosas de estado de objeto, el resultado de la llamada en cuestión.
De hecho es feo, pero no necesariamente malo.
Tener un argumento de "estado" pasado como un puntero puede parecer un poco feo, pero se acepta en la programación en C ya que los recursos de informes de errores de nivel superior no existen en el idioma.
Cada bit en la variable de estado representaría un error diferente, por lo que si se devuelve NULL
, la persona que llama tendrá que probar si el status & ERR_NOMEM
o el status & ERR_IO
, etc. Las máscaras de error se pueden definir de la siguiente manera:
#define ERR_NOMEM (1 << 0)
#define ERR_IO (1 << 1)
...
El ajuste del error apropiado dentro de las funciones se puede hacer como status |= ERR_IO
.
Esto incluso le da la flexibilidad de indicar más de un error a veces: la persona que llama puede verificar el status & (ERR_NOMEM | ERR_IO)
para probar si se ha producido alguno de los errores.
Tengo un par de sugerencias.
Sugerencia n. ° 1 : use errnos personalizados. Sé que usted indicó que preferiría no usar eso. Supongo que le preocupa que errno sea destruido en un entorno de subprocesos múltiples, pero esperaría que cada subproceso tuviera su propio almacenamiento para errno. El siguiente enlace http://compute.cnr.berkeley.edu/cgi-bin/man-cgi?errno+3 sugiere que es bastante fácil de hacer.
En cuanto a la estructuración de los errno personalizados, siempre puede dividir el errno en dos partes ... un número de módulo y un código de error de módulo.
eg.
#define MODULE_NAME_error_code ((MODULE_NUMBER << 16) | (error_code))
En caso de errores detectados en su biblioteca, puede configurar el error en el valor deseado. Si hay varios módulos, puede ayudar a identificar el área problemática. Por supuesto, si su biblioteca se va a usar con otras que usan este método, entonces se requiere alguna forma de sincronización de errno personalizados.
Sugerencia n. ° 2 : deje que sus rutinas devuelvan 0 en caso de éxito con un valor personalizado distinto de cero en el retorno y deje que uno de los parámetros sea un puntero al valor que desea establecer. Los errores se pueden detectar fácilmente; sin embargo, documentarlos puede ser problemático si tienes árboles de llamadas profundos que usan esta metodología.
Espero que esto ayude.
int get_data(void **ptr)
Si no hay ''retornos de error'' obvios, entonces tal vez su valor de salida no debería ser el valor de retorno. El error podría ser un errno, algún otro valor de error detallado personalizado (* tos * HRESULT), solo verdadero / falso para si la función tuvo éxito, o algún otro bit de información útil (la longitud de los datos, o -1 si el error )