src/lib/error.h
branchnew-lib-errors
changeset 216 a10ba529ae39
child 217 7728d6ec3abf
equal deleted inserted replaced
215:85863b89e38b 216:a10ba529ae39
       
     1 #ifndef LIBQMSK_ERROR_H
       
     2 #define LIBQMSK_ERROR_H
       
     3 
       
     4 /**
       
     5  * @file
       
     6  *
       
     7  * Support for error handling, using explicit function return codes and error info contexts.
       
     8  *
       
     9  * This is designed to support multiple "namespaces" of errors, which are entirely independent of eachother. These
       
    10  * namespaces can then reference one another, to build a 'tree' of useable error codes. Hence, each 'level' of this
       
    11  * tree has it's own set of error codes, and then error info contexts contain a stack of these error codes, which can
       
    12  * then be used to trace the error down.
       
    13  */
       
    14 
       
    15 /**
       
    16  * The type used to represent a scalar error code, there are only unique per level.
       
    17  *
       
    18  * Note that this type is signed to avoid nastyness with negating error codes, but all valid ERR_* codes are *always*
       
    19  * greater than zero in value.
       
    20  */
       
    21 typedef signed short err_t;
       
    22 
       
    23 /** 
       
    24  * Standardized err_t code to mean no error, evaluates as logical false.
       
    25  */
       
    26 static const err_t SUCCESS = 0;
       
    27 
       
    28 /**
       
    29  * Extra information about an error, used to reference error codes in external libraries etc.
       
    30  */
       
    31 union error_extra {
       
    32     /** (signed) integer code */
       
    33     int int_;
       
    34 
       
    35     /** Constant string */
       
    36     const char *str;
       
    37 };
       
    38 
       
    39 /**
       
    40  * Errors can contain 'extra' information to reference things like error codes from external libraries; when these are
       
    41  * used, a 'type' must be specified to handle these.
       
    42  */
       
    43 struct error_extra_type {
       
    44     /** Short name */
       
    45     const char *name;
       
    46 
       
    47     /** Conversion func, must return a static pointer */
       
    48     const char * (*msg_func) (const struct error_extra_type *type, const union error_extra *extra);
       
    49 };
       
    50 
       
    51 /**
       
    52  * error_extra_type for libc errno values
       
    53  */
       
    54 const struct error_extra_type error_extra_errno;
       
    55 
       
    56 /**
       
    57  * error_extra_type for string error values
       
    58  */
       
    59 const struct error_extra_type error_extra_string;
       
    60 
       
    61 /**
       
    62  * Description of an error code
       
    63  */
       
    64 struct error_type {
       
    65     /** The actual code */
       
    66     err_t code;
       
    67 
       
    68     /** The short name of the error */
       
    69     const char *name;
       
    70 
       
    71     /** The type of any contained extra info */
       
    72     const struct error_extra_type *extra_type;
       
    73 
       
    74     /** Any linked error_code table for looking up sub-errors */
       
    75     const struct error_list *sublist;
       
    76 };
       
    77 
       
    78 /**
       
    79  * Helper macros to define error_type's
       
    80  */
       
    81 #define ERROR_TYPE(code, name) \
       
    82     { (code), (name), NULL, NULL }
       
    83 
       
    84 #define ERROR_TYPE_ERRNO(code, name) \
       
    85     { (code), (name), &error_extra_errno, NULL }
       
    86 
       
    87 #define ERROR_TYPE_STRING(code, name) \
       
    88     { (code), (name), &error_extra_string, NULL }
       
    89 
       
    90 #define ERROR_TYPE_CUSTOM(code, name, type) \
       
    91     { (code), (name), (type), NULL }
       
    92 
       
    93 #define ERROR_TYPE_SUB(code, name, sub) \
       
    94     { (code), (name), NULL, (sub) }
       
    95 
       
    96 #define ERROR_TYPE_END \
       
    97     { 0, NULL, NULL, NULL }
       
    98 
       
    99 /**
       
   100  * List of error types
       
   101  */
       
   102 struct error_list {
       
   103     /** Name of sublib */
       
   104     const char *name;
       
   105 
       
   106     /** The list */
       
   107     struct error_type list[];
       
   108 };
       
   109 
       
   110 /**
       
   111  * Helper macro to define an error_list
       
   112  */
       
   113 #define ERROR_LIST(name, ...) \
       
   114     { (name), __VA_ARGS__, ERROR_TYPE_END }
       
   115 
       
   116 /**
       
   117  * Maximum number of nesting levels supported for errors
       
   118  */
       
   119 #define ERROR_DEPTH_MAX 8
       
   120 
       
   121 /**
       
   122  * Information about an actual error
       
   123  */
       
   124 typedef struct error_state {
       
   125     /** The stack of error codes */
       
   126     struct error_item {
       
   127         /** This level's error code */
       
   128         err_t code;
       
   129 
       
   130         /** Optional table for interpreting error_code, can be used instead of error_code::next */
       
   131         const struct error_list *list;
       
   132 
       
   133     } stack[ERROR_DEPTH_MAX];
       
   134 
       
   135     /** Current top of stack for accumulating errors, NULL means empty stack */
       
   136     struct error_info_level *cur;
       
   137 
       
   138     /** Type info for external error info */
       
   139     const struct error_extra_type *extra_type;
       
   140 
       
   141     /** Value of external error info */
       
   142     union error_extra extra_value;
       
   143 
       
   144 } error_t;
       
   145 
       
   146 /**
       
   147  * Look up the error_type for the given table and scalar code.
       
   148  */
       
   149 const struct error_type* error_lookup_code (const struct error_list *list, err_t code);
       
   150 
       
   151 /**
       
   152  * Look up the error_type for for the given root table and error_info.
       
   153  */
       
   154 const struct error_type* error_lookup (const error_t *err);
       
   155 
       
   156 /**
       
   157  * Return the error name for the lowest level error in the given state's stack.
       
   158  */
       
   159 const char* error_name (const error_t *err);
       
   160 
       
   161 /**
       
   162  * Maximum length of messages returned by error_msg
       
   163  */
       
   164 #define ERROR_MSG_MAX 1024
       
   165 
       
   166 /**
       
   167  * Return a pointer to a statically allocated string describing the given full error
       
   168  */
       
   169 const char* error_msg (const error_t *err);
       
   170 
       
   171 /**
       
   172  * Compare the given errors for equivalency
       
   173  */
       
   174 bool error_cmp_eq (const error_t *a, const error_t *b);
       
   175 
       
   176 
       
   177 /**
       
   178  * Reset the given error state to an empty state
       
   179  */
       
   180 static inline void error_reset (error_t *err)
       
   181 {
       
   182     memset(err, 0, sizeof(*err));
       
   183 }
       
   184 
       
   185 /**
       
   186  * Evaluates to the current top of the error stack, 
       
   187  */
       
   188 static struct error_item* error_top (const error_t *err)
       
   189 {
       
   190     return err->cur ? err->cur : err->stack;
       
   191 }
       
   192 
       
   193 /**
       
   194  * Evaluate to the most recent error code pushed, or SUCCESS if none
       
   195  */
       
   196 static inline err_t error_code (error_t *err)
       
   197 {
       
   198     return err->cur ? err->cur->code : SUCCESS;
       
   199 }
       
   200 
       
   201 /**
       
   202  * Reset the error state, push the given initial err.
       
   203  */
       
   204 void error_set (error_t *err, const struct error_list *list, err_t code);
       
   205 
       
   206 /**
       
   207  * Reset, push err, and set extra.
       
   208  */
       
   209 void error_set_extra (error_t *err, const struct error_list *list, err_t code, const struct error_extra_type *type, union error_extra extra);
       
   210 
       
   211 /**
       
   212  * Push an additional level of error info onto the error state's stack. If it doesn't fit, XXX: it is ignored.
       
   213  */
       
   214 void error_push (error_t *err, const struct error_list *list, err_t code);
       
   215 
       
   216 /**
       
   217  * Copy value of error
       
   218  */
       
   219 void error_copy (error_t *dst, const error_t *src);
       
   220 
       
   221 /** Evalutes to the lvalue representing the error code at the top of the stack */
       
   222 #define ERROR_CODE(err_state) \
       
   223     (error_top(err_state)->code)
       
   224 
       
   225 /** Push a new error code onto the given state */
       
   226 #define PUSH_ERROR(err_state, err_list, err_code) ({ \
       
   227         error_push(err_state, err_list, err_code); \
       
   228         err_code; })
       
   229 
       
   230 /** Set the error to the given value */
       
   231 #define SET_ERROR(err_state, err_list, err_code) ({ \
       
   232         error_set(err_state, err_list, err_code); \
       
   233         err_code; })
       
   234 
       
   235 /* Helper macro to use error_set_extra */
       
   236 #define _ERROR_SET_EXTRA(err_state, err_list, err_code, err_extra_type, _err_extra_field_, err_extra_value) \
       
   237         error_set_extra(err_state, err_list, err_code, err_extra_type, ((union error_extra) { ._err_extra_field_ = (err_extra_value)}))
       
   238 
       
   239 /** Set the error with extra info as integer */
       
   240 #define SET_ERROR_EXTRA(err_state, err_list, err_code, err_extra_type, err_extra_int) ({ \
       
   241         _ERROR_SET_EXTRA(err_state, err_list, err_code, err_extra_type, int_, err_extra);  \
       
   242         err_code; })
       
   243 
       
   244 /** Set the error with extra info as the libc errno */
       
   245 #define SET_ERROR_ERRNO(err_state, err_list, err_code) ({ \
       
   246         _ERROR_SET_EXTRA(err_state, err_list, err_code, &error_extra_errno, int_, errno); \
       
   247         err_code; })
       
   248 
       
   249 /** Set the error with extra info as the given static string */
       
   250 #define SET_ERROR_STR(err_state, err_list, err_code, err_str) ({ \
       
   251         _ERROR_SET_EXTRA(err_state, err_list, err_code, &error_extra_string, str, err_str); \
       
   252         err_code; })
       
   253 
       
   254 /** Set the error with the contents of the given error */
       
   255 #define SET_ERROR_COPY(err_state_dst, err_state_src) ({ \
       
   256         error_code(err_state_dst); })
       
   257 
       
   258 /** Same as above, but also do a 'goto error' jump */
       
   259 #define JUMP_PUSH_ERROR(...)        do { PUSH_ERROR(__VA_ARGS__);       goto error; } while (0)
       
   260 #define JUMP_SET_ERROR(...)         do { SET_ERROR(__VA_ARGS__);        goto error; } while (0)
       
   261 #define JUMP_SET_ERROR_EXTRA(...)   do { SET_ERROR_EXTRA(__VA_ARGS__);  goto error; } while (0)
       
   262 #define JUMP_SET_ERROR_ERRNO(...)   do { SET_ERROR_ERRNO(__VA_ARGS__);  goto error; } while (0)
       
   263 #define JUMP_SET_ERROR_STR(...)     do { SET_ERROR_STR(__VA_ARGS__);    goto error; } while (0)
       
   264 #define JUMP_SET_ERROR_INFO(...)    do { SET_ERROR_INFO(__VA_ARGS__);   goto error; } while (0)
       
   265 
       
   266 
       
   267 /**
       
   268  * Abort execution of process with error message
       
   269  */
       
   270 void _error_abort (const char *file, const char *line, const char *func, const char *fmt, ...);
       
   271 #define error_abort(...) _error_abort(__FILE__, __LINE__, __func__, __VA_ARGS__);
       
   272 
       
   273 /**
       
   274  * Used to mark unexpcted conditions for switch-default. The given val must be an integer, as passed to switch
       
   275  */
       
   276 #define NOT_REACHED(val) error_abort("%s = %#x", #val, (int) (val));
       
   277 
       
   278 #endif /* LIBQMSK_ERROR_H */