|
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 */ |