KASSERT  0.0.1
Karlsruhe Assertion Library
kassert.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 Macros for asserting runtime checks.
7
8#pragma once
9
10#include <iostream>
11#include <string_view>
12#include <type_traits>
13#include <utility>
14#include <vector>
15
19
20/// @brief Assertion levels
21namespace kassert::assert {
22/// @addtogroup assertion-levels Assertion levels
23/// @{
24
25/// @brief Assertion level for exceptions if exception mode is disabled.
26#define KASSERT_ASSERTION_LEVEL_KTHROW 10
27
28/// @brief Assertion level for exceptions if exception mode is disabled.
30
31/// @brief Default assertion level. This level is used if no assertion level is specified.
32#define KASSERT_ASSERTION_LEVEL_NORMAL 30
33
34/// @brief Default assertion level. This level is used if no assertion level is specified.
36
37/// @}
38} // namespace kassert::assert
39
40#ifndef KASSERT_ASSERTION_LEVEL
41 #warning "Assertion level was not set explicitly; using default assertion level."
42 /// @brief Default assertion level to `kassert::assert::normal` if not set explicitly.
43 #define KASSERT_ASSERTION_LEVEL KASSERT_ASSERTION_LEVEL_NORMAL
44#endif
45
46/// @brief Assertion macro. Accepts between one and three parameters.
47/// @ingroup assertion
48///
49/// Assertions are enabled or disabled by setting a compile-time assertion level (`-DKASSERT_ASSERTION_LEVEL=<int>`).
50/// For predefined assertion levels, see @ref assertion-levels.
51/// If an assertion is enabled and fails, the KASSERT() macro prints an expansion of the expression similar to Catch2.
52/// This process is described in @ref expression-expansion.
53///
54/// The macro accepts 1 to 3 parameters:
55/// 1. The assertion expression (mandatory).
56/// 2. Error message that is printed in addition to the decomposed expression (optional). The message is piped into
57/// a logger object. Thus, one can use the `<<` operator to build the error message similar to how one would use
58/// `std::cout`.
59/// 3. The level of the assertion (optional, default: `kassert::assert::normal`, see @ref assertion-levels).
60#define KASSERT(...) \
61 KASSERT_KASSERT_HPP_VARARG_HELPER_3( \
62 , \
63 __VA_ARGS__, \
64 KASSERT_3(__VA_ARGS__), \
65 KASSERT_2(__VA_ARGS__), \
66 KASSERT_1(__VA_ARGS__), \
67 ignore \
68 )
69
70/// @brief Macro for throwing exceptions. Accepts between one and three parameters.
71/// @ingroup assertion
72///
73/// Exceptions are only used in exception mode, which is enabled by using the CMake option
74/// `-DKASSERT_EXCEPTION_MODE=On`. Otherwise, the macro generates a KASSERT() with assertion level
75/// `kassert::assert::kthrow` (lowest level).
76///
77/// The macro accepts 1 to 2 parameters:
78/// 1. Expression that causes the exception to be thrown if it evaluates to \c false (mandatory).
79/// 2. Error message that is printed in addition to the decomposed expression (optional). The message is piped into
80/// a logger object. Thus, one can use the `<<` operator to build the error message similar to how one would use
81/// `std::cout`.
82#define THROWING_KASSERT(...) \
83 KASSERT_KASSERT_HPP_VARARG_HELPER_2( \
84 , \
85 __VA_ARGS__, \
86 THROWING_KASSERT_2(__VA_ARGS__), \
87 THROWING_KASSERT_1(__VA_ARGS__), \
88 ignore \
89 )
90
91/// @brief Macro for throwing custom exception.
92/// @ingroup assertion
93///
94/// The macro requires at least 2 parameters:
95/// 1. Expression that causes the exception to be thrown if it evaluates to \c false (mandatory).
96/// 2. Error message that is printed in addition to the decomposed expression (optional). The message is piped into
97/// a logger object. Thus, one can use the `<<` operator to build the error message similar to how one would use
98/// `std::cout`.
99/// 3. Type of the exception to be used. The exception type must have a ctor that takes a `std::string` as its
100/// first argument, followed by any additional parameters passed to this macro.
101/// 4, 5, 6, ... Parameters that are forwarded to the exception type's ctor.
102///
103/// Any other parameter is passed to the constructor of the exception class.
104#define THROWING_KASSERT_SPECIFIED(expression, message, exception_type, ...) \
105 KASSERT_KASSERT_HPP_THROWING_KASSERT_CUSTOM_IMPL(expression, exception_type, message, ##__VA_ARGS__)
106
107namespace kassert::internal {
108/// @brief Describes a source code location.
110 /// @brief Filename.
111 char const* file;
112 /// @brief Line number.
113 unsigned row;
114 /// @brief Function name.
115 char const* function;
116};
117
118/// @brief Builds the description for an exception.
119/// @param expression Expression that caused this exception to be thrown.
120/// @param where Source code location where the exception was thrown.
121/// @param message User message describing this exception.
122/// @return The description of this exception.
123[[maybe_unused]] inline std::string
124build_what(std::string const& expression, SourceLocation const where, std::string const& message) {
125 using namespace std::string_literals;
126 return "\n"s + where.file + ": In function '" + where.function + "':\n" + where.file + ": "
127 + std::to_string(where.row) + ": FAILED ASSERTION\n" + "\t" + expression + "\n" + message + "\n";
128}
129} // namespace kassert::internal
130
131namespace kassert {
132/// @brief The default exception type used together with \c THROWING_KASSERT. Reports the erroneous expression together
133/// with a custom error message.
134class KassertException : public std::exception {
135public:
136 /// @brief Constructs the exception
137 /// @param message A custom error message.
138 explicit KassertException(std::string message) : _what(std::move(message)) {}
139
140 /// @brief Gets a description of this exception.
141 /// @return A description of this exception.
142 [[nodiscard]] char const* what() const noexcept final {
143 return _what.c_str();
144 }
145
146private:
147 /// @brief The description of this exception.
148 std::string _what;
149};
150} // namespace kassert
151
152namespace kassert::internal {
153/// @brief Checks if a assertion of the given level is enabled. This is controlled by the CMake option
154/// \c KASSERT_ASSERTION_LEVEL.
155/// @param level The level of the assertion.
156/// @return Whether the assertion is enabled.
157constexpr bool assertion_enabled(int level) {
158 return level <= KASSERT_ASSERTION_LEVEL;
159}
160
161/// @brief Checks if a assertion of the given level is enabled. This is controlled by the CMake option
162/// \c KASSERT_ASSERTION_LEVEL. This is the macro version of assertion_enabled for use in the preprocessor.
163/// @param level The level of the assertion.
164/// @return Whether the assertion is enabled.
165#define KASSERT_ENABLED(level) level <= KASSERT_ASSERTION_LEVEL
166
167/// @brief Evaluates an assertion that could not be decomposed (i.e., expressions that use && or ||). If the assertion
168/// fails, prints an error describing the failed assertion.
169/// @param type Actual type of this check. In exception mode, this parameter has always value \c ASSERTION, otherwise
170/// it names the type of the exception that would have been thrown.
171/// @param result Assertion expression result to be checked.
172/// @param where Source code location of the assertion.
173/// @param expr_str Stringified assertion expression.
174/// @return Result of the assertion. If true, the assertion was triggered and the program should be halted.
175inline bool
176evaluate_and_print_assertion(char const* type, bool result, SourceLocation const& where, char const* expr_str) {
177 if (!result) {
178 OStreamLogger(std::cerr) << where.file << ": In function '" << where.function << "':\n"
179 << where.file << ":" << where.row << ": FAILED " << type << "\n"
180 << "\t" << expr_str << "\n";
181 }
182 return result;
183}
184
185/// @brief Evaluates an assertion expression. If the assertion fails, prints an error describing the failed assertion.
186/// @param type Actual type of this check. In exception mode, this parameter has always value \c ASSERTION, otherwise
187/// it names the type of the exception that would have been thrown.
188/// @param expr Assertion expression to be checked.
189/// @param where Source code location of the assertion.
190/// @param expr_str Stringified assertion expression.
191/// @return Result of the assertion. If true, the assertion was triggered and the program should be halted.
192inline bool
193evaluate_and_print_assertion(char const* type, Expression&& expr, SourceLocation const& where, char const* expr_str) {
194 if (!expr.result()) {
195 OStreamLogger(std::cerr) << where.file << ": In function '" << where.function << "':\n"
196 << where.file << ":" << where.row << ": FAILED " << type << "\n"
197 << "\t" << expr_str << "\n"
198 << "with expansion:\n"
199 << "\t" << expr << "\n";
200 }
201 return expr.result();
202}
203} // namespace kassert::internal
Provides macros to implement the KASSERT, THROWING_KASSERT and THROWING_KASSERT_SPECIFIED macros.
The default exception type used together with THROWING_KASSERT. Reports the erroneous expression toge...
Definition: kassert.hpp:134
KassertException(std::string message)
Constructs the exception.
Definition: kassert.hpp:138
char const * what() const noexcept final
Gets a description of this exception.
Definition: kassert.hpp:142
Interface for decomposed unary and binary expressions.
Definition: expression_decomposition.hpp:55
Expression decomposition.
constexpr int kthrow
Assertion level for exceptions if exception mode is disabled.
Definition: kassert.hpp:29
#define KASSERT_ASSERTION_LEVEL_KTHROW
Assertion level for exceptions if exception mode is disabled.
Definition: kassert.hpp:26
#define KASSERT_ASSERTION_LEVEL_NORMAL
Default assertion level. This level is used if no assertion level is specified.
Definition: kassert.hpp:32
constexpr int normal
Default assertion level. This level is used if no assertion level is specified.
Definition: kassert.hpp:35
Logger< std::ostream & > OStreamLogger
Logger writing all output to a std::ostream. This specialization is used to generate the KASSERT erro...
Definition: logger.hpp:113
std::string build_what(std::string const &expression, SourceLocation const where, std::string const &message)
Builds the description for an exception.
Definition: kassert.hpp:124
bool evaluate_and_print_assertion(char const *type, bool result, SourceLocation const &where, char const *expr_str)
Evaluates an assertion that could not be decomposed (i.e., expressions that use && or ||)....
Definition: kassert.hpp:176
constexpr bool assertion_enabled(int level)
Checks if a assertion of the given level is enabled. This is controlled by the CMake option KASSERT_A...
Definition: kassert.hpp:157
#define KASSERT_ASSERTION_LEVEL
Default assertion level to kassert::assert::normal if not set explicitly.
Definition: kassert.hpp:43
Logger utility class to build error messages for failed assertins.
Assertion levels.
Definition: kassert.hpp:21
Describes a source code location.
Definition: kassert.hpp:109
char const * function
Function name.
Definition: kassert.hpp:115
unsigned row
Line number.
Definition: kassert.hpp:113
char const * file
Filename.
Definition: kassert.hpp:111