Helios Engine 0.1.0
A modular ECS based data-oriented C++23 game engine
 
Loading...
Searching...
No Matches
assert.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <cstdio>
6#include <cstdlib>
7#include <format>
8#include <source_location>
9#include <string>
10#include <string_view>
11
12#if defined(__cpp_lib_print) && (__cpp_lib_print >= 202302L)
13#include <print>
14#endif
15
16namespace helios {
17
18namespace details {
19
20/**
21 * @brief Bridge to logger-provided assertion logging.
22 * @details The logger implementation provides an inline function named
23 * `LogAssertionFailureViaLogger` in `helios::details`.
24 * We forward-declare it here so `assert.hpp` can use logger integration when available.
25 *
26 * If the logger is present in the build, that inline function will be used.
27 * If not, calls to `LogAssertionFailure` below will safely fall back to
28 * printing to stderr (via std::println) and aborting.
29 *
30 * @param condition The failed condition as a string
31 * @param loc Source location of the assertion
32 * @param message Additional message to log
33 */
34void LogAssertionFailureViaLogger(std::string_view condition, const std::source_location& loc,
35 std::string_view message) noexcept;
36
37/**
38 * @brief Unified assertion logging function used by macros below.
39 * @details Tries to forward the assertion to the logger integration function above.
40 *
41 * If the logger isn't available (or calling it throws), this function
42 * simply returns so the caller may perform a fallback print-and-abort.
43 *
44 * @param condition The failed condition as a string
45 * @param loc Source location of the assertion
46 * @param message Additional message to log
47 */
48inline void LogAssertionFailure(std::string_view condition, const std::source_location& loc,
49 std::string_view message) noexcept {
50 try {
51 // Attempt to forward to the logger integration.
52 LogAssertionFailureViaLogger(condition, loc, message);
53 } catch (...) {
54 // Ignore any exception and allow caller to fallback to printing.
55 }
56}
57
58/**
59 * @brief Assertion handler that logs via the logger system.
60 * @param condition The failed condition as a string
61 * @param loc Source location of the assertion
62 * @param message Additional message to log
63 */
64inline void AssertionFailed(std::string_view condition, const std::source_location& loc,
65 std::string_view message) noexcept {
66 // Log via logger (will use stderr fallback if logger is not linked)
67 try {
68 LogAssertionFailure(condition, loc, message);
69 } catch (...) {
70 // If logger fails, print to stderr as last resort
71#if defined(__cpp_lib_print) && (__cpp_lib_print >= 202302L)
72 if (!message.empty()) {
73 std::println(stderr, "Assertion failed: {} | {} [{}:{}]", condition, message, loc.file_name(), loc.line());
74 } else {
75 std::println(stderr, "Assertion failed: {}\nFile: {}\nLine: {}\nFunction: {}", condition, loc.file_name(),
76 loc.line(), loc.function_name());
77 }
78#else
79 if (!message.empty()) {
80 std::fprintf(stderr, "Assertion failed: %.*s | %.*s [%s:%u]\n", static_cast<int>(condition.size()),
81 condition.data(), static_cast<int>(message.size()), message.data(), loc.file_name(), loc.line());
82 } else {
83 std::fprintf(stderr, "Assertion failed: %.*s\nFile: %s\nLine: %u\nFunction: %s\n",
84 static_cast<int>(condition.size()), condition.data(), loc.file_name(), loc.line(),
85 loc.function_name());
86 }
87#endif
88 }
89}
90
91#ifdef HELIOS_ENABLE_ASSERTS
92inline constexpr bool kEnableAssert = true;
93#else
94inline constexpr bool kEnableAssert = false;
95#endif
96
97} // namespace details
98
99/**
100 * @brief Prints a message with stack trace and aborts the program execution.
101 * @details Useful for placing in dead code branches or unreachable states.
102 * @param message The message to print before aborting
103 */
104void AbortWithStacktrace(std::string_view message) noexcept;
105
106} // namespace helios
107
108// Assert macros that work independently of logger but use it by default
109
110/**
111 * @brief Assertion macro that aborts execution in debug builds.
112 * @details Does nothing in release builds.
113 * Attempts to use logger if available, falls back to printing via std::println if not.
114 * Supports format strings and arguments.
115 * @param condition The condition to check
116 * @param ... Optional message (can be format string with arguments)
117 * @hideinitializer
118 */
119#ifdef HELIOS_ENABLE_ASSERTS
120#define HELIOS_ASSERT(condition, ...) \
121 do { \
122 if constexpr (::helios::details::kEnableAssert) { \
123 if (HELIOS_EXPECT_FALSE(!(condition))) [[unlikely]] { \
124 constexpr auto loc = std::source_location::current(); \
125 if constexpr (sizeof(#__VA_ARGS__) > 1) { \
126 try { \
127 const std::string msg = std::format("" __VA_ARGS__); \
128 ::helios::details::AssertionFailed(#condition, loc, msg); \
129 } catch (...) { \
130 ::helios::details::AssertionFailed(#condition, loc, "Formatting error in assertion"); \
131 } \
132 } else { \
133 ::helios::details::AssertionFailed(#condition, loc, ""); \
134 } \
135 HELIOS_DEBUG_BREAK(); \
136 } \
137 } \
138 } while (false)
139#else
140#define HELIOS_ASSERT(condition, ...) [[maybe_unused]] static constexpr auto HELIOS_ANONYMOUS_VAR(unused_assert) = 0
141#endif
142
143/**
144 * @brief Invariant check that asserts in debug builds and logs error in release.
145 * @details Provides runtime safety checks that are enforced even in release builds.
146 * In debug builds, triggers assertion. In release builds, logs error and continues.
147 * @param condition The condition to check
148 * @param ... Optional message (can be format string with arguments)
149 * @hideinitializer
150 */
151#ifdef HELIOS_ENABLE_ASSERTS
152#define HELIOS_INVARIANT(condition, ...) \
153 do { \
154 if (HELIOS_EXPECT_FALSE(!(condition))) [[unlikely]] { \
155 constexpr auto loc = std::source_location::current(); \
156 if constexpr (sizeof(#__VA_ARGS__) > 1) { \
157 try { \
158 const std::string msg = std::format("" __VA_ARGS__); \
159 ::helios::details::AssertionFailed(#condition, loc, msg); \
160 } catch (...) { \
161 ::helios::details::AssertionFailed(#condition, loc, "Formatting error in invariant"); \
162 } \
163 } else { \
164 ::helios::details::AssertionFailed(#condition, loc, ""); \
165 } \
166 HELIOS_DEBUG_BREAK(); \
167 } \
168 } while (false)
169#else
170#define HELIOS_INVARIANT(condition, ...) \
171 do { \
172 if (HELIOS_EXPECT_FALSE(!(condition))) [[unlikely]] { \
173 constexpr auto loc = std::source_location::current(); \
174 if constexpr (sizeof(#__VA_ARGS__) > 1) { \
175 try { \
176 const std::string msg = std::format("" __VA_ARGS__); \
177 ::helios::details::LogAssertionFailure(#condition, loc, msg); \
178 } catch (...) { \
179 ::helios::details::LogAssertionFailure(#condition, loc, ""); \
180 } \
181 } else { \
182 ::helios::details::LogAssertionFailure(#condition, loc, ""); \
183 } \
184 } \
185 } while (false)
186#endif
187
188/**
189 * @brief Verify macro that always checks the condition.
190 * @details Similar to assert but runs in both debug and release builds.
191 * Useful for validating external input or critical invariants.
192 * @param condition The condition to check
193 * @param ... Optional message (can be format string with arguments)
194 * @hideinitializer
195 */
196#define HELIOS_VERIFY(condition, ...) \
197 do { \
198 if (HELIOS_EXPECT_FALSE(!(condition))) [[unlikely]] { \
199 constexpr auto loc = std::source_location::current(); \
200 if constexpr (sizeof(#__VA_ARGS__) > 1) { \
201 try { \
202 const std::string msg = std::format("" __VA_ARGS__); \
203 ::helios::details::AssertionFailed(#condition, loc, msg); \
204 } catch (...) { \
205 ::helios::details::AssertionFailed(#condition, loc, "Formatting error in verify"); \
206 } \
207 } else { \
208 ::helios::details::AssertionFailed(#condition, loc, ""); \
209 } \
210 HELIOS_DEBUG_BREAK(); \
211 } \
212 } while (false)
213
214/**
215 * @brief Verify macro with logger name that always checks the condition.
216 * @details Similar to HELIOS_VERIFY but allows specifying a custom logger.
217 * @note The custom-logger variant delegates to the same assertion handling;
218 * the logger selection is performed by the logger integration function.
219 * @param logger_name The name of the logger to use
220 * @param condition The condition to check
221 * @param ... Optional message (can be format string with arguments)
222 * @hideinitializer
223 */
224#define HELIOS_VERIFY_LOGGER(logger_name, condition, ...) \
225 do { \
226 if (HELIOS_EXPECT_FALSE(!(condition))) [[unlikely]] { \
227 constexpr auto loc = std::source_location::current(); \
228 if constexpr (sizeof(#__VA_ARGS__) > 1) { \
229 try { \
230 const std::string msg = std::format("" __VA_ARGS__); \
231 ::helios::details::AssertionFailed(#condition, loc, msg); \
232 } catch (...) { \
233 ::helios::details::AssertionFailed(#condition, loc, "Formatting error in verify"); \
234 } \
235 } else { \
236 ::helios::details::AssertionFailed(#condition, loc, ""); \
237 } \
238 HELIOS_DEBUG_BREAK(); \
239 } \
240 } while (false)
void AssertionFailed(std::string_view condition, const std::source_location &loc, std::string_view message) noexcept
Assertion handler that logs via the logger system.
Definition assert.hpp:64
void LogAssertionFailure(std::string_view condition, const std::source_location &loc, std::string_view message) noexcept
Unified assertion logging function used by macros below.
Definition assert.hpp:48
void LogAssertionFailureViaLogger(std::string_view condition, const std::source_location &loc, std::string_view message) noexcept
Bridge to logger-provided assertion logging.
Definition logger.hpp:649
constexpr bool kEnableAssert
Definition assert.hpp:94
void AbortWithStacktrace(std::string_view message) noexcept
Prints a message with stack trace and aborts the program execution.
Definition assert.cpp:89