ZeroErr
Loading...
Searching...
No Matches
assert.h
Go to the documentation of this file.
1#pragma once
2
4
5#include "zeroerr/format.h"
8#include "zeroerr/print.h"
9
10#include <cstdint>
11#include <exception>
12#include <iostream>
13
15
16
17// This macro will be redefined in the log.h header, so the global context variable could be
18// envolved only when we use log.h at the same time. If you didn't use log.h, this is still a
19// header-only library.
20#ifndef ZEROERR_G_CONTEXT_SCOPE
21#define ZEROERR_G_CONTEXT_SCOPE(x)
22#endif
23
24
34#ifndef ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER
35#define ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER(cond, level, ...) \
36 do { \
37 if (cond) { \
38 switch (zeroerr::assert_level::ZEROERR_CAT(level, _l)) { \
39 case zeroerr::assert_level::ZEROERR_WARN_l: \
40 std::cerr << zeroerr::FgYellow << "WARN" << zeroerr::Reset; \
41 break; \
42 case zeroerr::assert_level::ZEROERR_ERROR_l: \
43 std::cerr << zeroerr::FgRed << "ERROR" << zeroerr::Reset; \
44 break; \
45 case zeroerr::assert_level::ZEROERR_FATAL_l: \
46 std::cerr << zeroerr::FgMagenta << "FATAL" << zeroerr::Reset; \
47 break; \
48 } \
49 std::cerr << zeroerr::format(__VA_ARGS__) << std::endl; \
50 } \
51 } while (0)
52#endif
53
54#ifdef ZEROERR_OS_WINDOWS
55#define ZEROERR_PRINT_ASSERT(cond, level, pattern, ...) \
56 ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER(cond, level, " Assertion Failed:\n{msg}" pattern, \
57 assertion_data.log(), __VA_ARGS__)
58#else
59ZEROERR_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wgnu-zero-variadic-macro-arguments")
60#define ZEROERR_PRINT_ASSERT(cond, level, pattern, ...) \
61 ZEROERR_PRINT_ASSERT_DEFAULT_PRINTER(cond, level, " Assertion Failed:\n{msg}" pattern, \
62 assertion_data.log(), ##__VA_ARGS__)
64#endif
65
66#define ZEROERR_ASSERT_EXP(cond, level, expect_throw, is_false, ...) \
67 ZEROERR_FUNC_SCOPE_BEGIN { \
68 zeroerr::assert_info info{zeroerr::assert_level::ZEROERR_CAT(level, _l), \
69 zeroerr::assert_throw::expect_throw, is_false}; \
70 \
71 zeroerr::AssertionData assertion_data(__FILE__, __LINE__, #cond, info); \
72 try { \
73 assertion_data.setResult(zeroerr::ExpressionDecomposer() << cond); \
74 } catch (const std::exception& e) { \
75 assertion_data.setException(e); \
76 } \
77 zeroerr::detail::context_helper< \
78 decltype(_ZEROERR_TEST_CONTEXT), \
79 std::is_same<decltype(_ZEROERR_TEST_CONTEXT), \
80 const bool>::value>::setContext(assertion_data, _ZEROERR_TEST_CONTEXT); \
81 ZEROERR_PRINT_ASSERT(assertion_data.passed == false, level, __VA_ARGS__); \
82 if (false) debug_break(); \
83 assertion_data(); \
84 ZEROERR_FUNC_SCOPE_RET(assertion_data.passed); \
85 } \
86 ZEROERR_FUNC_SCOPE_END
87
88
89#define ZEROERR_ASSERT_CMP(lhs, op, rhs, level, expect_throw, is_false, ...) \
90 ZEROERR_FUNC_SCOPE_BEGIN { \
91 zeroerr::assert_info info{zeroerr::assert_level::ZEROERR_CAT(level, _l), \
92 zeroerr::assert_throw::expect_throw, is_false}; \
93 \
94 zeroerr::Printer print; \
95 print.isQuoted = false; \
96 zeroerr::AssertionData assertion_data(__FILE__, __LINE__, #lhs " " #op " " #rhs, info); \
97 try { \
98 assertion_data.setResult({(lhs)op(rhs), print(lhs, #op, rhs)}); \
99 } catch (const std::exception& e) { \
100 assertion_data.setException(e); \
101 } \
102 zeroerr::detail::context_helper< \
103 decltype(_ZEROERR_TEST_CONTEXT), \
104 std::is_same<decltype(_ZEROERR_TEST_CONTEXT), \
105 const bool>::value>::setContext(assertion_data, _ZEROERR_TEST_CONTEXT); \
106 ZEROERR_PRINT_ASSERT(assertion_data.passed == false, level, __VA_ARGS__); \
107 if (false) debug_break(); \
108 assertion_data(); \
109 ZEROERR_FUNC_SCOPE_RET(assertion_data.passed); \
110 } \
111 ZEROERR_FUNC_SCOPE_END
112
113
114#ifdef ZEROERR_NO_ASSERT
115
116#define CHECK(...)
117#define CHECK_NOT(...)
118#define CHECK_THROWS(...)
119#define REQUIRE(...)
120#define REQUIRE_NOT(...)
121#define REQUIRE_THROWS(...)
122#define ASSERT(...)
123#define ASSERT_NOT(...)
124#define ASSERT_THROWS(...)
125
126#define CHECK_EQ(...)
127#define CHECK_NE(...)
128#define CHECK_LT(...)
129#define CHECK_LE(...)
130#define CHECK_GT(...)
131#define CHECK_GE(...)
132
133#define REQUIRE_EQ(...)
134#define REQUIRE_NE(...)
135#define REQUIRE_LT(...)
136#define REQUIRE_LE(...)
137#define REQUIRE_GT(...)
138#define REQUIRE_GE(...)
139
140#define ASSERT_EQ(...)
141#define ASSERT_NE(...)
142#define ASSERT_LT(...)
143#define ASSERT_LE(...)
144#define ASSERT_GT(...)
145#define ASSERT_GE(...)
146
147#else
148// clang-format off
149ZEROERR_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wgnu-zero-variadic-macro-arguments")
150
151#define ZEROERR_CHECK(cond, ...) ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_WARN, no_throw, false, __VA_ARGS__))
152#define ZEROERR_CHECK_NOT(cond, ...) ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_WARN, no_throw, true, __VA_ARGS__))
153#define ZEROERR_CHECK_THROWS(cond, ...) ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_WARN, throws, false, __VA_ARGS__))
154#define ZEROERR_REQUIRE(cond, ...) ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_ERROR, no_throw, false, __VA_ARGS__))
155#define ZEROERR_REQUIRE_NOT(cond, ...) ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_ERROR, no_throw, true, __VA_ARGS__))
156#define ZEROERR_REQUIRE_THROWS(cond, ...) ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_ERROR, throws, false, __VA_ARGS__))
157#define ZEROERR_ASSERT(cond, ...) ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_FATAL, no_throw, false, __VA_ARGS__))
158#define ZEROERR_ASSERT_NOT(cond, ...) ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_FATAL, no_throw, true, __VA_ARGS__))
159#define ZEROERR_ASSERT_THROWS(cond, ...) ZEROERR_EXPAND(ZEROERR_ASSERT_EXP(cond, ZEROERR_FATAL, throws, false, __VA_ARGS__))
160
161#define CHECK(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
162#define CHECK_NOT(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_NOT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
163#define CHECK_THROWS(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_THROWS(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
164#define REQUIRE(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
165#define REQUIRE_NOT(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_NOT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
166#define REQUIRE_THROWS(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_THROWS(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
167#define ASSERT(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
168#define ASSERT_NOT(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_NOT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
169#define ASSERT_THROWS(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_THROWS(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
170
171#define ZEROERR_CHECK_EQ(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, ==, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)
172#define ZEROERR_CHECK_NE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, !=, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)
173#define ZEROERR_CHECK_LT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)
174#define ZEROERR_CHECK_LE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <=, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)
175#define ZEROERR_CHECK_GT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)
176#define ZEROERR_CHECK_GE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >=, rhs, ZEROERR_WARN, no_throw, false, __VA_ARGS__)
177
178#define ZEROERR_REQUIRE_EQ(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, ==, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)
179#define ZEROERR_REQUIRE_NE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, !=, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)
180#define ZEROERR_REQUIRE_LT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)
181#define ZEROERR_REQUIRE_LE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <=, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)
182#define ZEROERR_REQUIRE_GT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)
183#define ZEROERR_REQUIRE_GE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >=, rhs, ZEROERR_ERROR, no_throw, false, __VA_ARGS__)
184
185#define ZEROERR_ASSERT_EQ(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, ==, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)
186#define ZEROERR_ASSERT_NE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, !=, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)
187#define ZEROERR_ASSERT_LT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)
188#define ZEROERR_ASSERT_LE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, <=, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)
189#define ZEROERR_ASSERT_GT(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)
190#define ZEROERR_ASSERT_GE(lhs, rhs, ...) ZEROERR_ASSERT_CMP(lhs, >=, rhs, ZEROERR_FATAL, no_throw, false, __VA_ARGS__)
191
192#define CHECK_EQ(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_EQ(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
193#define CHECK_NE(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_NE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
194#define CHECK_LT(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_LT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
195#define CHECK_LE(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_LE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
196#define CHECK_GT(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_GT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
197#define CHECK_GE(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_CHECK_GE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
198#define REQUIRE_EQ(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_EQ(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
199#define REQUIRE_NE(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_NE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
200#define REQUIRE_LT(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_LT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
201#define REQUIRE_LE(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_LE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
202#define REQUIRE_GT(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_GT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
203#define REQUIRE_GE(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_REQUIRE_GE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
204#define ASSERT_EQ(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_EQ(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
205#define ASSERT_NE(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_NE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
206#define ASSERT_LT(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_LT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
207#define ASSERT_LE(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_LE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
208#define ASSERT_GT(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_GT(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
209#define ASSERT_GE(...) ZEROERR_SUPPRESS_VARIADIC_MACRO ZEROERR_EXPAND(ZEROERR_ASSERT_GE(__VA_ARGS__)) ZEROERR_SUPPRESS_VARIADIC_MACRO_POP
210
212// clang-format on
213#endif
214
215
216// This symbol must be in the global namespace or anonymous namespace
217// used for checking the assert is inside testing or not
218namespace {
219constexpr bool _ZEROERR_TEST_CONTEXT = false;
220} // namespace
221
222
223namespace zeroerr {
224
226enum class assert_throw : uint8_t { no_throw, throws, throws_as };
227enum class assert_cmp : uint8_t { eq, ne, gt, ge, lt, le };
228
238
239
244struct AssertionData : std::exception {
245 const char* file; // file name
246 unsigned line; // line number
247 assert_info info; // assert info
248 bool passed; // if the assertion passed
249 std::string message; // the message of the assertion
250 std::string cond; // the condition of the assertion
251
252 AssertionData(const char* file, unsigned line, const char* cond, assert_info info)
253 : file(file), line(line), info(info) {
254 static std::string pattern = "zeroerr::ExpressionDecomposer() << ";
255 std::string cond_str(cond);
256 size_t pos = cond_str.find(pattern);
257 if (pos != std::string::npos) cond_str.replace(pos, pos + pattern.size(), "");
258 this->cond = cond_str;
259 }
260
261 void setResult(ExprResult&& result) {
262 ExprResult r(std::move(result));
263 if (info.is_false)
264 passed = !r.res;
265 else
266 passed = r.res;
267 message = r.decomp;
268 }
269
270 void setException(const std::exception& e) {
272 message = e.what();
273 }
274
275 std::string log() {
276 std::stringstream ss;
277 ss << " " << cond << " expands to " << message << std::endl;
278 ss << Dim << "(" << file << ":" << line << ")" << Reset << std::endl;
279 return ss.str();
280 }
281
282 // throw the exception
283 void operator()() {
284 if (passed) return;
285 if (shouldThrow()) throw *this;
286 }
287
289};
290
291namespace detail {
292// This struct is used for handle constexpr if in C++11
293// https://stackoverflow.com/questions/43587405/constexpr-if-alternative
294template <typename T, bool>
296
297template <typename T>
298struct context_helper<T, true> {
299 static void setContext(AssertionData& data, T) {
300 if (data.passed) return;
301 }
302};
303
304template <typename T>
305struct context_helper<T, false> {
306 static void setContext(AssertionData& data, T ctx) {
307 if (data.passed) {
308 ctx->passed_as++;
309 return;
310 }
311 switch (data.info.level) {
313 case assert_level::ZEROERR_ERROR_l: ctx->failed_as++; break;
314 case assert_level::ZEROERR_WARN_l: ctx->warning_as++; break;
315 }
316 }
317};
318} // namespace detail
319
320
321} // namespace zeroerr
322
#define ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
Definition config.h:265
#define ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH
Definition config.h:218
#define ZEROERR_CLANG_SUPPRESS_WARNING_WITH_PUSH(w)
Definition config.h:182
Definition assert.h:218
constexpr bool _ZEROERR_TEST_CONTEXT
Definition assert.h:219
Definition benchmark.cpp:17
const char * Dim
Definition color.cpp:36
assert_throw
Definition assert.h:226
assert_level
Definition assert.h:225
assert_cmp
Definition assert.h:227
const char * Reset
Definition color.cpp:34
AssertionData is a struct that contains all the information of an assertion. It will be thrown as an ...
Definition assert.h:244
assert_info info
Definition assert.h:247
std::string log()
Definition assert.h:275
std::string message
Definition assert.h:249
const char * file
Definition assert.h:245
void operator()()
Definition assert.h:283
void setResult(ExprResult &&result)
Definition assert.h:261
bool shouldThrow()
Definition assert.h:288
AssertionData(const char *file, unsigned line, const char *cond, assert_info info)
Definition assert.h:252
void setException(const std::exception &e)
Definition assert.h:270
bool passed
Definition assert.h:248
unsigned line
Definition assert.h:246
std::string cond
Definition assert.h:250
Definition decomposition.h:87
std::string decomp
Definition decomposition.h:89
bool res
Definition decomposition.h:88
This is a one-byte assert info struct, which is used to collect the meta info of an assertion.
Definition assert.h:233
assert_level level
Definition assert.h:234
assert_throw throw_type
Definition assert.h:235
bool is_false
Definition assert.h:236
static void setContext(AssertionData &data, T ctx)
Definition assert.h:306
static void setContext(AssertionData &data, T)
Definition assert.h:299
Definition assert.h:295