1 | // CODYlib -*- mode:c++ -*- |
2 | // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org |
3 | // License: Apache v2.0 |
4 | |
5 | #include "cody.hh" |
6 | |
7 | #ifndef __has_builtin |
8 | #define __has_builtin(X) 0 |
9 | #endif |
10 | #ifndef __has_include |
11 | #define __has_include(X) 0 |
12 | #endif |
13 | |
14 | // C++ |
15 | #if __has_builtin(__builtin_FILE) && __has_builtin(__builtin_LINE) |
16 | #define CODY_LOC_BUILTIN 1 |
17 | #elif __has_include (<source_location>) |
18 | #include <source_location> |
19 | #ifdef __cpp_lib_source_location |
20 | #define CODY_LOC_SOURCE 1 |
21 | #endif |
22 | #endif |
23 | |
24 | // C |
25 | #include <cstdio> |
26 | |
27 | namespace Cody { |
28 | |
29 | // Location is needed regardless of checking, to make the fatal |
30 | // handler simpler |
31 | class Location |
32 | { |
33 | protected: |
34 | char const *file; |
35 | unsigned line; |
36 | |
37 | public: |
38 | constexpr Location (char const *file_ |
39 | #if CODY_LOC_BUILTIN |
40 | = __builtin_FILE () |
41 | #elif !CODY_LOC_SOURCE |
42 | = nullptr |
43 | #endif |
44 | , unsigned line_ |
45 | #if CODY_LOC_BUILTIN |
46 | = __builtin_LINE () |
47 | #elif !CODY_LOC_SOURCE |
48 | = 0 |
49 | #endif |
50 | ) |
51 | :file (file_), line (line_) |
52 | { |
53 | } |
54 | |
55 | #if !CODY_LOC_BUILTIN && CODY_LOC_SOURCE |
56 | using source_location = std::source_location; |
57 | |
58 | constexpr Location (source_location loc = source_location::current ()) |
59 | : Location (loc.file (), loc.line ()) |
60 | { |
61 | } |
62 | #endif |
63 | |
64 | public: |
65 | constexpr char const *File () const |
66 | { |
67 | return file; |
68 | } |
69 | constexpr unsigned Line () const |
70 | { |
71 | return line; |
72 | } |
73 | }; |
74 | |
75 | void HCF [[noreturn]] |
76 | ( |
77 | char const *msg |
78 | #if NMS_CHECKING |
79 | , Location const = Location () |
80 | #if !CODY_LOC_BUILTIN && !CODY_LOC_SOURCE |
81 | #define HCF(M) HCF ((M), Cody::Location (__FILE__, __LINE__)) |
82 | #endif |
83 | #endif |
84 | ) noexcept; |
85 | |
86 | #if NMS_CHECKING |
87 | void AssertFailed [[noreturn]] (Location loc = Location ()) noexcept; |
88 | void Unreachable [[noreturn]] (Location loc = Location ()) noexcept; |
89 | #if !CODY_LOC_BUILTIN && !CODY_LOC_SOURCE |
90 | #define AssertFailed() AssertFailed (Cody::Location (__FILE__, __LINE__)) |
91 | #define Unreachable() Unreachable (Cody::Location (__FILE__, __LINE__)) |
92 | #endif |
93 | |
94 | // Do we have __VA_OPT__, alas no specific feature macro for it :( |
95 | // From stack overflow |
96 | // https://stackoverflow.com/questions/48045470/portably-detect-va-opt-support |
97 | // Relies on having variadic macros, but they're a C++11 thing, so |
98 | // we're good |
99 | #define HAVE_ARG_3(a,b,c,...) c |
100 | #define HAVE_VA_OPT_(...) HAVE_ARG_3(__VA_OPT__(,),true,false,) |
101 | #define HAVE_VA_OPT HAVE_VA_OPT_(?) |
102 | |
103 | // Oh, for lazily evaluated function parameters |
104 | #if HAVE_VA_OPT |
105 | // Assert is variadic, so you can write Assert (TPL<A,B>(C)) without |
106 | // extraneous parens. I don't think we need that though. |
107 | #define Assert(EXPR, ...) \ |
108 | (__builtin_expect (bool (EXPR __VA_OPT__ (, __VA_ARGS__)), true) \ |
109 | ? (void)0 : AssertFailed ()) |
110 | #else |
111 | // If you don't have the GNU ,##__VA_ARGS__ pasting extension, we'll |
112 | // need another fallback |
113 | #define Assert(EXPR, ...) \ |
114 | (__builtin_expect (bool (EXPR, ##__VA_ARGS__), true) \ |
115 | ? (void)0 : AssertFailed ()) |
116 | #endif |
117 | #else |
118 | // Not asserting, use EXPR in an unevaluated context |
119 | #if HAVE_VA_OPT |
120 | #define Assert(EXPR, ...) \ |
121 | ((void)sizeof (bool (EXPR __VA_OPT__ (, __VA_ARGS__))), (void)0) |
122 | #else |
123 | #define Assert(EXPR, ...) \ |
124 | ((void)sizeof (bool (EXPR, ##__VA_ARGS__)), (void)0) |
125 | #endif |
126 | |
127 | inline void Unreachable () noexcept |
128 | { |
129 | __builtin_unreachable (); |
130 | } |
131 | #endif |
132 | |
133 | } |
134 | |