src/lib/error.h
branchnew-lib-errors
changeset 216 a10ba529ae39
child 217 7728d6ec3abf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/error.h	Wed May 27 23:07:00 2009 +0300
@@ -0,0 +1,278 @@
+#ifndef LIBQMSK_ERROR_H
+#define LIBQMSK_ERROR_H
+
+/**
+ * @file
+ *
+ * Support for error handling, using explicit function return codes and error info contexts.
+ *
+ * This is designed to support multiple "namespaces" of errors, which are entirely independent of eachother. These
+ * namespaces can then reference one another, to build a 'tree' of useable error codes. Hence, each 'level' of this
+ * tree has it's own set of error codes, and then error info contexts contain a stack of these error codes, which can
+ * then be used to trace the error down.
+ */
+
+/**
+ * The type used to represent a scalar error code, there are only unique per level.
+ *
+ * Note that this type is signed to avoid nastyness with negating error codes, but all valid ERR_* codes are *always*
+ * greater than zero in value.
+ */
+typedef signed short err_t;
+
+/** 
+ * Standardized err_t code to mean no error, evaluates as logical false.
+ */
+static const err_t SUCCESS = 0;
+
+/**
+ * Extra information about an error, used to reference error codes in external libraries etc.
+ */
+union error_extra {
+    /** (signed) integer code */
+    int int_;
+
+    /** Constant string */
+    const char *str;
+};
+
+/**
+ * Errors can contain 'extra' information to reference things like error codes from external libraries; when these are
+ * used, a 'type' must be specified to handle these.
+ */
+struct error_extra_type {
+    /** Short name */
+    const char *name;
+
+    /** Conversion func, must return a static pointer */
+    const char * (*msg_func) (const struct error_extra_type *type, const union error_extra *extra);
+};
+
+/**
+ * error_extra_type for libc errno values
+ */
+const struct error_extra_type error_extra_errno;
+
+/**
+ * error_extra_type for string error values
+ */
+const struct error_extra_type error_extra_string;
+
+/**
+ * Description of an error code
+ */
+struct error_type {
+    /** The actual code */
+    err_t code;
+
+    /** The short name of the error */
+    const char *name;
+
+    /** The type of any contained extra info */
+    const struct error_extra_type *extra_type;
+
+    /** Any linked error_code table for looking up sub-errors */
+    const struct error_list *sublist;
+};
+
+/**
+ * Helper macros to define error_type's
+ */
+#define ERROR_TYPE(code, name) \
+    { (code), (name), NULL, NULL }
+
+#define ERROR_TYPE_ERRNO(code, name) \
+    { (code), (name), &error_extra_errno, NULL }
+
+#define ERROR_TYPE_STRING(code, name) \
+    { (code), (name), &error_extra_string, NULL }
+
+#define ERROR_TYPE_CUSTOM(code, name, type) \
+    { (code), (name), (type), NULL }
+
+#define ERROR_TYPE_SUB(code, name, sub) \
+    { (code), (name), NULL, (sub) }
+
+#define ERROR_TYPE_END \
+    { 0, NULL, NULL, NULL }
+
+/**
+ * List of error types
+ */
+struct error_list {
+    /** Name of sublib */
+    const char *name;
+
+    /** The list */
+    struct error_type list[];
+};
+
+/**
+ * Helper macro to define an error_list
+ */
+#define ERROR_LIST(name, ...) \
+    { (name), __VA_ARGS__, ERROR_TYPE_END }
+
+/**
+ * Maximum number of nesting levels supported for errors
+ */
+#define ERROR_DEPTH_MAX 8
+
+/**
+ * Information about an actual error
+ */
+typedef struct error_state {
+    /** The stack of error codes */
+    struct error_item {
+        /** This level's error code */
+        err_t code;
+
+        /** Optional table for interpreting error_code, can be used instead of error_code::next */
+        const struct error_list *list;
+
+    } stack[ERROR_DEPTH_MAX];
+
+    /** Current top of stack for accumulating errors, NULL means empty stack */
+    struct error_info_level *cur;
+
+    /** Type info for external error info */
+    const struct error_extra_type *extra_type;
+
+    /** Value of external error info */
+    union error_extra extra_value;
+
+} error_t;
+
+/**
+ * Look up the error_type for the given table and scalar code.
+ */
+const struct error_type* error_lookup_code (const struct error_list *list, err_t code);
+
+/**
+ * Look up the error_type for for the given root table and error_info.
+ */
+const struct error_type* error_lookup (const error_t *err);
+
+/**
+ * Return the error name for the lowest level error in the given state's stack.
+ */
+const char* error_name (const error_t *err);
+
+/**
+ * Maximum length of messages returned by error_msg
+ */
+#define ERROR_MSG_MAX 1024
+
+/**
+ * Return a pointer to a statically allocated string describing the given full error
+ */
+const char* error_msg (const error_t *err);
+
+/**
+ * Compare the given errors for equivalency
+ */
+bool error_cmp_eq (const error_t *a, const error_t *b);
+
+
+/**
+ * Reset the given error state to an empty state
+ */
+static inline void error_reset (error_t *err)
+{
+    memset(err, 0, sizeof(*err));
+}
+
+/**
+ * Evaluates to the current top of the error stack, 
+ */
+static struct error_item* error_top (const error_t *err)
+{
+    return err->cur ? err->cur : err->stack;
+}
+
+/**
+ * Evaluate to the most recent error code pushed, or SUCCESS if none
+ */
+static inline err_t error_code (error_t *err)
+{
+    return err->cur ? err->cur->code : SUCCESS;
+}
+
+/**
+ * Reset the error state, push the given initial err.
+ */
+void error_set (error_t *err, const struct error_list *list, err_t code);
+
+/**
+ * Reset, push err, and set extra.
+ */
+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);
+
+/**
+ * Push an additional level of error info onto the error state's stack. If it doesn't fit, XXX: it is ignored.
+ */
+void error_push (error_t *err, const struct error_list *list, err_t code);
+
+/**
+ * Copy value of error
+ */
+void error_copy (error_t *dst, const error_t *src);
+
+/** Evalutes to the lvalue representing the error code at the top of the stack */
+#define ERROR_CODE(err_state) \
+    (error_top(err_state)->code)
+
+/** Push a new error code onto the given state */
+#define PUSH_ERROR(err_state, err_list, err_code) ({ \
+        error_push(err_state, err_list, err_code); \
+        err_code; })
+
+/** Set the error to the given value */
+#define SET_ERROR(err_state, err_list, err_code) ({ \
+        error_set(err_state, err_list, err_code); \
+        err_code; })
+
+/* Helper macro to use error_set_extra */
+#define _ERROR_SET_EXTRA(err_state, err_list, err_code, err_extra_type, _err_extra_field_, err_extra_value) \
+        error_set_extra(err_state, err_list, err_code, err_extra_type, ((union error_extra) { ._err_extra_field_ = (err_extra_value)}))
+
+/** Set the error with extra info as integer */
+#define SET_ERROR_EXTRA(err_state, err_list, err_code, err_extra_type, err_extra_int) ({ \
+        _ERROR_SET_EXTRA(err_state, err_list, err_code, err_extra_type, int_, err_extra);  \
+        err_code; })
+
+/** Set the error with extra info as the libc errno */
+#define SET_ERROR_ERRNO(err_state, err_list, err_code) ({ \
+        _ERROR_SET_EXTRA(err_state, err_list, err_code, &error_extra_errno, int_, errno); \
+        err_code; })
+
+/** Set the error with extra info as the given static string */
+#define SET_ERROR_STR(err_state, err_list, err_code, err_str) ({ \
+        _ERROR_SET_EXTRA(err_state, err_list, err_code, &error_extra_string, str, err_str); \
+        err_code; })
+
+/** Set the error with the contents of the given error */
+#define SET_ERROR_COPY(err_state_dst, err_state_src) ({ \
+        error_code(err_state_dst); })
+
+/** Same as above, but also do a 'goto error' jump */
+#define JUMP_PUSH_ERROR(...)        do { PUSH_ERROR(__VA_ARGS__);       goto error; } while (0)
+#define JUMP_SET_ERROR(...)         do { SET_ERROR(__VA_ARGS__);        goto error; } while (0)
+#define JUMP_SET_ERROR_EXTRA(...)   do { SET_ERROR_EXTRA(__VA_ARGS__);  goto error; } while (0)
+#define JUMP_SET_ERROR_ERRNO(...)   do { SET_ERROR_ERRNO(__VA_ARGS__);  goto error; } while (0)
+#define JUMP_SET_ERROR_STR(...)     do { SET_ERROR_STR(__VA_ARGS__);    goto error; } while (0)
+#define JUMP_SET_ERROR_INFO(...)    do { SET_ERROR_INFO(__VA_ARGS__);   goto error; } while (0)
+
+
+/**
+ * Abort execution of process with error message
+ */
+void _error_abort (const char *file, const char *line, const char *func, const char *fmt, ...);
+#define error_abort(...) _error_abort(__FILE__, __LINE__, __func__, __VA_ARGS__);
+
+/**
+ * Used to mark unexpcted conditions for switch-default. The given val must be an integer, as passed to switch
+ */
+#define NOT_REACHED(val) error_abort("%s = %#x", #val, (int) (val));
+
+#endif /* LIBQMSK_ERROR_H */