Search code examples
cerror-handlinglibraries

How to create a common error handling mechanism for multiple C libraries?


Each source file of my library (libA) includes an in_err.h file.
This file defines an enum for every possible error, and an array of string describing the corresponding error.
There is a function get_err_mess() which returns the error message corresponding to the error enum.

in_err.h

typedef enum {
    E_ERR_NOERROR = OK, //OK=0
    E_ERR_1 = E_ERR_BADPARAM,
    ...
    E_ERR_MAX
} ERROR;

const char *error_description[] = {
         "No error.",
         "Bad parameter.",
         ...
         "Max error enum reached.",
};

const char * get_err_mess(ERROR err){
    ...
    return err_mess;
}

Every functions of my library (libA) returns an ERROR type.

libA.a

#include "in_err.h"
ERROR func1()
{
    ERROR err = OK;
    ...
    return err
}

The main program calls functions from libA and displays error message if any.

main.c

#include "in_err.h"
#include "headerfromlibA.h"
int main()
{
    ERROR err = OK;

    err = func1()
    if (!err){
        err = func2()
    }

    if(err){
        printf("Error: %s\n", get_err_mess(err));
    }
    return err;
}

Now I want to make another library (libB) with the same kind of error management but with its own types of errors.
My problem is that I would have two get_err_mess() functions with potentially the same error id.
I'd like to have a single get_err_mess() to manage errors from libA and libB.
What would you recommend ?

The only solution I see is to set a range of error for each library... but I don't like it


Solution

  • Basic idea: return a 16-byte struct (UNIFORM_ERROR below) on the stack.

    err.h:

    #pragma once
    
    typedef const char *(*err_msg_func)(int err);
    typedef struct {
        err_msg_func err_to_msg;
        const char *name;
    } ERR_SRC;
    
    typedef struct {
        int code;
        const ERR_SRC* src;
    } UNIFORM_ERROR;
    
    static inline const char *err_msg(UNIFORM_ERROR err) {
        return err.src->err_to_msg(err.code);
    }
    

    liba.h:

    #pragma once
    
    #include "err.h"
    
    typedef enum {
        EA_ERR_NOERROR = 0,
        EA_ERR_BADPARAM,
        EA_ERR_PERMISSION,
        EA_ERR_MAX
    } A_ERROR;
    
    UNIFORM_ERROR a_func();
    

    liba.c:

    #include "liba.h"
    
    static const char *error_msg[] = {
        "No error",
        "Bad parameter",
        "No permission",
        "Max error enum reached"
    };
    
    static const char *a_error_msg(int code) {
        return error_msg[code];
    }
    
    static const ERR_SRC a_err_src = {
        a_error_msg,
        "a"
    };
    
    static UNIFORM_ERROR mkerr(A_ERROR code) {
        UNIFORM_ERROR err = {code, &a_err_src};
        return err;
    }
    
    UNIFORM_ERROR a_func()
    {
        return mkerr(EA_ERR_BADPARAM);
    }
    

    libb.h:

    #pragma once
    
    #include "err.h"
    
    typedef enum {
        EB_ERR_NOERROR = 0,
        EB_ERR_NOTFOUND,
        EB_ERR_MAX
    } B_ERROR;
    
    UNIFORM_ERROR b_func();
    

    libb.c:

    #include "libb.h"
    
    static const char *error_msg[] = {
        "No error",
        "Not found",
        "Max error enum reached"
    };
    
    const char *b_error_msg(int code) {
        return error_msg[code];
    }
    
    static const ERR_SRC b_err_src = {
        b_error_msg,
        "b"
    };
    
    static UNIFORM_ERROR mkerr(B_ERROR code) {
        UNIFORM_ERROR err = {code, &b_err_src};
        return err;
    }
    
    UNIFORM_ERROR b_func()
    {
        return mkerr(EB_ERR_NOTFOUND);
    }
    

    main.c:

    #include <stdio.h>
    #include "liba.h"
    #include "libb.h"
    #include "err.h"
    
    int main()
    {
        UNIFORM_ERROR err;
        err = a_func();
        printf("Error %d from %s: %s\n", err.code, err.src->name, err_msg(err));
        err = b_func();
        printf("Error %d from %s: %s\n", err.code, err.src->name, err_msg(err));
    }
    

    output:

    Error 1 from a: Bad parameter
    Error 1 from b: Not found