C++ Utilities
Loading...
Searching...
No Matches
confirm.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 confirm macros -- in particular, the ones defined in the standard library.
16#ifdef confirm
17 #undef confirm
18#endif
19#ifdef confirm_eq
20 #undef confirm_eq
21#endif
22#ifdef debug_confirm
23 #undef debug_confirm
24#endif
25#ifdef debug_confirm_eq
26 #undef debug_confirm_eq
27#endif
28#ifdef always_confirm
29 #undef always_confirm
30#endif
31#ifdef always_confirm_eq
32 #undef always_confirm_eq
33#endif
34
42#define always_confirm(cond, ...) \
43 if (!(cond)) [[unlikely]] { failed(#cond, std::source_location::current() __VA_OPT__(, __VA_ARGS__)); }
44
52#define always_confirm_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_confirm(cond, ...) always_confirm(cond __VA_OPT__(, __VA_ARGS__))
68#else
69 #define debug_confirm(cond, ...) void(0)
70#endif
71
81#ifdef DEBUG
82 #define debug_confirm_eq(lhs, rhs, ...) always_confirm_eq(lhs, rhs __VA_OPT__(, __VA_ARGS__))
83#else
84 #define debug_confirm_eq(a, b, ...) void(0)
85#endif
86
96#ifdef NDEBUG
97 #define confirm(cond, ...) void(0)
98#else
99 #define confirm(cond, ...) always_confirm(cond __VA_OPT__(, __VA_ARGS__))
100#endif
101
111#ifdef NDEBUG
112 #define confirm_eq(lhs, rhs, ...) void(0)
113#else
114 #define confirm_eq(lhs, rhs, ...) always_confirm_eq(lhs, rhs __VA_OPT__(, __VA_ARGS__))
115#endif
116
117// ---------------------------------------------------------------------------------------------------------------------
118// Functions to handle confirmation 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 confirm `confirm(cond, ...)` failures.
133constexpr void
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 `confirm({})` [{}:{}]", 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
148constexpr void
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 `confirm_eq({}, {})` [{}:{}]", lhs_str, rhs_str, basename(loc.file_name()),
154 loc.line());
155 if (!msg_fmt.empty()) {
156 std::vprint_nonunicode(stderr, msg_fmt, std::make_format_args(msg_args...));
157 std::println(stderr);
158 }
159 std::println(stderr, "lhs = {}", lhs);
160 std::println(stderr, "rhs = {}", rhs);
161 std::println(stderr);
162 ::exit(1);
163}
164
165} // namespace