-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathassertions.h
289 lines (265 loc) · 11.8 KB
/
assertions.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/**
* Assertions assertion library v1.0.0
* This is a header-only C library to help write better tests
* using a more complete assert than the one provided in the
* standard C library.
*
* A lot of the design of this library is influenced by Criterion's
* assertions, but there is hardly any code actually used from it,
* apart from variadic utility macros.
*/
#ifndef ASSERTIONS_H
#define ASSERTIONS_H
#include <stdlib.h>
#include <stdio.h>
// Utility macros
// All of these macros are pretty much helpers and are not meant to be
// used outside of this header. They are really just here to make complex
// things more simple. All of these macros are internal, as they have the
// "ASSERTS_" prefix. Any macro with that prefix is subject to change at
// any time without warning.
// Expand the parameter. This is usefull when calling a macro from another
// macro to ensure that it expands completely when the preprocessor runs.
#define ASSERTS_EXPAND(x) x
// Get the tail of the variadic arguments. This will check to see how many
// arguments there are, and if there are more than 1, it just returns everything
// after the first. This allows there to be only 1 argument.
#define ASSERTS_VA_TAIL(...) ASSERTS_EXPAND(ASSERTS_VA_TAIL_HELPER(ASSERTS_VA_TAIL_SELECT(_, __VA_ARGS__), __VA_ARGS__))
// Helper macros for getting the tail of variadic arguments. These are what actually
// implement the functionality.
#define ASSERTS_VA_TAIL_HELPER(N, ...) ASSERTS_EXPAND(ASSERTS_VA_TAIL_HELPER_(N, __VA_ARGS__))
#define ASSERTS_VA_TAIL_HELPER_(N, ...) ASSERTS_EXPAND(ASSERTS_VA_TAIL_HELPER_ ## N(__VA_ARGS__))
#define ASSERTS_VA_TAIL_HELPER_1(Head)
#define ASSERTS_VA_TAIL_HELPER_2(Head, ...) __VA_ARGS__
// Get the head of the variadic arguments. This will just return whatever the first
// argument is, and disregard the rest.
#define ASSERTS_VA_HEAD(...) ASSERTS_EXPAND(ASSERTS_VA_HEAD_HELPER(ASSERTS_VA_TAIL_SELECT(_, __VA_ARGS__), __VA_ARGS__))
// Helper macros for getting the head of variadic arguments. These are what actually
// implement the functionality.
#define ASSERTS_VA_HEAD_HELPER(N, ...) ASSERTS_EXPAND(ASSERTS_VA_HEAD_HELPER_(N, __VA_ARGS__))
#define ASSERTS_VA_HEAD_HELPER_(N, ...) ASSERTS_EXPAND(ASSERTS_VA_HEAD_HELPER_ ## N(__VA_ARGS__))
#define ASSERTS_VA_HEAD_HELPER_1(Head) Head
#define ASSERTS_VA_HEAD_HELPER_2(Head, ...) Head
// Construct a printf based on the number of arguments. This takes the file and line
// to print at, and variadic arguments. It requires at least one, being the condition
// that is being checked. The ones after that are optional. Without any extra arguments,
// This will print a generic error message and a string version of the condition. With
// one argument, it uses it as a format string instead of the generic error message. With
// anything over one argument, they are used as format arguments being passed into printf.
#define ASSERTS_VA_PRINTF(File, Line, ...) ASSERTS_EXPAND(ASSERTS_VA_PRINTF_HELPER(File, Line, ASSERTS_VA_HEAD(__VA_ARGS__), ASSERTS_VA_TAIL_SELECT(__VA_ARGS__), ASSERTS_VA_TAIL(__VA_ARGS__)))
// Helper macros for creating a printf statement. These are what actually implement
// the functionality.
#define ASSERTS_VA_PRINTF_HELPER(File, Line, Condition, N, ...) ASSERTS_EXPAND(ASSERTS_VA_PRINTF_HELPER_(File, Line, Condition, N, __VA_ARGS__))
#define ASSERTS_VA_PRINTF_HELPER_(File, Line, Condition, N, ...) ASSERTS_EXPAND(ASSERTS_VA_PRINTF_HELPER_ ## N(File, Line, Condition, __VA_ARGS__))
#define ASSERTS_VA_PRINTF_HELPER_0(File, Line, Condition, Head) fprintf(stderr, "[%s:%d] Assertion failed: `" #Condition "`\n", File, Line)
#define ASSERTS_VA_PRINTF_HELPER_1(File, Line, Condition, Head) fprintf(stderr, "[%s:%d] " Head "\n", File, Line)
#define ASSERTS_VA_PRINTF_HELPER_2(File, Line, Condition, Head, ...) fprintf(stderr, "[%s:%d] " Head "\n", File, Line, __VA_ARGS__)
// Tail selector macro. This takes variadic arguments, and depending
// on how many there are will return 0, 1, or 2. If there is one argument,
// 0 is returned. 1 for two arguments, and 2 for anything over 1.
#define ASSERTS_VA_TAIL_SELECT(...) \
ASSERTS_EXPAND(ASSERTS_VA_TAIL_SELECT64(__VA_ARGS__, \
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \
2, 1, 0, _))
// Helper macro for tail select. This is basically where the magic happens.
#define ASSERTS_VA_TAIL_SELECT64( \
_01, _02, _03, _04, _05, _06, _07, _08, _09, _10, \
_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
_31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
_51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
_61, _62, _63, X, ...) X
// Assertion macros
// These are the macros for the actual implementation of assertions.
#define Assert_impl(File, Line, ...) \
do { \
int passed = !!(ASSERTS_VA_HEAD(__VA_ARGS__)); \
if (!passed) { \
ASSERTS_VA_PRINTF(File, Line, __VA_ARGS__); \
abort(); \
} \
} while(0)
/**
* Assert that an expression is true
* Make an assertion that the specified expression is true. If it is
* not, print an error message and abort the program. This macro is
* variadic to allow for custom error formatting. By default, an
* error message will be constructed using only the condition. If two
* arguments are specified, the second will be interpreted as a message
* string, and will be printed instead of the condition. Any extra arguments
* will be interpreted as format arguments for the error message.
*
* Example:
* ```c
* Assert(1);
* Assert(1, "Expected success");
* Assert(1, "Expected success with %d", 1);
* ```
*
* Arguments:
* - `Condition` This is the condition to be tested. If false, an error will occur
* - [_Optional_] `Message` An error message to print if the condition is false
* - [_Optional_] `...` Format parameters to pass to the print if the condition is false
*/
#define Assert(...) Assert_impl(__FILE__, __LINE__, __VA_ARGS__)
// Comparison macros
// These are macros to help compare one or more values. They are really just
// here to provide an alternate way of writing an expression, but might receive
// more functionality in the future.
#define ASSERTS_EQ_(Actual, Expected) ((Actual) == (Expected))
/**
* Check the equality of two values
* Take two values and make an exact comparison between the two.
* This is simply a cleaner way to write `Actual == Expected`.
* If the two values are not of a type that is meant to be directly
* compared, then don't use this macro. For example, rather than
* comparing the equality of two strings, it will just compare their
* pointers and almost always return false.
*
* Example:
* ```c
* Assert(EQ(1, 1)); // Passes
* Assert(EQ(0, 1)); // Fails
* ```
*
* Arguments:
* - `Actual` The actual value that was calculated
* - `Expected` The value that was expected to be received
*/
#define EQ(Actual, Expected) ASSERTS_EXPAND(ASSERTS_EQ_(Actual, Expected))
#define ASSERTS_NOT_(Condition) !(Condition)
/**
* Invert a condition
* Take a condition and invert its value. For a zero or false
* condition, the result is 1, and for a non-zero or true condition,
* the result is zero. This is simply a cleaner and more expressive
* way of writing `!Condition`.
*
* Example:
* ```c
* Assert(Not(0)); // Passes
* Assert(Not(1)); // Fails
* ```
*
* Arguments:
* - `Condition` The condition to negate
*/
#define Not(Condition) ASSERTS_EXPAND(ASSERTS_NOT_(Condition))
#define ASSERTS_LT_(Left, Right) ((Left) < (Right))
/**
* Check if a value is less than another
* A simple check to see if one value is less than another.
* This is simply a cleaner and more expressive way of writing
* `Left < Right`.
* If the values are not meant to be directly compared, do not
* use this macro. It compares the values directly without checking
* for special types, and can cause things like floating point
* inaccuracy.
*
* Example:
* ```c
* Assert(LT(0, 1)); // Passes
* Assert(LT(1, 1)); // Fails
* Assert(LT(1, 0)); // Fails
* ```
*
* Arguments:
* - `Left` The left-hand side of the expression
* - `Right` The right-hand side of the expression
*/
#define LT(Left, Right) ASSERTS_EXPAND(ASSERTS_LT_(Left, Right))
#define ASSERTS_LE_(Left, Right) ((Left) <= (Right))
/**
* Check if a value is less than or equal to another
* A simple check to see if one value is less than or equal to another.
* This is simply a cleaner and more expressive way of writing
* `Left <= Right`.
* If the values are not meant to be directly compared, do not
* use this macro. It compares the values directly without checking
* for special types, and can cause things like floating point
* inaccuracy.
*
* Example:
* ```c
* Assert(LE(0, 1)); // Passes
* Assert(LE(1, 1)); // Passes
* Assert(LE(1, 0)); // Fails
* ```
*
* Arguments:
* - `Left` The left-hand side of the expression
* - `Right` The right-hand side of the expression
*/
#define LE(Left, Right) ASSERTS_EXPAND(ASSERTS_LE_(Left, Right))
#define ASSERTS_GT_(Left, Right) ((Left) > (Right))
/**
* Check if a value is greater than another
* A simple check to see if one value is greater than another.
* This is simply a cleaner and more expressive way of writing
* `Left > Right`.
* If the values are not meant to be directly compared, do not
* use this macro. It compares the values directly without checking
* for special types, and can cause things like floating point
* inaccuracy.
*
* Example:
* ```c
* Assert(GT(1, 0)); // Passes
* Assert(GT(1, 1)); // Fails
* Assert(GT(0, 1)); // Fails
* ```
*
* Arguments:
* - `Left` The left-hand side of the expression
* - `Right` The right-hand side of the expression
*/
#define GT(Left, Right) ASSERTS_EXPAND(ASSERTS_GT_(Left, Right))
#define ASSERTS_GE_(Left, Right) ((Left) >= (Right))
/**
* Check if a value is greater than or equal to another
* A simple check to see if one value is greater than or equal to another.
* This is simply a cleaner and more expressive way of writing
* `Left >= Right`.
* If the values are not meant to be directly compared, do not
* use this macro. It compares the values directly without checking
* for special types, and can cause things like floating point
* inaccuracy.
*
* Example:
* ```c
* Assert(GE(1, 0)); // Passes
* Assert(GE(1, 1)); // Passes
* Assert(GE(0, 1)); // Fails
* ```
*
* Arguments:
* - `Left` The left-hand side of the expression
* - `Right` The right-hand side of the expression
*/
#define GE(Left, Right) ASSERTS_EXPAND(ASSERTS_GE_(Left, Right))
#define ASSERTS_ZERO_(Value) (Value) == 0
/**
* Check if a value is equal to zero
* A simple check to see if a value is equal to zero. This is
* simply a cleaner and more expressive way of writing `Value == 0`.
* If the value is not meant to be directly compared to zero, do not
* use this macro. It compares the value directly to zero without checking
* for special types, and can cause things like floating point
* inaccuracy.
*
* ```c
* Assert(Zero(0)); // Passes
* Assert(Zero(1)); // Fails
* ```
*
* Arguments:
* - `Value` The value to compare with zero
*/
#define Zero(Value) ASSERTS_EXPAND(ASSERTS_ZERO_(Value))
#endif // _ASSERTIONS_H