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