/src/cmp_tool/lib/common/cmp_error.h
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file cmp_error.h |
3 | | * @author Dominik Loidolt (dominik.loidolt@univie.ac.at) |
4 | | * @date 2024 |
5 | | * |
6 | | * @copyright GPLv2 |
7 | | * This program is free software; you can redistribute it and/or modify it |
8 | | * under the terms and conditions of the GNU General Public License, |
9 | | * version 2, as published by the Free Software Foundation. |
10 | | * |
11 | | * This program is distributed in the hope it will be useful, but WITHOUT |
12 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
14 | | * more details. |
15 | | * |
16 | | * @brief collection of macros to handling errors |
17 | | * This is heavy inspired by the error handling of zstd (lib/common/error_private.h) by |
18 | | * @author Yann Collet et al. |
19 | | * https://github.com/facebook/zstd/blob/dev/ |
20 | | */ |
21 | | |
22 | | #ifndef CMP_ERROR_H |
23 | | #define CMP_ERROR_H |
24 | | |
25 | | |
26 | | #include <stdint.h> |
27 | | |
28 | | |
29 | | /** |
30 | | * @brief add the CMP_ERROR prefix to the error name |
31 | | * |
32 | | * @param name the name to concatenate with cmp error prefix |
33 | | */ |
34 | | |
35 | 2.13M | #define PREFIX_CMP_ERRROR(name) CMP_ERROR_##name |
36 | | |
37 | | |
38 | | /** |
39 | | * @brief generate a return value of a error |
40 | | * |
41 | | * @param name error name without CMP_ERROR prefix to create an error code for |
42 | | */ |
43 | | |
44 | 2.13M | #define CMP_ERROR(name) ((uint32_t)-PREFIX_CMP_ERRROR(name)) |
45 | | |
46 | | |
47 | | /** |
48 | | * @brief ignore this is an internal helper |
49 | | * |
50 | | * this is a helper function to help force c99-correctness during compilation |
51 | | * under strict compilation modes variadic macro arguments can't be empty |
52 | | * however variadic function arguments can be using a function therefore lets |
53 | | * us statically check that at least one string argument was passed |
54 | | * independent of the compilation flags |
55 | | */ |
56 | | |
57 | 0 | static __inline void _force_has_format_string(const char *format, ...) { |
58 | 0 | (void)format; |
59 | 0 | } Unexecuted instantiation: cmp_error.c:_force_has_format_string Unexecuted instantiation: cmp_icu.c:_force_has_format_string Unexecuted instantiation: cmp_guess.c:_force_has_format_string |
60 | | |
61 | | |
62 | | /** |
63 | | * @brief ignore this is an internal helper |
64 | | * |
65 | | * we want to force this function invocation to be syntactically correct but |
66 | | * we don't want to force runtime evaluation of its arguments |
67 | | */ |
68 | | |
69 | | __extension__ |
70 | | #define _FORCE_HAS_FORMAT_STRING(...) \ |
71 | 3.91k | do { \ |
72 | 3.91k | if (0) { \ |
73 | 0 | _force_has_format_string(__VA_ARGS__); \ |
74 | 0 | } \ |
75 | 3.91k | } while (0) |
76 | | |
77 | | #define ERR_QUOTE(str) #str |
78 | | |
79 | | |
80 | | /** |
81 | | * @brief return the specified error if the condition evaluates to true |
82 | | * |
83 | | * @param cond the condition to evaluate |
84 | | * @param err the error to return if the condition is true |
85 | | * @param ... additional arguments for error logging |
86 | | * |
87 | | * in debug modes (DEBUGLEVEL>=3) prints additional information |
88 | | */ |
89 | | |
90 | | __extension__ |
91 | | #define RETURN_ERROR_IF(cond, err, ...) \ |
92 | 3.31M | do { \ |
93 | 4.49M | if (cond) { \ |
94 | 2.51k | debug_print_level(3, "%s:%d: Error: check %s failed, returning %s", \ |
95 | 2.51k | __FILE__, __LINE__, ERR_QUOTE(cond), \ |
96 | 2.51k | ERR_QUOTE(CMP_ERROR(err))); \ |
97 | 2.51k | _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ |
98 | 2.51k | debug_print_level(3, "-> " __VA_ARGS__); \ |
99 | 2.51k | return CMP_ERROR(err); \ |
100 | 2.51k | } \ |
101 | 3.31M | } while (0) |
102 | | |
103 | | |
104 | | /** |
105 | | * @brief unconditionally return the specified error |
106 | | * |
107 | | * @param err the error to return unconditionally |
108 | | * @param ... additional arguments for error logging |
109 | | * |
110 | | * in debug modes (DEBUGLEVEL>=3) prints additional information |
111 | | */ |
112 | | |
113 | | __extension__ |
114 | | #define RETURN_ERROR(err, ...) \ |
115 | 0 | do { \ |
116 | 0 | debug_print_level(3, "%s:%d: Error: unconditional check failed, returning %s", \ |
117 | 0 | __FILE__, __LINE__, ERR_QUOTE(ERROR(err))); \ |
118 | 0 | _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ |
119 | 0 | debug_print_level(3, "-> " __VA_ARGS__); \ |
120 | 0 | return CMP_ERROR(err); \ |
121 | 0 | } while(0) |
122 | | |
123 | | |
124 | | /** |
125 | | * @brief if the provided expression evaluates to an error code returns that error code |
126 | | * |
127 | | * @param err the error expression to evaluate |
128 | | * @param ... additional arguments for error logging |
129 | | * |
130 | | * in debug modes (DEBUGLEVEL>=3) prints additional information |
131 | | */ |
132 | | |
133 | | __extension__ |
134 | | #define FORWARD_IF_ERROR(err, ...) \ |
135 | 715k | do { \ |
136 | 715k | uint32_t const err_code = (err); \ |
137 | 715k | if (cmp_is_error(err_code)) { \ |
138 | 1.40k | debug_print_level(3, "%s:%d: Error: forwarding error in %s: %s",\ |
139 | 1.40k | __FILE__, __LINE__, ERR_QUOTE(err), \ |
140 | 1.40k | cmp_get_error_name(err_code)); \ |
141 | 1.40k | _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \ |
142 | 1.40k | debug_print_level(3, "--> " __VA_ARGS__); \ |
143 | 1.40k | return err_code; \ |
144 | 1.40k | } \ |
145 | 715k | } while(0) |
146 | | |
147 | | |
148 | | #endif /* CMP_ERROR_H */ |