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