C++ Utilities
Loading...
Searching...
No Matches
assert.h
Go to the documentation of this file.
1#pragma once
2// SPDX-FileCopyrightText: 2025 Nessan Fitzmaurice <nzznfitz+gh@icloud.com>
3// SPDX-License-Identifier: MIT
4
9
10#include <format>
11#include <print>
12#include <string>
13#include <source_location>
14
15// Avoid conflicts with other assert macros -- in particular, the ones defined in the standard library.
16#ifdef assert
17 #undef assert
18#endif
19#ifdef assert_eq
20 #undef assert_eq
21#endif
22#ifdef debug_assert
23 #undef debug_assert
24#endif
25#ifdef debug_assert_eq
26 #undef debug_assert_eq
27#endif
28#ifdef always_assert
29 #undef always_assert
30#endif
31#ifdef always_assert_eq
32 #undef always_assert_eq
33#endif
34
42#define always_assert(cond, ...) \
43 if (!(cond)) [[unlikely]] { failed(#cond, std::source_location::current() __VA_OPT__(, __VA_ARGS__)); }
44
52#define always_assert_eq(lhs, rhs, ...) \
53 if (!((lhs) == (rhs))) [[unlikely]] { \
54 failed_eq(#lhs, #rhs, (lhs), (rhs), std::source_location::current() __VA_OPT__(, __VA_ARGS__)); \
55 }
56
66#ifdef DEBUG
67 #define debug_assert(cond, ...) always_assert(cond __VA_OPT__(, __VA_ARGS__))
68#else
69 #define debug_assert(cond, ...) void(0)
70#endif
71
81#ifdef DEBUG
82 #define debug_assert_eq(lhs, rhs, ...) always_assert_eq(lhs, rhs __VA_OPT__(, __VA_ARGS__))
83#else
84 #define debug_assert_eq(a, b, ...) void(0)
85#endif
86
96#ifdef NDEBUG
97 #define assert(cond, ...) void(0)
98#else
99 #define assert(cond, ...) always_assert(cond __VA_OPT__(, __VA_ARGS__))
100#endif
101
111#ifdef NDEBUG
112 #define assert_eq(lhs, rhs, ...) void(0)
113#else
114 #define assert_eq(lhs, rhs, ...) always_assert_eq(lhs, rhs __VA_OPT__(, __VA_ARGS__))
115#endif
116
117// ---------------------------------------------------------------------------------------------------------------------
118// Functions to handle assertion failures in an anonymous namespace
119// ---------------------------------------------------------------------------------------------------------------------
120namespace {
121
122// Given a path like `/home/jj/dev/project/src/foo.cpp` this returns its "basename" `foo.cpp`
123constexpr std::string
124basename(std::string_view path)
125{
126 const auto pos = path.find_last_of("/\\\\");
127 if (pos == std::string_view::npos) { return std::string(path); }
128 return std::string(path.substr(pos + 1));
129}
130
131// Prints an error message with source code location information and optionally exits the program.
132// This handles simple boolean assert `assert(cond, ...)` failures.
133void
134failed(std::string_view cond_str, std::source_location loc, std::string_view msg_fmt = "", auto... msg_args)
135{
136 std::println(stderr);
137 std::println(stderr, "FAILED `assert({})` [{}:{}]", cond_str, basename(loc.file_name()), loc.line());
138 if (!msg_fmt.empty()) {
139 std::vprint_nonunicode(stderr, msg_fmt, std::make_format_args(msg_args...));
140 std::println(stderr);
141 }
142 std::println(stderr);
143 ::exit(1);
144}
145
148void
149failed_eq(std::string_view lhs_str, std::string_view rhs_str, auto lhs, auto rhs, std::source_location loc,
150 std::string_view msg_fmt = "", auto... msg_args)
151{
152 std::println(stderr);
153 std::println(stderr, "FAILED `assert_eq({}, {})` [{}:{}]", lhs_str, rhs_str, basename(loc.file_name()), loc.line());
154 if (!msg_fmt.empty()) {
155 std::vprint_nonunicode(stderr, msg_fmt, std::make_format_args(msg_args...));
156 std::println(stderr);
157 }
158 std::println(stderr, "lhs = {}", lhs);
159 std::println(stderr, "rhs = {}", rhs);
160 std::println(stderr);
161 ::exit(1);
162}
163
164} // namespace