MCRO
C++23 utilities for Unreal Engine.
Loading...
Searching...
No Matches
AssertMacros.h
Go to the documentation of this file.
1/** @noop License Comment
2 * @file
3 * @copyright
4 * This Source Code is subject to the terms of the Mozilla Public License, v2.0.
5 * If a copy of the MPL was not distributed with this file You can obtain one at
6 * https://mozilla.org/MPL/2.0/
7 *
8 * @author David Mórász
9 * @date 2025
10 */
11
12#pragma once
13
14/**
15 * @file
16 * By default `ASSERT_QUIT|CRASH` macros are also active in Shipping builds. This was a personal preference and
17 * that's why you can turn it off if you define `MCRO_ASSERT_IGNORE_SHIPPING`.
18 *
19 * @remarks
20 * Yes I know that "shipping builds must have all debug info and checks removed" but I have yet to see one project in
21 * my career where Shipping builds didn't have weird issues or crashes not present in other debug builds, and debugging
22 * them removed many years from my expected life-span.
23 */
24
25#include "CoreMinimal.h"
26#include "Mcro/Error.h"
27#include "Mcro/Macros.h"
28#include "Logging/LogMacros.h"
29
30/** @brief Do not use this namespace directly use `ASSERT_QUIT|CRASH` macros instead */
32{
33 using namespace Mcro::Error;
34
35 namespace Detail
36 {
37 MCRO_API void SubmitError(
38 EErrorSeverity severity,
39 FString const& codeContext,
40 bool async, bool important,
41 TUniqueFunction<void(IErrorRef const&)>&& extraSetup,
42 std::source_location const& location = std::source_location::current()
43 );
44 MCRO_API bool IsRunningPIE();
45 MCRO_API void StopPie();
46 }
47}
48
49#define MCRO_ASSERT_SUBMIT_ERROR(condition, severity, async, important, ...) \
50 Mcro::AssertMacros::Detail::SubmitError( \
51 Mcro::Error::EErrorSeverity::severity, \
52 PREPROCESSOR_TO_TEXT(condition), \
53 async, important, \
54 [&](Mcro::Error::IErrorRef const& error) { (error __VA_ARGS__); } \
55 ); //
56
57#define MCRO_ASSERT_CRASH_METHOD \
58 UE_LOG(LogTemp, Fatal, \
59 TEXT_"Program cannot continue for the reasons above. (at " \
60 PREPROCESSOR_TO_TEXT(__FILE__:__LINE__) TEXT_")" \
61 ) //
62
63#define MCRO_CRASH_BODY(condition, ...) \
64 MCRO_ASSERT_SUBMIT_ERROR( \
65 condition, Crashing, false, false, \
66 __VA_ARGS__ \
67 ) \
68 MCRO_ASSERT_CRASH_METHOD; //
69
70#define MCRO_QUIT_BODY(condition, returnOnFailure, ...) \
71 MCRO_ASSERT_SUBMIT_ERROR( \
72 condition, Fatal, \
73 Mcro::AssertMacros::Detail::IsRunningPIE(), \
74 Mcro::AssertMacros::Detail::IsRunningPIE(), \
75 __VA_ARGS__ \
76 ) \
77 if (Mcro::AssertMacros::Detail::IsRunningPIE()) \
78 { \
79 Mcro::AssertMacros::Detail::StopPie(); \
80 return returnOnFailure; \
81 } \
82 else { MCRO_ASSERT_CRASH_METHOD } //
83
84#define MCRO_ASSERT_CRASH_COMMON(condition, ...) \
85 if (UNLIKELY(!(condition))) \
86 { \
87 MCRO_CRASH_BODY(condition, __VA_ARGS__) \
88 } //
89
90#define MCRO_ASSERT_QUIT_COMMON(condition, returnOnFailure, ...) \
91 if (UNLIKELY(!(condition))) \
92 { \
93 MCRO_QUIT_BODY(condition, returnOnFailure, __VA_ARGS__) \
94 } //
95
96#if WITH_EDITOR
97
98/**
99 * @brief
100 * Use this instead of `check` macro if the checked expression shouldn't be ignored in shipping builds. This version
101 * will also crash the entire editor even during a PIE session. Use this only if there's absolutely no way to recover
102 * from an invalid state, like a situation when early return is not possible / doesn't make sense (e.g.: return a
103 * checked pointer by reference).
104 *
105 * This macro will attempt to display the error before crashing via the IError type. You can also append extra
106 * information to it, or even override preset information like so:
107 * @code
108 * ASSERT_CRASH(arrayNum > 0,
109 * ->WithDetails(TEXT_"Did you forget to specify data?")
110 * ->WithAppendix(TEXT_"Data", data.ToString())
111 * )
112 * @endcode
113 */
114#define ASSERT_CRASH(condition, ...) MCRO_ASSERT_CRASH_COMMON(condition, __VA_ARGS__)
115
116/** @brief This is equivalent to ASSERT_CRASH but if a code path reaches this macro it will always crash */
117#define FORCE_CRASH(...) MCRO_CRASH_BODY(Invalid code path, __VA_ARGS__)
118
119/**
120 * @brief
121 * Use this instead of `check` macro if the checked expression shouldn't be ignored in shipping builds. This version
122 * will not crash the editor but will display an error, stops the PIE, and should return the calling function.
123 *
124 * A return value should be provided on failure, or leave it empty to return void. so either
125 * @code
126 * ASSERT_QUIT(SomethingImportantButNullable, ) // important to leave an extra comma for void functions
127 * // or return default
128 * ASSERT_QUIT(SomethingImportantButNullable, {})
129 * // or return whatever
130 * ASSERT_QUIT(SomethingImportantButNullable, myReturnValue)
131 * @endcode
132 *
133 * This is done in the hopes that these checks failing in PIE sessions doesn't crash the entire editor giving the
134 * developer a chance for fixing the error before restarting. However, early returns might cause
135 * undefined/vaguely-defined side effects and may lead to uninitialized resources or in worst case scenario may lead to
136 * incorrect states in resource cleanups which then might crash the editor. Always check the validity of your resources in destructors.
137 * Failing this check outside of PIE or in a Standalone session will still crash with a `check` macro.
138 *
139 * This macro will attempt to display the error before quitting via the IError type. You can also append extra
140 * information to it like so, or even override preset information like so:
141 * @code
142 * ASSERT_QUIT(arrayNum > 0, myReturnValue,
143 * ->WithDetails(TEXT_"Did you forget to specify data?")
144 * ->WithAppendix(TEXT_"Data", data.ToString())
145 * )
146 * @endcode
147 */
148#define ASSERT_QUIT(condition, returnOnFailure, ...) MCRO_ASSERT_QUIT_COMMON(condition, returnOnFailure, __VA_ARGS__)
149
150/** @brief This is equivalent to ASSERT_QUIT but if a code path reaches this macro it will always quit */
151#define FORCE_QUIT(returnOnFailure, ...) MCRO_QUIT_BODY(Invalid code path, returnOnFailure, __VA_ARGS__)
152
153#elif UE_BUILD_SHIPPING && defined(MCRO_ASSERT_IGNORE_SHIPPING)
154
155#define ASSERT_CRASH(condition, ...)
156#define ASSERT_QUIT(condition, returnOnFailure, ...)
157#define FORCE_CRASH(...)
158
159#else
160
161#define ASSERT_CRASH(condition, ...) MCRO_ASSERT_CRASH_COMMON(condition, __VA_ARGS__)
162#define ASSERT_QUIT(condition, returnOnFailure, ...) MCRO_ASSERT_CRASH_COMMON(condition, __VA_ARGS__)
163#define FORCE_CRASH(...) MCRO_CRASH_BODY(Invalid code path, __VA_ARGS__)
164
165#endif
166
167/** @brief Shorthand of `ASSERT_QUIT` for TMaybe monads */
168#define ASSERT_QUIT_NON_ERROR(maybe, returnOnFailure, ...) ASSERT_QUIT(maybe, returnOnFailure, ->WithError(maybe.GetErrorRef()) __VA_ARGS__)
169
170/** @brief Shorthand of `ASSERT_CRASH` for TMaybe monads */
171#define ASSERT_CRASH_NON_ERROR(maybe, ...) ASSERT_CRASH(maybe, ->WithError(maybe.GetErrorRef()) __VA_ARGS__)
MCRO_API void SubmitError(EErrorSeverity severity, FString const &codeContext, bool async, bool important, TUniqueFunction< void(IErrorRef const &)> &&extraSetup, std::source_location const &location=std::source_location::current())
MCRO_API bool IsRunningPIE()
Do not use this namespace directly use ASSERT_QUIT|CRASH macros instead.
Contains utilities for structured error handling.
Definition Error.Fwd.h:19
TSharedRef< IError > IErrorRef
Convenience alias for an instance of an error.
Definition Error.Fwd.h:25
EErrorSeverity
Indicate the severity of an error and at what discretion the caller may treat it.
Definition Error.Fwd.h:53