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/** 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 );
43 MCRO_API bool IsRunningPIE();
44 MCRO_API void StopPie();
45 }
46}
47
48#define MCRO_ASSERT_SUBMIT_ERROR(condition, severity, async, important, ...) \
49 Mcro::AssertMacros::Detail::SubmitError( \
50 Mcro::Error::EErrorSeverity::severity, \
51 PREPROCESSOR_TO_TEXT(condition), \
52 async, important, \
53 [&](Mcro::Error::IErrorRef const& error) { (error __VA_ARGS__); } \
54 ); //
55
56#define MCRO_ASSERT_CRASH_METHOD \
57 UE_LOG(LogTemp, Fatal, \
58 TEXT("Program cannot continue for the reasons above. (at ") \
59 PREPROCESSOR_TO_TEXT(__FILE__:__LINE__) TEXT(")") \
60 ) //
61
62#define MCRO_CRASH_BODY(condition, ...) \
63 MCRO_ASSERT_SUBMIT_ERROR( \
64 condition, Crashing, false, false, \
65 __VA_ARGS__ \
66 ) \
67 MCRO_ASSERT_CRASH_METHOD; //
68
69#define MCRO_QUIT_BODY(condition, returnOnFailure, ...) \
70 MCRO_ASSERT_SUBMIT_ERROR( \
71 condition, Fatal, \
72 Mcro::AssertMacros::Detail::IsRunningPIE(), \
73 Mcro::AssertMacros::Detail::IsRunningPIE(), \
74 __VA_ARGS__ \
75 ) \
76 if (Mcro::AssertMacros::Detail::IsRunningPIE()) \
77 { \
78 Mcro::AssertMacros::Detail::StopPie(); \
79 return returnOnFailure; \
80 } \
81 else { MCRO_ASSERT_CRASH_METHOD } //
82
83#define MCRO_ASSERT_CRASH_COMMON(condition, ...) \
84 if (UNLIKELY(!(condition))) \
85 { \
86 MCRO_CRASH_BODY(condition, __VA_ARGS__) \
87 } //
88
89#define MCRO_ASSERT_QUIT_COMMON(condition, returnOnFailure, ...) \
90 if (UNLIKELY(!(condition))) \
91 { \
92 MCRO_QUIT_BODY(condition, returnOnFailure, __VA_ARGS__) \
93 } //
94
95#if WITH_EDITOR
96
97/**
98 * Use this instead of `check` macro if the checked expression shouldn't be ignored in shipping builds. This version will
99 * also crash the entire editor even during a PIE session. Use this only if there's absolutely no way to recover from
100 * an invalid state, like a situation when early return is not possible / doesn't make sense (e.g.: return a checked
101 * pointer by reference).
102 *
103 * This macro will attempt to display the error before crashing via the IError type. You can also append extra
104 * information to it, or even override preset information like so:
105 *
106 * @code
107 * ASSERT_CRASH(arrayNum > 0,
108 * ->WithDetails(TEXT("Did you forget to specify data?"))
109 * ->WithAppendix(TEXT("Data"), data.ToString())
110 * )
111 * @endcode
112 */
113#define ASSERT_CRASH(condition, ...) MCRO_ASSERT_CRASH_COMMON(condition, __VA_ARGS__)
114
115/** This is equivalent to ASSERT_CRASH but if a code path reaches this macro it will always crash */
116#define FORCE_CRASH(...) MCRO_CRASH_BODY(Invalid code path, __VA_ARGS__)
117
118/**
119 * Use this instead of `check` macro if the checked expression shouldn't be ignored in shipping builds. This version will
120 * not crash the editor but will display an error, stops the PIE, and should return the calling function. A return value
121 * should be provided on failure, or leave it empty to return void. so either
122 *
123 * @code
124 * ASSERT_QUIT(SomethingImportantButNullable, ) // important to leave an extra comma for void functions
125 * // or return default
126 * ASSERT_QUIT(SomethingImportantButNullable, {})
127 * // or return whatever
128 * ASSERT_QUIT(SomethingImportantButNullable, myReturnValue)
129 * @endcode
130 *
131 * This is done in the hopes that these checks failing in PIE sessions doesn't crash the entire editor giving the
132 * developer a chance for fixing the error before restarting. However early returns might cause undefined/vaguely-defined
133 * side effects and may lead to uninitialized resources or in worst case scenario may lead to incorrect states in
134 * resource cleanups which then might crash the editor. Always check the validity of your resources in destructors.
135 * Failing this check outside of PIE or in a Standalone session will still crash with a `check` macro.
136 *
137 * This macro will attempt to display the error before quitting via the IError type. You can also append extra
138 * information to it like so, or even override preset information like so:
139 *
140 * @code
141 * ASSERT_QUIT(arrayNum > 0, myReturnValue,
142 * ->WithDetails(TEXT("Did you forget to specify data?"))
143 * ->WithAppendix(TEXT("Data"), data.ToString())
144 * )
145 * @endcode
146 */
147#define ASSERT_QUIT(condition, returnOnFailure, ...) MCRO_ASSERT_QUIT_COMMON(condition, returnOnFailure, __VA_ARGS__)
148
149/** This is equivalent to ASSERT_QUIT but if a code path reaches this macro it will always quit */
150#define FORCE_QUIT(returnOnFailure, ...) MCRO_QUIT_BODY(Invalid code path, returnOnFailure, __VA_ARGS__)
151
152#elif UE_BUILD_SHIPPING && defined(MCRO_ASSERT_IGNORE_SHIPPING)
153
154#define ASSERT_CRASH(condition, ...)
155#define ASSERT_QUIT(condition, returnOnFailure, ...)
156
157#else
158
159#define ASSERT_CRASH(condition, ...) MCRO_ASSERT_CRASH_COMMON(condition, __VA_ARGS__)
160#define ASSERT_QUIT(condition, returnOnFailure, ...) MCRO_ASSERT_CRASH_COMMON(condition, __VA_ARGS__)
161
162#endif
MCRO_API bool IsRunningPIE()
MCRO_API void SubmitError(EErrorSeverity severity, FString const &codeContext, bool async, bool important, TUniqueFunction< void(IErrorRef const &)> &&extraSetup)
TSharedRef< IError > IErrorRef
Definition Error.Fwd.h:25