qxLib
assert.h
Go to the documentation of this file.
1 /**
2 
3  @file assert.h
4  @author Khrapov
5  @date 29.10.2020
6  @copyright © Nick Khrapov, 2021. All right reserved.
7 
8 **/
9 #pragma once
10 
11 #include <qx/category.h>
12 #include <qx/logger/logger.h>
13 
14 #include <exception>
15 
16 #if QX_MSVC
17  #define _QX_DEBUG_BREAK __debugbreak()
18 #elif QX_CLANG
19  #define _QX_DEBUG_BREAK __builtin_debugtrap()
20 #elif QX_GNU
21  #include <signal.h>
22  #define _QX_DEBUG_BREAK raise(SIGTRAP)
23 #else
24  #define _QX_DEBUG_BREAK QX_EMPTY_MACRO
25 #endif
26 
27 namespace qx::details
28 {
29 
30 template<verbosity eVerbosity>
31 void resolve_assert_proceeding(
32  // macro args
33  const category& fileCategory,
34  string_view svFunction,
35  string_view svFile,
36  int nLine,
37  string_view svCondition)
38 {
39  QX_LOGGER_INSTANCE.log(eVerbosity, QX_TEXT("[{}] "), fileCategory, svFile, svFunction, nLine, svCondition);
40  QX_LOGGER_INSTANCE.flush();
41 }
42 
43 template<verbosity eVerbosity>
44 void resolve_assert_proceeding(
45  // macro args
46  const category& fileCategory,
47  string_view svFunction,
48  string_view svFile,
49  int nLine,
50  string_view svCondition,
51  // ... args
52  const category& category)
53 {
54  string sMessage;
55  sMessage.append_format(QX_TEXT("[{}] "), svCondition);
56  QX_LOGGER_INSTANCE.log(eVerbosity, sMessage, category, svFile, svFunction, nLine);
57  QX_LOGGER_INSTANCE.flush();
58 }
59 
60 template<verbosity eVerbosity, class... args_t>
61  requires log_acceptable_args<args_t...>
62 void resolve_assert_proceeding(
63  // macro args
64  const category& fileCategory,
65  string_view svFunction,
66  string_view svFile,
67  int nLine,
68  string_view svCondition,
69  // ... args
70  format_string_strong_checks<args_t...> sFormat,
71  args_t&&... args)
72 {
73  string sMessage;
74  sMessage.append_format(QX_TEXT("[{}] "), svCondition);
75  sMessage.append_format(sFormat, std::forward<args_t>(args)...);
76  QX_LOGGER_INSTANCE.log(eVerbosity, sMessage, fileCategory, svFile, svFunction, nLine);
77  QX_LOGGER_INSTANCE.flush();
78 }
79 
80 template<verbosity eVerbosity>
81 void resolve_assert_proceeding(
82  // macro args
83  const category& fileCategory,
84  string_view svFunction,
85  string_view svFile,
86  int nLine,
87  string_view svCondition,
88  // ... args
89  string_view svMessage)
90 {
91  string sMessage;
92  sMessage.append_format(QX_TEXT("[{}] {}"), svCondition, svMessage);
93  QX_LOGGER_INSTANCE.log(eVerbosity, sMessage, fileCategory, svFile, svFunction, nLine);
94  QX_LOGGER_INSTANCE.flush();
95 }
96 
97 template<verbosity eVerbosity, class... args_t>
98  requires log_acceptable_args<args_t...>
99 void resolve_assert_proceeding(
100  // macro args
101  const category& fileCategory,
102  string_view svFunction,
103  string_view svFile,
104  int nLine,
105  string_view svCondition,
106  // ... args
107  const category& category,
108  format_string_strong_checks<args_t...> sFormat,
109  args_t&&... args)
110 {
111  string sMessage;
112  sMessage.append_format(QX_TEXT("[{}] "), svCondition);
113  sMessage.append_format(sFormat, std::forward<args_t>(args)...);
114  QX_LOGGER_INSTANCE.log(eVerbosity, sMessage, category, svFile, svFunction, nLine);
115  QX_LOGGER_INSTANCE.flush();
116 }
117 
118 template<verbosity eVerbosity>
119 void resolve_assert_proceeding(
120  // macro args
121  const category& fileCategory,
122  string_view svFunction,
123  string_view svFile,
124  int nLine,
125  string_view svCondition,
126  // ... args
127  const category& category,
128  string_view svMessage)
129 {
130  string sMessage;
131  sMessage.append_format(QX_TEXT("[{}] {}"), svCondition, svMessage);
132  QX_LOGGER_INSTANCE.log(eVerbosity, sMessage, category, svFile, svFunction, nLine);
133  QX_LOGGER_INSTANCE.flush();
134 }
135 
136 } // namespace qx::details
137 
138 // ----------------------------------- setup -----------------------------------
139 
140 #ifndef QX_DEBUG_BREAK
141  #define QX_DEBUG_BREAK _QX_DEBUG_BREAK
142 #endif
143 
144 #ifndef QX_EXPECT_BEFORE_DEBUG_BREAK
145  #define QX_EXPECT_BEFORE_DEBUG_BREAK(condition, ...) \
146  qx::details::resolve_assert_proceeding<qx::verbosity::error>( \
147  QX_FILE_CATEGORY(), \
148  qx::to_string(__FUNCTION__), \
149  QX_SHORT_FILE, \
150  QX_LINE, \
151  QX_TEXT(#condition), \
152  ##__VA_ARGS__)
153 #endif
154 
155 #ifndef QX_EXPECT_DEBUG_BREAK
156  #if QX_DEBUG || QX_REL_WITH_DEBUG_INFO
157  #define QX_EXPECT_DEBUG_BREAK QX_DEBUG_BREAK
158  #else
159  #define QX_EXPECT_DEBUG_BREAK QX_EMPTY_MACRO
160  #endif
161 #endif
162 
163 #ifndef QX_EXPECT_AFTER_DEBUG_BREAK
164  #define QX_EXPECT_AFTER_DEBUG_BREAK(condition, ...) QX_EMPTY_MACRO
165 #endif
166 
167 #ifndef QX_ASSERT_BEFORE_DEBUG_BREAK
168  #define QX_ASSERT_BEFORE_DEBUG_BREAK(condition, ...) \
169  qx::details::resolve_assert_proceeding<qx::verbosity::critical>( \
170  QX_FILE_CATEGORY(), \
171  qx::to_string(__FUNCTION__), \
172  QX_SHORT_FILE, \
173  QX_LINE, \
174  QX_TEXT(#condition), \
175  ##__VA_ARGS__)
176 #endif
177 
178 #ifndef QX_ASSERT_DEBUG_BREAK
179  #if QX_DEBUG || QX_REL_WITH_DEBUG_INFO
180  #define QX_ASSERT_DEBUG_BREAK QX_DEBUG_BREAK
181  #else
182  #define QX_ASSERT_DEBUG_BREAK QX_EMPTY_MACRO
183  #endif
184 #endif
185 
186 #ifndef QX_ASSERT_AFTER_DEBUG_BREAK
187  #define QX_ASSERT_AFTER_DEBUG_BREAK(condition, ...) std::terminate()
188 #endif
189 
190 // ------------------------------- common macros -------------------------------
191 
192 #define _QX_ASSERT(before_debug_break, debug_break, after_debug_break, condition, ...) \
193  [&]() \
194  { \
195  QX_PUSH_SUPPRESS_MSVC_WARNINGS(4702); \
196  if (!(condition)) [[unlikely]] \
197  { \
198  before_debug_break(condition, ##__VA_ARGS__); \
199  debug_break; \
200  after_debug_break(condition, ##__VA_ARGS__); \
201  return false; \
202  } \
203  else \
204  { \
205  return true; \
206  } \
207  QX_POP_SUPPRESS_WARNINGS(); \
208  }()
209 
210 #define _QX_ASSERT_NO_ENTRY(before_debug_break, debug_break, after_debug_break, ...) \
211  _QX_ASSERT(before_debug_break, debug_break, after_debug_break, !QX_TEXT("No entry"), ##__VA_ARGS__)
212 
213 #define _QX_ASSERT_NOT_IMPLEMENTED(before_debug_break, debug_break, after_debug_break, ...) \
214  _QX_ASSERT(before_debug_break, debug_break, after_debug_break, !QX_TEXT("Not implemented"), ##__VA_ARGS__)
215 
216 #define _QX_ASSERT_CONTINUE(before_debug_break, debug_break, after_debug_break, condition, ...) \
217  if (!_QX_ASSERT(before_debug_break, debug_break, after_debug_break, condition, ##__VA_ARGS__)) [[unlikely]] \
218  continue; \
219  else \
220  QX_EMPTY_MACRO
221 
222 #define _QX_ASSERT_BREAK(before_debug_break, debug_break, after_debug_break, condition, ...) \
223  if (!_QX_ASSERT(before_debug_break, debug_break, after_debug_break, condition, ##__VA_ARGS__)) [[unlikely]] \
224  break; \
225  else \
226  QX_EMPTY_MACRO
227 
228 #define _QX_ASSERT_RETURN( \
229  before_debug_break, \
230  debug_break, \
231  after_debug_break, \
232  return_keyword, \
233  condition, \
234  return_value, \
235  ...) \
236  if (!_QX_ASSERT(before_debug_break, debug_break, after_debug_break, condition, ##__VA_ARGS__)) [[unlikely]] \
237  return_keyword return_value; \
238  else \
239  QX_EMPTY_MACRO
240 
241 // -------------------------------- user macros --------------------------------
242 
243 /**
244  @brief Verifies that condition is true
245  @details ASSERT macros generate fatal failures and abort the program execution
246  @param condition - condition to check. if false, assert fails
247  @param ... - "category + format string + format arguments"
248  or "category + format string"
249  or "format string + format arguments"
250  or "format string"
251  or "category"
252 **/
253 #define QX_ASSERT(condition, ...) \
254  _QX_ASSERT( \
255  QX_ASSERT_BEFORE_DEBUG_BREAK, \
256  QX_ASSERT_DEBUG_BREAK, \
257  QX_ASSERT_AFTER_DEBUG_BREAK, \
258  condition, \
259  ##__VA_ARGS__)
260 
261 /**
262  @brief Fails unconditionally if this code should not be executed
263  @details ASSERT macros generate fatal failures and abort the program execution
264  @param ... - nothing or "category"
265 **/
266 #define QX_ASSERT_NO_ENTRY(...) \
267  _QX_ASSERT_NO_ENTRY(QX_ASSERT_BEFORE_DEBUG_BREAK, QX_ASSERT_DEBUG_BREAK, QX_ASSERT_AFTER_DEBUG_BREAK, ##__VA_ARGS__)
268 
269 /**
270  @brief Fails unconditionally if this code should not be executed with "Not implemented" message
271  @details ASSERT macros generate fatal failures and abort the program execution
272  @param ... - nothing or "category"
273 **/
274 #define QX_ASSERT_NOT_IMPLEMENTED(...) \
275  _QX_ASSERT_NOT_IMPLEMENTED( \
276  QX_ASSERT_BEFORE_DEBUG_BREAK, \
277  QX_ASSERT_DEBUG_BREAK, \
278  QX_ASSERT_AFTER_DEBUG_BREAK, \
279  ##__VA_ARGS__)
280 
281 /**
282  @brief Verifies that condition is true
283  @details EXPECT macros generate nonfatal failures and allow to continue running
284  @param condition - condition to check. if false, assert fails
285  @param ... - "category + format string + format arguments"
286  or "category + format string"
287  or "format string + format arguments"
288  or "format string"
289  or "category"
290 **/
291 #define QX_EXPECT(condition, ...) \
292  _QX_ASSERT( \
293  QX_EXPECT_BEFORE_DEBUG_BREAK, \
294  QX_EXPECT_DEBUG_BREAK, \
295  QX_EXPECT_AFTER_DEBUG_BREAK, \
296  condition, \
297  ##__VA_ARGS__)
298 
299 /**
300  @brief Fails unconditionally if this code should not be executed
301  @details EXPECT macros generate nonfatal failures and allow to continue running
302  @param ... - nothing or "category"
303 **/
304 #define QX_EXPECT_NO_ENTRY(...) \
305  _QX_ASSERT_NO_ENTRY(QX_EXPECT_BEFORE_DEBUG_BREAK, QX_EXPECT_DEBUG_BREAK, QX_EXPECT_AFTER_DEBUG_BREAK, ##__VA_ARGS__)
306 
307 /**
308  @brief Fails unconditionally if this code should not be executed with "Not implemented" message
309  @details EXPECT macros generate nonfatal failures and allow to continue running
310  @param ... - nothing or "category"
311 **/
312 #define QX_EXPECT_NOT_IMPLEMENTED(...) \
313  _QX_ASSERT_NOT_IMPLEMENTED( \
314  QX_EXPECT_BEFORE_DEBUG_BREAK, \
315  QX_EXPECT_DEBUG_BREAK, \
316  QX_EXPECT_AFTER_DEBUG_BREAK, \
317  ##__VA_ARGS__)
318 
319 /**
320  @brief Verifies that condition is true and continues loop if false
321  @details EXPECT macros generate nonfatal failures and allow to continue running
322  @param condition - condition to check. if false, assert fails
323  @param ... - "category + format string + format arguments"
324  or "category + format string"
325  or "format string + format arguments"
326  or "format string"
327  or "category"
328 **/
329 #define QX_EXPECT_CONTINUE(condition, ...) \
330  _QX_ASSERT_CONTINUE( \
331  QX_EXPECT_BEFORE_DEBUG_BREAK, \
332  QX_EXPECT_DEBUG_BREAK, \
333  QX_EXPECT_AFTER_DEBUG_BREAK, \
334  condition, \
335  ##__VA_ARGS__)
336 
337 /**
338  @brief Verifies that condition is true and break loop if false
339  @details EXPECT macros generate nonfatal failures and allow to continue running
340  @param condition - condition to check. if false, assert fails
341  @param ... - "category + format string + format arguments"
342  or "category + format string"
343  or "format string + format arguments"
344  or "format string"
345  or "category"
346 **/
347 #define QX_EXPECT_BREAK(condition, ...) \
348  _QX_ASSERT_BREAK( \
349  QX_EXPECT_BEFORE_DEBUG_BREAK, \
350  QX_EXPECT_DEBUG_BREAK, \
351  QX_EXPECT_AFTER_DEBUG_BREAK, \
352  condition, \
353  ##__VA_ARGS__)
354 
355 /**
356  @brief Verifies that condition is true and "return return_value;" if false
357  @details EXPECT macros generate nonfatal failures and allow to continue running
358  @param condition - condition to check. if false, assert fails
359  @param return_value - return value. use empty argument, if return type is void
360  @param ... - "category + format string + format arguments"
361  or "category + format string"
362  or "format string + format arguments"
363  or "format string"
364  or "category"
365 **/
366 #define QX_EXPECT_RETURN(condition, return_value, ...) \
367  _QX_ASSERT_RETURN( \
368  QX_EXPECT_BEFORE_DEBUG_BREAK, \
369  QX_EXPECT_DEBUG_BREAK, \
370  QX_EXPECT_AFTER_DEBUG_BREAK, \
371  return, \
372  condition, \
373  return_value, \
374  ##__VA_ARGS__)
375 
376 /**
377  @brief Verifies that condition is true and "return;" if false
378  @details EXPECT macros generate nonfatal failures and allow to continue running
379  @param condition - condition to check. if false, assert fails
380  @param ... - "category + format string + format arguments"
381  or "category + format string"
382  or "format string + format arguments"
383  or "format string"
384  or "category"
385 **/
386 #define QX_EXPECT_RETURN_VOID(condition, ...) \
387  _QX_ASSERT_RETURN( \
388  QX_EXPECT_BEFORE_DEBUG_BREAK, \
389  QX_EXPECT_DEBUG_BREAK, \
390  QX_EXPECT_AFTER_DEBUG_BREAK, \
391  return, \
392  condition, \
393  , \
394  ##__VA_ARGS__)
395 
396 /**
397  @brief Verifies that condition is true and "co_return return_value;" if false
398  @details EXPECT macros generate nonfatal failures and allow to continue running
399  @param condition - condition to check. if false, assert fails
400  @param return_value - return value. use empty argument, if return type is void
401  @param ... - "category + format string + format arguments"
402  or "category + format string"
403  or "format string + format arguments"
404  or "format string"
405  or "category"
406 **/
407 #define QX_EXPECT_CO_RETURN(condition, return_value, ...) \
408  _QX_ASSERT_RETURN( \
409  QX_EXPECT_BEFORE_DEBUG_BREAK, \
410  QX_EXPECT_DEBUG_BREAK, \
411  QX_EXPECT_AFTER_DEBUG_BREAK, \
412  co_return, \
413  condition, \
414  return_value, \
415  ##__VA_ARGS__)
416 
417 /**
418  @brief Verifies that condition is true and "co_return;" if false
419  @details EXPECT macros generate nonfatal failures and allow to continue running
420  @param condition - condition to check. if false, assert fails
421  @param ... - "category + format string + format arguments"
422  or "category + format string"
423  or "format string + format arguments"
424  or "format string"
425  or "category"
426 **/
427 #define QX_EXPECT_CO_RETURN_VOID(condition, ...) \
428  _QX_ASSERT_RETURN( \
429  QX_EXPECT_BEFORE_DEBUG_BREAK, \
430  QX_EXPECT_DEBUG_BREAK, \
431  QX_EXPECT_AFTER_DEBUG_BREAK, \
432  co_return, \
433  condition, \
434  , \
435  ##__VA_ARGS__)
436 
437 namespace qx::details
438 {
439 
440 inline bool hit_once(bool& bHit)
441 {
442  const bool bReturn = bHit;
443  bHit = true;
444  return bReturn;
445 }
446 
447 } // namespace qx::details
448 
449 /**
450  @def QX_PREDICATE_HIT_ONCE
451  @brief Predicate to add to a condition in any EXPECT macro. When added, a macro will only hit once.
452  @note It must be after the actual condition.
453  @code
454  QX_EXPECT((a > b || b == 0) || QX_PREDICATE_HIT_ONCE());
455  @endcode
456 **/
457 #define QX_PREDICATE_HIT_ONCE() \
458  []() \
459  { \
460  static bool h = false; \
461  return qx::details::hit_once(h); \
462  }()