Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
Assertions.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include <cassert>
7#include <cstdlib>
8
11#include "Stroika/Foundation/Debug/Debugger.h"
13
14#include "Assertions.h"
15
16#if qStroika_Foundation_Common_Platform_POSIX
17#include <cstdio>
18#endif
19
20using namespace Stroika::Foundation;
22using namespace Stroika::Foundation::Debug;
23
25
26#if qStroika_Foundation_Debug_AssertionsChecked
27
28namespace {
29 void DefaultAssertionHandler_ (const wchar_t* assertCategory, const wchar_t* assertionText, const wchar_t* fileName, int lineNum,
30 const wchar_t* functionName) noexcept
31 {
32 try {
33 String msg = "{} ({}) failed in '{}'; {}:{}"_f(assertCategory == nullptr ? L"Unknown assertion" : assertCategory,
34 assertionText == nullptr ? L"" : assertionText, functionName == nullptr ? L"" : functionName,
35 fileName == nullptr ? L"" : fileName, lineNum);
36 DbgTrace ("{}"_f, msg);
37#if qStroika_Foundation_Common_Platform_POSIX
38 fprintf (stderr, "%s\n", msg.AsNarrowSDKString (AllowMissingCharacterErrorsFlag::eIgnoreErrors).c_str ());
39#endif
40#if qStroika_Foundation_Debug_DefaultTracingOn
41 {
42 wstring tmp{Debug::BackTrace::Capture ()};
43 if (not tmp.empty ()) {
44 DbgTrace ("BackTrace: {}"_f, tmp);
45 }
46 }
47#endif
48 DropIntoDebuggerIfPresent ();
49 DbgTrace ("ABORTING..."_f);
50#if qStroika_Foundation_Common_Platform_POSIX
51 fprintf (stderr, "ABORTING...\n");
52#endif
53 }
54 catch (...) {
55 }
56 abort (); // if we ever get that far...
57 }
58 void DefaultWeakAssertionHandler_ (const wchar_t* assertCategory, const wchar_t* assertionText, const wchar_t* fileName, int lineNum,
59 const wchar_t* functionName) noexcept
60 {
61 String msg = "{} ({}) failed in '{}'; {}:{}"_f(assertCategory == nullptr ? L"Unknown weak assertion" : assertCategory,
62 assertionText == nullptr ? L"" : assertionText, functionName == nullptr ? L"" : functionName,
63 fileName == nullptr ? L"" : fileName, lineNum);
64 DbgTrace ("{}"_f, msg);
65#if qStroika_Foundation_Common_Platform_POSIX
66 fprintf (stderr, "%s\n", msg.AsNarrowSDKString (AllowMissingCharacterErrorsFlag::eIgnoreErrors).c_str ());
67#endif
68#if qStroika_Foundation_Debug_DefaultTracingOn
69 {
70 wstring tmp{Debug::BackTrace::Capture ()};
71 if (not tmp.empty ()) {
72 DbgTrace ("BackTrace: {}"_f, tmp);
73 }
74 }
75#endif
76 }
77}
78
79namespace {
80 atomic<AssertionHandlerType> sAssertFailureHandler_{DefaultAssertionHandler_};
81 atomic<AssertionHandlerType> sWeakAssertFailureHandler_{DefaultWeakAssertionHandler_};
82}
83
84/*
85 ********************************************************************************
86 **************************** Debug::GetAssertionHandler ************************
87 ********************************************************************************
88 */
90{
91 return sAssertFailureHandler_;
92}
93
94/*
95 ********************************************************************************
96 ************************** Debug::GetDefaultAssertionHandler *******************
97 ********************************************************************************
98 */
99AssertionHandlerType Stroika::Foundation::Debug::GetDefaultAssertionHandler ()
100{
101 return DefaultAssertionHandler_;
102}
103
104/*
105 ********************************************************************************
106 ***************************** Debug::SetAssertionHandler ***********************
107 ********************************************************************************
108 */
110{
111 sAssertFailureHandler_ = (assertionHandler == nullptr) ? DefaultAssertionHandler_ : assertionHandler;
112}
113
114namespace {
115 void (*sLegacyHandlerDelegate2_) (const char* assertCategory, const char* assertionText, const char* fileName, int lineNum,
116 const char* functionName) noexcept;
117}
118void Stroika::Foundation::Debug::SetAssertionHandler (void (*legacyHandler) (const char* assertCategory, const char* assertionText,
119 const char* fileName, int lineNum, const char* functionName) noexcept)
120{
121 sLegacyHandlerDelegate2_ = legacyHandler;
122 auto wrapper = [] (const wchar_t* assertCategory, const wchar_t* assertionText, const wchar_t* fileName, int lineNum,
123 const wchar_t* functionName) noexcept {
124 try {
125 string narrowAssertCategory = String{assertCategory}.AsNarrowSDKString ();
126 string narrowAssert = String{assertionText}.AsNarrowSDKString ();
127 string narrowFile = String{fileName}.AsNarrowSDKString ();
128 string narrowFunctionName = String{functionName}.AsNarrowSDKString ();
129 sLegacyHandlerDelegate2_ (narrowAssertCategory.c_str (), narrowAssert.c_str (), narrowFile.c_str (), lineNum, narrowFunctionName.c_str ());
130 }
131 catch (...) {
132 abort (); // ooops
133 }
134 };
135 SetAssertionHandler (wrapper);
136}
137
138/*
139 ********************************************************************************
140 ************************** Debug::GetWeakAssertionHandler **********************
141 ********************************************************************************
142 */
143AssertionHandlerType Stroika::Foundation::Debug::GetWeakAssertionHandler ()
144{
145 return sWeakAssertFailureHandler_;
146}
147
148/*
149 ********************************************************************************
150 ********************** Debug::GetDefaultWeakAssertionHandler *******************
151 ********************************************************************************
152 */
153AssertionHandlerType Stroika::Foundation::Debug::GetDefaultWeakAssertionHandler ()
154{
155 return DefaultWeakAssertionHandler_;
156}
157
158/*
159 ********************************************************************************
160 ************************** Debug::SetWeakAssertionHandler **********************
161 ********************************************************************************
162 */
164{
165 sWeakAssertFailureHandler_ = (assertionHandler == nullptr) ? DefaultWeakAssertionHandler_ : assertionHandler;
166}
167
168namespace {
169 void (*sLegacyWeakHandlerDelegate2_) (const char* assertCategory, const char* assertionText, const char* fileName, int lineNum,
170 const char* functionName) noexcept;
171}
172void Stroika::Foundation::Debug::SetWeakAssertionHandler (void (*legacyHandler) (const char* assertCategory, const char* assertionText,
173 const char* fileName, int lineNum, const char* functionName) noexcept)
174{
175 sLegacyWeakHandlerDelegate2_ = legacyHandler;
176 auto wrapper = [] (const wchar_t* assertCategory, const wchar_t* assertionText, const wchar_t* fileName, int lineNum,
177 const wchar_t* functionName) noexcept {
178 try {
179 string narrowAssertCategory = String{assertCategory}.AsNarrowSDKString ();
180 string narrowAssert = String{assertionText}.AsNarrowSDKString ();
181 string narrowFile = String{fileName}.AsNarrowSDKString ();
182 string narrowFunctionName = String{functionName}.AsNarrowSDKString ();
183 sLegacyHandlerDelegate2_ (narrowAssertCategory.c_str (), narrowAssert.c_str (), narrowFile.c_str (), lineNum, narrowFunctionName.c_str ());
184 }
185 catch (...) {
186 abort (); // ooops
187 }
188 };
189 SetWeakAssertionHandler (wrapper);
190}
191
192[[noreturn]] void Stroika::Foundation::Debug::Private_::Assertion_Failure_Handler_ (const wchar_t* assertCategory, const wchar_t* assertionText,
193 const wchar_t* fileName, int lineNum, const char* functionName) noexcept
194{
195 static bool s_InTrap = false;
196 if (s_InTrap) {
197 // prevent infinite looping if we get an assertion triggered while processing an assertion.
198 // And ignore threading issues, because we are pragmatically aborting at this stage anyhow...
199 abort ();
200 }
201 s_InTrap = true;
202
203 (sAssertFailureHandler_.load ()) (assertCategory, assertionText, fileName, lineNum, Characters::NarrowSDK2Wide (functionName).c_str ());
204 // in case using some sort of assertion handler that allows for continuation
205 // (like under debugger manipulation of PC to go a little further in the code)
206 s_InTrap = false;
207
208 // Doesn't matter much what we do at this stage, but in visual studio debugger, you can skip this line
209#if qCompilerAndStdLib_quick_exit_Buggy
210 _Exit (1);
211#else
212 quick_exit (1);
213#endif
214}
215
216void Stroika::Foundation::Debug::Private_::Weak_Assertion_Failure_Handler_ (const wchar_t* assertCategory, const wchar_t* assertionText,
217 const wchar_t* fileName, int lineNum, const char* functionName) noexcept
218{
219 (sWeakAssertFailureHandler_.load ()) (assertCategory, assertionText, fileName, lineNum, Characters::NarrowSDK2Wide (functionName).c_str ());
220}
221#endif
void SetWeakAssertionHandler(AssertionHandlerType assertionHandler)
void SetAssertionHandler(AssertionHandlerType assertionHandler)
AssertionHandlerType GetAssertionHandler()
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:48
void(*)(const wchar_t *assertCategory, const wchar_t *assertionText, const wchar_t *fileName, int lineNum, const wchar_t *functionName) noexcept AssertionHandlerType
Definition Assertions.h:83
#define CompileTimeFlagChecker_SOURCE(NS_PREFIX, NAME, VALUE)
#define DbgTrace
Definition Trace.h:309
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual string AsNarrowSDKString() const
Definition String.inl:834