KASSERT
0.0.1
Karlsruhe Assertion Library
assertion_macros.hpp
Go to the documentation of this file.
1
// This file is part of KAssert.
2
//
3
// Copyright 2021-2022 The KAssert Authors
4
5
/// @file
6
/// @brief Provides macros to implement the KASSERT, THROWING_KASSERT and THROWING_KASSERT_SPECIFIED macros.
7
8
#pragma once
9
10
/// @cond IMPLEMENTATION
11
12
// To decompose expressions, the KASSERT_KASSERT_HPP_ASSERT_IMPL() produces code such as
13
//
14
// Decomposer{} <= a == b [ with implicit parentheses: ((Decomposer{} <= a) == b) ]
15
//
16
// This triggers a warning with -Wparentheses, suggesting to set explicit parentheses, which is impossible in this
17
// situation. Thus, we use compiler-specific _Pragmas to suppress these warning.
18
// Note that warning suppression in GCC does not work if the KASSERT() call is passed through >= two macro calls:
19
//
20
// #define A(stmt) B(stmt)
21
// #define B(stmt) stmt;
22
// A(KASSERT(1 != 1)); -- warning suppression does not work
23
//
24
// This is a known limitation of the current implementation.
25
#if defined(__GNUC__) && !defined(__clang__)
// GCC
26
#define KASSERT_KASSERT_HPP_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push"
)
27
#define KASSERT_KASSERT_HPP_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop"
)
28
#define KASSERT_KASSERT_HPP_DIAGNOSTIC_IGNORE_PARENTHESES _Pragma("GCC diagnostic ignored \"-Wparentheses\""
)
29
#elif defined(__clang__)
// Clang
30
#define KASSERT_KASSERT_HPP_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push"
)
31
#define KASSERT_KASSERT_HPP_DIAGNOSTIC_POP _Pragma("clang diagnostic pop"
)
32
#define KASSERT_KASSERT_HPP_DIAGNOSTIC_IGNORE_PARENTHESES _Pragma("clang diagnostic ignored \"-Wparentheses\""
)
33
#else
// Other compilers -> no supression supported
34
#define KASSERT_KASSERT_HPP_DIAGNOSTIC_PUSH
35
#define KASSERT_KASSERT_HPP_DIAGNOSTIC_POP
36
#define KASSERT_KASSERT_HPP_DIAGNOSTIC_IGNORE_PARENTHESES
37
#endif
38
39
// This is the actual implementation of the KASSERT() macro.
40
//
41
// - Note that expanding the macro into a `do { ... } while(false)` pseudo-loop is a common trick to make a macro
42
// "act like a statement". Otherwise, it would have surprising effects if the macro is used inside a `if` branch
43
// without braces.
44
// - If the assertion level is disabled, this should not generate any code (assuming that the compiler removes the
45
// dead loop).
46
// - `evaluate_and_print_assertion` evaluates the assertion and prints an error message if it failed.
47
// - The call to `std::abort()` is not wrapped in a function to keep the stack trace clean.
48
#define KASSERT_KASSERT_HPP_KASSERT_IMPL(type, expression, message, level) \
49
do { \
50
if constexpr (kassert::internal::assertion_enabled(level)) { \
51
KASSERT_KASSERT_HPP_DIAGNOSTIC_PUSH \
52
KASSERT_KASSERT_HPP_DIAGNOSTIC_IGNORE_PARENTHESES \
53
if (!kassert::internal::evaluate_and_print_assertion( \
54
type, \
55
kassert::internal::finalize_expr(kassert::internal::Decomposer{} <= expression), \
56
KASSERT_KASSERT_HPP_SOURCE_LOCATION, \
57
#expression \
58
)) { \
59
kassert::Logger<std::ostream&>(std::cerr) << message << "\n"
; \
60
std::abort(); \
61
} \
62
KASSERT_KASSERT_HPP_DIAGNOSTIC_POP \
63
} \
64
} while (false)
65
66
// Expands a macro depending on its number of arguments. For instance,
67
//
68
// #define FOO(...) KASSERT_KASSERT_HPP_VARARG_HELPER_3(, __VA_ARGS__, IMPL3, IMPL2, IMPL1, dummy)
69
//
70
// expands to IMPL3 with 3 arguments, IMPL2 with 2 arguments and IMPL1 with 1 argument.
71
// To do this, the macro always expands to its 5th argument. Depending on the number of parameters, __VA_ARGS__
72
// pushes the right implementation to the 5th parameter.
73
#define KASSERT_KASSERT_HPP_VARARG_HELPER_3(X, Y, Z, W, FUNC, ...) FUNC
74
#define KASSERT_KASSERT_HPP_VARARG_HELPER_2(X, Y, Z, FUNC, ...) FUNC
75
76
// KASSERT() chooses the right implementation depending on its number of arguments.
77
#define KASSERT_3(expression, message, level) KASSERT_KASSERT_HPP_KASSERT_IMPL("ASSERTION"
, expression, message, level)
78
#define KASSERT_2(expression, message) KASSERT_3(expression, message, kassert::assert::normal)
79
#define KASSERT_1(expression) KASSERT_2(expression, ""
)
80
81
// Implementation of the THROWING_KASSERT() macro.
82
// In KASSERT_EXCEPTION_MODE, we throw an exception similar to the implementation of KASSERT(), although expression
83
// decomposition in exceptions is currently unsupported. Otherwise, the macro delegates to KASSERT().
84
#ifdef KASSERT_EXCEPTION_MODE
85
#define KASSERT_KASSERT_HPP_THROWING_KASSERT_IMPL_INTERNAL(expression, exception_type, message, ...) \
86
do { \
87
if (!(expression)) { \
88
throw exception_type(message, ##__VA_ARGS__); \
89
} \
90
} while (false)
91
#else
92
#define KASSERT_KASSERT_HPP_THROWING_KASSERT_IMPL_INTERNAL(expression, exception_type, message, ...) \
93
do { \
94
if constexpr (kassert::internal::assertion_enabled(kassert::assert::kthrow)) { \
95
if (!(expression)) { \
96
kassert::Logger<std::ostream&>(std::cerr) \
97
<< (exception_type(message, ##__VA_ARGS__).what()) << "\n"
; \
98
std::abort(); \
99
} \
100
} \
101
} while (false)
102
#endif
103
104
#define KASSERT_KASSERT_HPP_THROWING_KASSERT_IMPL(expression, message) \
105
KASSERT_KASSERT_HPP_THROWING_KASSERT_IMPL_INTERNAL( \
106
expression, \
107
kassert::KassertException, \
108
kassert::internal::build_what( \
109
#expression, \
110
KASSERT_KASSERT_HPP_SOURCE_LOCATION, \
111
(kassert::internal::RrefOStringstreamLogger{std::ostringstream{}} << message).stream().str() \
112
) \
113
)
114
115
#define KASSERT_KASSERT_HPP_THROWING_KASSERT_CUSTOM_IMPL(expression, exception_type, message, ...) \
116
KASSERT_KASSERT_HPP_THROWING_KASSERT_IMPL_INTERNAL( \
117
expression, \
118
exception_type, \
119
kassert::internal::build_what( \
120
#expression, \
121
KASSERT_KASSERT_HPP_SOURCE_LOCATION, \
122
(kassert::internal::RrefOStringstreamLogger{std::ostringstream{}} << message).stream().str() \
123
), \
124
##__VA_ARGS__ \
125
)
126
127
// THROWING_KASSERT() chooses the right implementation depending on its number of arguments.
128
#define THROWING_KASSERT_2(expression, message) KASSERT_KASSERT_HPP_THROWING_KASSERT_IMPL(expression, message)
129
#define THROWING_KASSERT_1(expression) THROWING_KASSERT_2(expression, ""
)
130
131
// __PRETTY_FUNCTION__ is a compiler extension supported by GCC and clang that prints more information than __func__
132
#if defined(__GNUC__) || defined(__clang__)
133
#define KASSERT_KASSERT_HPP_FUNCTION_NAME __PRETTY_FUNCTION__
134
#else
135
#define KASSERT_KASSERT_HPP_FUNCTION_NAME __func__
136
#endif
137
138
// Represents the static location in the source code.
139
#define KASSERT_KASSERT_HPP_SOURCE_LOCATION \
140
kassert::internal::SourceLocation { \
141
__FILE__, __LINE__, KASSERT_KASSERT_HPP_FUNCTION_NAME \
142
}
143
144
/// @endcond
include
kassert
internal
assertion_macros.hpp
Generated by
1.9.4