aboutsummaryrefslogtreecommitdiff
path: root/chad/experimental/require.h
blob: e9a8ea8f7da7e215d3bfc13121811073c6aa0036 (plain) (blame)
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
#ifndef REQUIRE_H
#define REQUIRE_H

#include <chad/experimental/bits.h>

/* Require macros for easy uniform error handling.
 * They can be used to replace repeated error handling logic
 *  or some messier solutions.
 *
 * REQUIRE() and REQUIRE_EX() assert a statement,
 *  on failure they invoke REQUIRE_TERMINATOR,
 *  on success they return the resulting value.
 *
 * REQUIRE_EX() takes a custom error value as its first argument,
 *  REQUIRE() assumes standard C truthliness.
 *
 * Example:
 *     int extreme_example(int i) {
 *         switch (i) {
 *             case 1: return 4;
 *             case 2: return 5;
 *             default: return 0;
 *         }
 *     }
 *     
 *     signed main(void) {
 *         FILE * f = REQUIRE(fopen("a.txt", "r"));
 *     
 *         REQUIRE(3 < 5);
 *     
 *         int i = REQUIRE(extreme_example(1));
 *     
 *         return 0;
 *     }
 * Possible (stderr) output:
 *     Required operation 'fopen("a.txt", "r")' failed.
 */

thread_local long long require_v;

#ifndef REQUIRE_TERMINATOR
# define REQUIRE_TERMINATOR default_require_terminator
#endif

[[ noreturn ]]
static inline
void require_default_terminator(const char * const argv) {
    fprintf(stderr, "Required operation '%s' failed.\n", argv);
    exit(1);
}

#define REQUIRE_EX(errval, ...) (typeof(__VA_ARGS__))( \
    (typeof(__VA_ARGS__))(require_v = (long long)(__VA_ARGS__)) != (typeof(__VA_ARGS__))errval \
    ? require_v \
    : (REQUIRE_TERMINATOR(STRINGIFY(__VA_ARGS__)), 0ll) \
)

#define REQUIRE(...) REQUIRE_EX(0ll, __VA_ARGS__)

#endif