ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
assert.cpp
1#if ALIB_DEBUG
3
4namespace alib::assert {
5
6//##################################################################################################
7// Debug functions
8//##################################################################################################
9#if (ALIB_SINGLE_THREADED && ALIB_EXT_LIB_THREADS_AVAILABLE) || DOXYGEN
10
11#if !DOXYGEN
12 namespace { std::thread::id dbg_thread_seen;
13 bool dbg_in_single_threaded_check= false; }
14#endif
15
16
17//==================================================================================================
18/// This function stores the first thread that invokes it, and if in the future the method is
19/// visited by a different thread, it asserts.
20///
21/// In release compilations, this function is inlined and empty, and therefore it is not
22/// necessary to remove usages with preprocessor macro #"ALIB_DBG" or similar.
23///
24/// In debug-compilations, this is not empty if:
25/// 1. Configuration macro #"ALIB_SINGLE_THREADED" is set (what disables thread-safeness
26/// throughout the library), and
27/// 2. Configuration macro #"ALIB_EXT_LIB_THREADS_AVAILABLE" was passed on library compilation,
28/// which allows using the C++ standard library's threading types without causing linker
29/// failures.
30///
31/// If given, this function is, for example, called by macros #"ALIB_LOCK" or
32/// #"ALIB_DCS", which otherwise are defined to do what they are supposed to do.
33/// This exclamatory approach was made with \alib to motivate to
34/// write #"alib_threads_intro_agnostic;threading-agnostic software".
35///
36/// Besides several macros, some other prominent \alib-entities, like #"Lox", #"format::Formatter",
37/// or #"TMonoAllocator" invoke this method with their acquisition.
38//==================================================================================================
40{
41 if( dbg_in_single_threaded_check ) // this would happen when the assertion below is raised
42 return;
43 dbg_in_single_threaded_check= true;
44
45 // first time?
46 if( lang::IsNull(dbg_thread_seen) )
47 {
48 dbg_thread_seen= std::this_thread::get_id();
49 dbg_in_single_threaded_check= false;
50 return;
51 }
52
53 if( dbg_thread_seen != std::this_thread::get_id() )
54 {
55 ALIB_ERROR( "THREADS",
56 "A second thread was detected using a single-threaded compilation of ALib"
57 "(Configuration Macro 'ALIB_SINGLE_THREADED' given with the ALib Build)." )
58 }
59 dbg_in_single_threaded_check= false;
60}
61#endif // ALIB_SINGLE_THREADED && ALIB_EXT_LIB_THREADS_AVAILABLE
62
63} // namespace [alib]
64
65//##################################################################################################
66// Assert functions
67//##################################################################################################
68namespace alib::assert {
69
70void (*PLUGIN)( const CallerInfo& ci , int type,
71 std::string_view domain, std::string_view msg ) = nullptr;
72
73std::string_view FORMAT = "{file}:{line} {type}:\n{message}";
74std::ostream* STREAM_ERRORS = &std::cerr;
75std::ostream* STREAM_WARNINGS = &std::cout;
76std::ostream* STREAM_MESSAGES = &std::cout;
77
78#if !DOXYGEN
79namespace {
80 thread_local TLD HALT_FLAGS_AND_COUNTERS;
81 bool isInitialized= false;
82 std::string outBuffer;
83#if !ALIB_SINGLE_THREADED
84 RecursiveLock lock;
85#endif
86
87
88static std::unordered_map<std::type_index, AnyConversionFunc> registeredAnys;
89
90#if __has_include(<format>)
91 namespace f=std;
92#else
93 namespace f=fmt;
94#endif
95
96void appendUtf8CodePoint(uint32_t codePoint, std::string& s) {
97 if (codePoint > 0x10FFFFu || (codePoint >= 0xD800u && codePoint <= 0xDFFFu))
98 codePoint= 0xFFFDu;
99
100 if (codePoint <= 0x7Fu) {
101 s+= static_cast<char>(codePoint);
102 } else if (codePoint <= 0x7FFu) {
103 s+= static_cast<char>(0xC0u | (codePoint >> 6));
104 s+= static_cast<char>(0x80u | (codePoint & 0x3Fu));
105 } else if (codePoint <= 0xFFFFu) {
106 s+= static_cast<char>(0xE0u | (codePoint >> 12));
107 s+= static_cast<char>(0x80u | ((codePoint >> 6) & 0x3Fu));
108 s+= static_cast<char>(0x80u | (codePoint & 0x3Fu));
109 } else {
110 s+= static_cast<char>(0xF0u | (codePoint >> 18));
111 s+= static_cast<char>(0x80u | ((codePoint >> 12) & 0x3Fu));
112 s+= static_cast<char>(0x80u | ((codePoint >> 6) & 0x3Fu));
113 s+= static_cast<char>(0x80u | (codePoint & 0x3Fu));
114} }
115
116template<typename TChar>
117uint32_t toUnsignedCodeUnit(TChar value) {
118 if constexpr (sizeof(TChar) == 1) return static_cast<uint32_t>(static_cast<uint8_t >(value));
119 if constexpr (sizeof(TChar) == 2) return static_cast<uint32_t>(static_cast<uint16_t>(value));
120 return static_cast<uint32_t>(value);
121}
122
123template<typename TChar>
124void appendUtf8FromScalar(TChar value, std::string& s)
125{
126 appendUtf8CodePoint(toUnsignedCodeUnit(value), s);
127}
128
129#if ALIB_STRINGS
130template<typename TChar>
131void appendUtf8FromRange(const TChar* begin, const TChar* end, std::string& s) {
132 if (begin == nullptr || end == nullptr || begin >= end)
133 return;
134
135 if constexpr (sizeof(TChar) == 2) {
136 for (const TChar* it= begin; it < end; ++it) {
137 uint32_t codePoint= toUnsignedCodeUnit(*it);
138 if (codePoint >= 0xD800u && codePoint <= 0xDBFFu) {
139 if (it + 1 < end) {
140 uint32_t low= toUnsignedCodeUnit(*(it + 1));
141 if (low >= 0xDC00u && low <= 0xDFFFu) {
142 codePoint= 0x10000u + ((codePoint - 0xD800u) << 10) + (low - 0xDC00u);
143 ++it;
144 } else {
145 codePoint= 0xFFFDu;
146 }
147 } else {
148 codePoint= 0xFFFDu;
149 }
150 } else if (codePoint >= 0xDC00u && codePoint <= 0xDFFFu) {
151 codePoint= 0xFFFDu;
152 }
153 appendUtf8CodePoint(codePoint, s);
154 }
155 } else {
156 for (const TChar* it= begin; it < end; ++it)
157 appendUtf8CodePoint(toUnsignedCodeUnit(*it), s);
158} }
159#endif
160
161template<typename TChar>
162void appendUtf8FromNullTerminated(const TChar* value, std::string& s) {
163 if (value == nullptr)
164 return;
165
166 if constexpr (sizeof(TChar) == 2) {
167 while (*value != static_cast<TChar>(0)) {
168 uint32_t codePoint= toUnsignedCodeUnit(*value++);
169 if (codePoint >= 0xD800u && codePoint <= 0xDBFFu) {
170 if (*value != static_cast<TChar>(0)) {
171 uint32_t low= toUnsignedCodeUnit(*value);
172 if (low >= 0xDC00u && low <= 0xDFFFu) {
173 codePoint= 0x10000u + ((codePoint - 0xD800u) << 10) + (low - 0xDC00u);
174 ++value;
175 } else {
176 codePoint= 0xFFFDu;
177 }
178 } else {
179 codePoint= 0xFFFDu;
180 }
181 } else if (codePoint >= 0xDC00u && codePoint <= 0xDFFFu) {
182 codePoint= 0xFFFDu;
183 }
184 appendUtf8CodePoint(codePoint, s);
185 }
186 } else {
187 while (*value != static_cast<TChar>(0))
188 appendUtf8CodePoint(toUnsignedCodeUnit(*value++), s);
189} }
190
191void initializeDefaultPrintables() {
192 isInitialized= true;
193 RegisterPrintable(typeid( bool ), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast< bool >(any)); });
194 RegisterPrintable(typeid( char ), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast< char >(any)); });
195 RegisterPrintable(typeid( int8_t ), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast< int8_t >(any)); });
196 RegisterPrintable(typeid(uint8_t ), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast<uint8_t >(any)); });
197 RegisterPrintable(typeid( int16_t ), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast< int16_t >(any)); });
198 RegisterPrintable(typeid(uint16_t ), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast<uint16_t >(any)); });
199 RegisterPrintable(typeid( int32_t ), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast< int32_t >(any)); });
200 RegisterPrintable(typeid(uint32_t ), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast<uint32_t >(any)); });
201 RegisterPrintable(typeid( int64_t ), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast< int64_t >(any)); });
202 RegisterPrintable(typeid(uint64_t ), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast<uint64_t >(any)); });
203 RegisterPrintable(typeid(float ), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast<float >(any)); });
204 RegisterPrintable(typeid(unsigned long), [](const std::any& any, std::string& s) { s+= f::format("{}", std::any_cast<unsigned long>(any)); });
205 RegisterPrintable(typeid( wchar_t ), [](const std::any& any, std::string& s) {
206 wchar_t wc= std::any_cast<wchar_t>(any);
207 appendUtf8FromScalar(wc, s);
208 });
209DOX_MARKER( [DOX_ASSERT_REGISTER_PRINTABLE])
210RegisterPrintable(typeid(double),
211 [](const std::any& any, std::string& s) {
212 s+= f::format("{}", std::any_cast<double >(any));
213 });
214RegisterPrintable(typeid(const char*),
215 [](const std::any& any, std::string& s) {
216 auto* value= std::any_cast<const char*>(any);
217 if (value)
218 s+= value;
219 });
220RegisterPrintable(typeid(const char8_t*),
221 [](const std::any& any, std::string& s) {
222 auto* value= std::any_cast<const char*>(any);
223 if (value)
224 s+= value;
225 });
226RegisterPrintable(typeid(const wchar_t*),
227 [](const std::any& any, std::string& s) {
228 auto* value= std::any_cast<const wchar_t*>(any);
229 appendUtf8FromNullTerminated(value, s);
230 });
231DOX_MARKER( [DOX_ASSERT_REGISTER_PRINTABLE])
232
233 if constexpr (sizeof(wchar_t) == 2)
234 RegisterPrintable(typeid(const char32_t*),
235 [](const std::any& any, std::string& s) {
236 auto* value= std::any_cast<const char32_t*>(any);
237 appendUtf8FromNullTerminated(value, s);
238 });
239 if constexpr (sizeof(wchar_t) == 4)
240 RegisterPrintable(typeid(const char16_t*),
241 [](const std::any& any, std::string& s) {
242 auto* value= std::any_cast<const char16_t*>(any);
243 appendUtf8FromNullTerminated(value, s);
244 });
245
246 RegisterPrintable(typeid(std::string ), [](const std::any& any, std::string& s) { s+= *std::any_cast<std::string >(&any); });
247 RegisterPrintable(typeid(std::string_view ), [](const std::any& any, std::string& s) { s+= std::any_cast<std::string_view>( any); });
248 RegisterPrintable(typeid(const std::type_info*), [](const std::any& any, std::string& s) {
249 auto* typeInfo= std::any_cast<const std::type_info*>(any);
250 if (typeInfo)
251 s+= lang::DbgTypeDemangler(*typeInfo).Get();
252 });
253 RegisterPrintable(typeid(std::thread::id) , [](const std::any& any, std::string& s) {
254 #if !ALIB_SINGLE_THREADED
255 auto threadID= std::any_cast<std::thread::id>(any);
256 if ( !lang::IsNull(threadID)) {
257 Thread* thread= Thread::Get(threadID);
258 if (thread) {
259 #if !ALIB_CHARACTERS_WIDE
260 s+= thread->GetName();
261 #else
262 appendUtf8FromNullTerminated(thread->GetName(), s);
263 #endif
264 s += f::format("({})", thread->GetID());
265 }
266 else
267 s+= "<Unknown thread>";
268 }
269 #else
270 (void) any;
271 s+= "<SINGLE_THREADED>";
272 #endif
273 });
274
275 RegisterPrintable(typeid(std::errc), [](const std::any& any, std::string& s) {
276 std::error_code ec = std::make_error_code(std::any_cast<std::errc>(any));
277 s+= '"'; s+= ec.message(); s+= '"'; s+= f::format("({})", ec.value());
278 });
279
280
281 RegisterPrintable(typeid(CallerInfo), [](const std::any& any, std::string& s) {
282 CallerInfo callerInfo= std::any_cast<CallerInfo>(any);
283 if ( callerInfo.File == nullptr ) {
284 s+= "<nulled caller>";
285 return;
286 }
287 s+=f::format("{{Caller: @ {}:{} ({}) ", callerInfo.File,callerInfo.Line, callerInfo.Func);
288 if ( callerInfo.TypeInfo ) {
289 s+= "<"; s+= lang::DbgTypeDemangler(*callerInfo.TypeInfo).Get();s+= "> ";
290 }
291 #if !ALIB_SINGLE_THREADED
292 s+=f::format("thread::id= {}", 5 );
293 #endif
294 s+= '}';
295 });
296
297 RegisterPrintable(typeid(Thread*) , [](const std::any& any, std::string& s) {
298 #if !ALIB_SINGLE_THREADED
299 Thread* thread= std::any_cast<Thread*>(any);
300 if (thread) {
301 #if !ALIB_CHARACTERS_WIDE
302 s+= thread->GetName();
303 #else
304 appendUtf8FromNullTerminated(thread->GetName(), s);
305 #endif
306 s += f::format("({})", thread->GetID());
307 }
308 else
309 s+= "<Unknown thread>";
310 #else
311 (void) any;
312 s+= "<SINGLE_THREADED>";
313 #endif
314 });
315
316 #if !ALIB_SINGLE_THREADED
317 RegisterPrintable(typeid(Thread::State) , [](const std::any& any, std::string& s) {
318 auto state= std::any_cast<Thread::State>(any);
319 if (state==Thread::State::Unstarted ) s+= "Unstarted";
320 else if (state==Thread::State::Started ) s+= "Started";
321 else if (state==Thread::State::Running ) s+= "Running";
322 else if (state==Thread::State::Done ) s+= "Done";
323 else if (state==Thread::State::Terminated) s+= "Terminated";
324 else s+= "<Unknown thread state>";
325 });
326 #endif
327
328 #if ALIB_STRINGS
329 RegisterPrintable(typeid(NString ),[](const std::any& any, std::string& s) { s+= std::string_view( std::any_cast<NString >(any).Buffer(), size_t(std::any_cast<NString >(any).Length()) ); });
330 RegisterPrintable(typeid(NAString ),[](const std::any& any, std::string& s) { s+= std::string_view( std::any_cast<NAString>(any).Buffer(), size_t(std::any_cast<NAString>(any).Length()) ); });
331 RegisterPrintable(typeid(NCString ),[](const std::any& any, std::string& s) { s+= std::string_view( std::any_cast<NCString>(any).Buffer(), size_t(std::any_cast<NCString>(any).Length()) ); });
332 RegisterPrintable(typeid(WString ),[](const std::any& any, std::string& s) { auto src=std::any_cast<WString >(any); appendUtf8FromRange(src.Buffer(), src.Buffer() + src.Length(), s); });
333 RegisterPrintable(typeid(WAString ),[](const std::any& any, std::string& s) { auto src=std::any_cast<WAString>(any); appendUtf8FromRange(src.Buffer(), src.Buffer() + src.Length(), s); });
334 RegisterPrintable(typeid(WCString ),[](const std::any& any, std::string& s) { auto src=std::any_cast<WCString>(any); appendUtf8FromRange(src.Buffer(), src.Buffer() + src.Length(), s); });
335 RegisterPrintable(typeid(XString ),[](const std::any& any, std::string& s) { auto src=std::any_cast<XString >(any); appendUtf8FromRange(src.Buffer(), src.Buffer() + src.Length(), s); });
336 RegisterPrintable(typeid(XAString ),[](const std::any& any, std::string& s) { auto src=std::any_cast<XAString>(any); appendUtf8FromRange(src.Buffer(), src.Buffer() + src.Length(), s); });
337 RegisterPrintable(typeid(XCString ),[](const std::any& any, std::string& s) { auto src=std::any_cast<XCString>(any); appendUtf8FromRange(src.Buffer(), src.Buffer() + src.Length(), s); });
338
339 RegisterPrintable(typeid(NSubstring),[](const std::any& any, std::string& s) { s+= std::string_view( std::any_cast<NSubstring >(any).Buffer(), size_t(std::any_cast<NSubstring >(any).Length()) ); });
340 RegisterPrintable(typeid(WSubstring),[](const std::any& any, std::string& s) { auto src=std::any_cast<WSubstring >(any); appendUtf8FromRange(src.Buffer(), src.Buffer() + src.Length(), s); });
341
342 RegisterPrintable(typeid(Token ),[](const std::any& any, std::string& s) { s+= NString256(String256(std::any_cast<Token>(any))); });
343 #if ALIB_MONOMEM
344 RegisterPrintable(typeid(NAStringMA),[](const std::any& any, std::string& s) { s+= std::string_view( std::any_cast<NAStringMA>(any).Buffer(), size_t(std::any_cast<NAStringMA>(any).Length()) ); });
345 RegisterPrintable(typeid(WAStringMA),[](const std::any& any, std::string& s) { auto src=std::any_cast<WAStringMA>(any); appendUtf8FromRange(src.Buffer(), src.Buffer() + src.Length(), s); });
346 RegisterPrintable(typeid(NAStringPA),[](const std::any& any, std::string& s) { s+= std::string_view( std::any_cast<NAStringPA>(any).Buffer(), size_t(std::any_cast<NAStringPA>(any).Length()) ); });
347 RegisterPrintable(typeid(WAStringPA),[](const std::any& any, std::string& s) { auto src=std::any_cast<WAStringPA>(any); appendUtf8FromRange(src.Buffer(), src.Buffer() + src.Length(), s); });
348 #endif
349 #endif
350
351 #if ALIB_BOXING && ALIB_ENUMRECORDS
352 RegisterPrintable(typeid(Box ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<Box >(any)); });
353 RegisterPrintable(typeid(Enum), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<Enum>(any)); });
354 #endif
355
356 // CodeMarker_CommonEnums
357 #if ALIB_ENUMRECORDS
358 RegisterPrintable(typeid(lang::Alignment ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Alignment >(any)); });
359 RegisterPrintable(typeid(lang::Bool ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Bool >(any)); });
360 RegisterPrintable(typeid(lang::Caching ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Caching >(any)); });
361 RegisterPrintable(typeid(lang::Case ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Case >(any)); });
362 RegisterPrintable(typeid(lang::ContainerOp ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::ContainerOp >(any)); });
363 RegisterPrintable(typeid(lang::CreateDefaults ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::CreateDefaults >(any)); });
364 RegisterPrintable(typeid(lang::CreateIfNotExists), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::CreateIfNotExists>(any)); });
365 RegisterPrintable(typeid(lang::CurrentData ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::CurrentData >(any)); });
366 RegisterPrintable(typeid(lang::Inclusion ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Inclusion >(any)); });
367 RegisterPrintable(typeid(lang::Initialization ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Initialization >(any)); });
368 RegisterPrintable(typeid(lang::LineFeeds ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::LineFeeds >(any)); });
369 RegisterPrintable(typeid(lang::Phase ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Phase >(any)); });
370 RegisterPrintable(typeid(lang::Propagation ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Propagation >(any)); });
371 RegisterPrintable(typeid(lang::Reach ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Reach >(any)); });
372 RegisterPrintable(typeid(lang::Recursive ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Recursive >(any)); });
373 RegisterPrintable(typeid(lang::Responsibility ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Responsibility >(any)); });
374 RegisterPrintable(typeid(lang::Safeness ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Safeness >(any)); });
375 RegisterPrintable(typeid(lang::Side ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Side >(any)); });
376 RegisterPrintable(typeid(lang::SortOrder ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::SortOrder >(any)); });
377 RegisterPrintable(typeid(lang::SourceData ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::SourceData >(any)); });
378 RegisterPrintable(typeid(lang::Switch ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Switch >(any)); });
379 RegisterPrintable(typeid(lang::Timezone ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Timezone >(any)); });
380 RegisterPrintable(typeid(lang::Timing ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Timing >(any)); });
381 RegisterPrintable(typeid(lang::ValueReference ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::ValueReference >(any)); });
382 RegisterPrintable(typeid(lang::Whitespaces ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<lang::Whitespaces >(any)); });
383 #endif
384
385
386 #if ALIB_VARIABLES
387 RegisterPrintable(typeid(const variables::Variable*), [](const std::any& any, std::string& s) {
388 auto* o= std::any_cast<const variables::Variable*>(any);
389 String256 serialize;
390 o->Name(serialize);
391 ALIB_STRINGS_TO_NARROW(serialize, ns, 256)
392 s+= ns;
393 });
394 RegisterPrintable(typeid(variables::Variable*), [](const std::any& any, std::string& s) {
395 auto* o= std::any_cast<variables::Variable*>(any);
396 String256 serialize;
397 o->Name(serialize);
398 ALIB_STRINGS_TO_NARROW(serialize, ns, 256)
399 s+= ns;
400 });
401 RegisterPrintable(typeid(variables::Variable), [](const std::any& any, std::string& s) {
402 auto o= std::any_cast<variables::Variable>(any);
403 String256 serialize;
404 o.Name(serialize);
405 ALIB_STRINGS_TO_NARROW(serialize, ns, 256)
406 s+= ns;
407 });
408 RegisterPrintable(typeid(variables::Priority), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<variables::Priority>(any)); });
409 #endif
410
411 #if ALIB_SYSTEM
412 RegisterPrintable(typeid(system::Path ), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<system::Path >(any)); });
413 #endif
414
415 #if ALIB_THREADMODEL && ALIB_ENUMRECORDS
416 RegisterPrintable(typeid(threadmodel::Priority), [](const std::any& any, std::string& s) { s+= NString256(std::any_cast<threadmodel::Priority>(any)); });
417 #endif
418
419 }
420
421void printRegisteredAny(const std::any& any, const CallerInfo& ci) {
422 if (!any.has_value())
423 return;
424 auto it = registeredAnys.find(std::type_index(any.type()));
425 if (it != registeredAnys.end()) {
426 // Call the associated function
427 it->second(any, outBuffer);
428 } else {
429 std::cerr << "Internal Error using alib::assert::Assert(): No converter registered for type: <"
430 << lang::DbgTypeDemangler( any.type() ).Get()
431 << '>' << std::endl
432 << "at " << ci.File << ':' << ci.Line << std::endl;
433 assert(false);
434} }
435
436const char* resolveMessageType(int msgType) {
437 if (msgType == 0) return "error";
438 if (msgType == 1) return "warning";
439 return "message";
440}
441
442void writeMessage( std::ostream& os, int msgType, const std::string_view& domain,
443 const char* file, int line,
444 const std::string_view& message ) {
445 // Start with the global format string
446 std::string_view format = FORMAT;
447
448 // Loop and process the format string
449 size_t start = 0;
450 while (start < format.size()) {
451 // Find the next placeholder starting from 'start'
452 size_t openBrace = format.find('{', start);
453 if (openBrace == std::string_view::npos) {
454 // No more placeholders, print the remaining part
455 os << format.substr(start);
456 break;
457 }
458
459 // Print everything before the '{'
460 os << format.substr(start, openBrace - start);
461
462 // Find the closing '}'
463 size_t closeBrace = format.find('}', openBrace + 1);
464 if (closeBrace == std::string_view::npos) {
465 // Invalid placeholder syntax (no closing '}'), just print raw text
466 os << format.substr(openBrace);
467 break;
468 }
469
470 // Extract the placeholder name
471 std::string_view placeholder = format.substr(openBrace + 1, closeBrace - openBrace - 1);
472
473 // Substitute the placeholder with the corresponding value
474 if ( placeholder == "type" ) os << resolveMessageType(msgType);
475 else if ( placeholder == "file" ) os << file;
476 else if ( placeholder == "line" ) os << line;
477 else if ( placeholder == "message" ) os << message;
478 else if ( placeholder == "domain" ) os << domain;
479 else os << "{" << placeholder << "}"; // unknown placeholder
480
481 // Move past the closing '}'
482 start = closeBrace + 1;
483 }
484 os << std::endl;
485} // writeMessage
486
487class RecursionBlocker {
488 private:
489 static inline std::atomic<bool> isRecursing{false};
490 bool wasBlocked;
491
492 public:
493 RecursionBlocker() noexcept { wasBlocked = isRecursing.exchange(true); }
494
495 ~RecursionBlocker() { if (!wasBlocked) isRecursing = false; }
496
497 [[nodiscard]] bool blocked() const noexcept { return wasBlocked; }
498
499 // Delete copy operations to ensure RAII semantics
500 RecursionBlocker(const RecursionBlocker&) =delete;
501 RecursionBlocker& operator =(const RecursionBlocker&) =delete;
502};
503
504#if ALIB_DEBUG_ASSERTION_PRINTABLES
505 struct Key { const char* str; // Pointer to the file name
506 int value; // Line number
507 bool operator==(const Key& other) const {
508 return str == other.str && value == other.value;
509 }};
510
511 struct KeyHash { std::size_t operator()(const Key& key) const {
512 return std::hash<const char*>()(key.str) ^ ( std::hash<int>()(key.value) << 1);
513 }};
514
515 std::unordered_set<Key, KeyHash> seenSourceLocations;
516#endif // ALIB_DEBUG_ASSERTION_PRINTABLES
517
518} // namespace [anonymous]
519
520void RegisterPrintable(std::type_index typeIndex, AnyConversionFunc func) {
521 registeredAnys[typeIndex] = func;
522}
523
524#if ALIB_DEBUG_ASSERTION_PRINTABLES
525# pragma message ("ALIB_DEBUG_ASSERTION_PRINTABLES set. ALib will print all assertions once, no matter if they are raised or not." )
526 void CheckArgsImpl( const CallerInfo& ci, const std::span<std::any>& args ) {
527 if ( !alib::NonCampModulesInitialized ) return;
528 if( !isInitialized ) initializeDefaultPrintables();
529
530 if (!seenSourceLocations.insert(Key{ci.File, ci.Line}).second)
531 return;
532
533
534 for (size_t i = 0; i < args.size(); ++i) {
535 auto it = registeredAnys.find(std::type_index(args[i].type()));
536 if (it == registeredAnys.end()) {
537 std::cerr << "Internal Error using alib::assert::Assert(): No converter registered for type: <"
538 << lang::DbgTypeDemangler( args[i].type() ).Get()
539 << '>' << std::endl
540 << " at: " << ci.File << ':' << ci.Line << std::endl;
541 assert(false);
542 }}
543
544 raise(ci, -1, "ASSERTTEST", args );
545 }
546#endif
547
548#endif // !DOXYGEN
549
550TLD& GetHaltFlagAndCounters() { return HALT_FLAGS_AND_COUNTERS; }
551
552void raise( const CallerInfo& ci, int type, std::string_view domain,
553 const std::span<std::any>& args ) {
554
555 // lock parallel assertions if necessary
557 RecursionBlocker blocker;
558 if (blocker.blocked())
559 return;
560
561 // register printable types (once)
562 if( !isInitialized )
563 initializeDefaultPrintables();
564
565 // assemble message
566 outBuffer.clear();
567
568 // With ALox replacing this method (the usual thing), the first string might auto-detected
569 // as an ALox domain name. To keep this consistent, we do a similar effort here.
570 {
571 for( nchar c : domain ) {
572 if (! ( isdigit( c )
573 || ( c >= 'A' && c <= 'Z' )
574 || c == '-' || c == '_' || c == '/' || c == '.' ) ) {
575 std::cerr << "Illegal alib::assert::Assert() domain given: " << domain << std::endl;
576 assert(false);
577 }}}
578
579 size_t i = 0; // Index to track the current argument
580 while (i < args.size()) {
581 const std::any& arg = args[i];
582
583 if ( arg.type() == typeid(const char*)
584 || arg.type() == typeid(std::string)
585 || arg.type() == typeid(std::string_view) ) {
586
587 std::string_view str;
588
589 if (arg.type() == typeid(const char*)) str = std::string_view( std::any_cast<const char* >( arg));
590 else if (arg.type() == typeid(std::string)) str = std::string_view(*std::any_cast<std::string >(&arg));
591 else str = std::any_cast<std::string_view>( arg) ;
592 std::string_view origStr= str;
593
594
595 // Lambda function to parse and print the format string
596 auto handle_format_string = [&]() {
597 size_t pos = 0; // Position to find "{}"
598 while ((pos = str.find("{}")) != std::string::npos) {
599 // Print the portion before the placeholder
600 outBuffer+= str.substr(0, pos);
601
602 // Move to the next argument for substitution
603 if (++i >= args.size()) {
604 std::cerr << "alib::assert: Not enough arguments for format placeholders!" << std::endl;
605 std::cerr << " Format string: <" << origStr << '>' << std::endl;
606 std::cerr << " @ : " << ci.File << ':' << ci.Line << std::endl;
607 assert(false);
608 return;
609 }
610
611 // Print the argument substituted in place of "{}"
612 const std::any& placeholderArg = args[i];
613 printRegisteredAny(placeholderArg, ci);
614
615 // Remove the processed portion of the string
616 str = str.substr(pos + 2);
617 }
618
619 // Print the remaining part of the string (after all `{}` are replaced)
620 outBuffer+= str;
621
622 // End the lambda function
623 };
624
625 // Process the format string with the lambda
626 handle_format_string();
627 } else
628 printRegisteredAny(arg, ci);
629
630 ++i;
631 }
632
633 // if plugin hook, use it.
634 if( PLUGIN && type!= -1 ) // -1 = CheckArgs
635 PLUGIN( ci, type, domain, outBuffer.c_str() );
636
637 // Print the formatted message to the console.
638 else {
639 // check if we already lock io-streams. We can do this, because this is anyhow
640 // debug-code. This way, we avoid recursive locking.
641 // (If locked by somebody else, we do not care and still write to the ostream!)
642 #if !ALIB_SINGLE_THREADED
643 bool lockingIoStreams= !threads::STD_IOSTREAMS_LOCK.Dbg.IsOwnedByCurrentThread();
644 if( lockingIoStreams )
646 #endif
647
648 writeMessage( type == 0 ? *STREAM_ERRORS
649 : type == 1 ? *STREAM_WARNINGS
651 type, domain, ci.File, ci.Line, outBuffer);
652 #if !ALIB_SINGLE_THREADED
653 if( lockingIoStreams )
655 #endif
656 }
657
658 // Halt?
659 bool halt;
660 if (type == 0) {HALT_FLAGS_AND_COUNTERS.CtdErrors++; halt= HALT_FLAGS_AND_COUNTERS.HaltOnErrors; }
661 else if (type == 1) {HALT_FLAGS_AND_COUNTERS.CtdWarnings++; halt= HALT_FLAGS_AND_COUNTERS.HaltOnWarnings; }
662 else {HALT_FLAGS_AND_COUNTERS.CtdMessages++; halt= false; }
663 #if defined( _WIN32 )
664 if( halt ) {
665 #if ALIB_CAMP
666 if ( BASECAMP.IsDebuggerPresent() )
667 DebugBreak();
668 else
669 #endif
670 assert( false );
671 }
672 #else
673 #if defined(__GNUC__) || defined(__clang__)
674 if (halt)
675 __builtin_trap();
676 #elif defined ( _MSC_VER )
677 if (halt)
678 __debugbreak();
679 #else
680 (void) halt; // for release compiles with ALIB_DEBUG set
681 assert( !halt );
682 #endif
683
684 #endif
685}
686}// namespace [alib::assert]
687
688# include "ALib.Lang.CIMethods.H"
689#endif // ALIB_DEBUG
#define ALIB_CALLER
#define ALIB_ERROR(domain,...)
#define ALIB_LOCK_RECURSIVE_WITH(lock)
@ Running
The thread's #".Run" method is currently processed.
Definition thread.hpp:138
@ Started
Method #".Start" was invoked but not running, yet.
Definition thread.hpp:137
@ Terminated
The thread is terminated.
Definition thread.hpp:141
ThreadID GetID() const
Definition thread.hpp:223
virtual const character * GetName() const
Definition thread.hpp:237
static Thread * Get(std::thread::id nativeID)
Definition thread.cpp:253
This namespace exposes entities of module ALib Assert.
Definition assert.cpp:4
void CheckArgsImpl(const CallerInfo &ci, const std::span< std::any > &args)
void(* PLUGIN)(const CallerInfo &ci, int type, std::string_view domain, std::string_view msg)
Definition assert.cpp:70
std::string_view FORMAT
Definition assert.cpp:73
void RegisterPrintable(std::type_index typeIndex, AnyConversionFunc func)
std::ostream * STREAM_MESSAGES
Definition assert.cpp:76
void raise(const CallerInfo &ci, int type, std::string_view domain, const std::span< std::any > &args)
Definition assert.cpp:552
std::ostream * STREAM_ERRORS
Definition assert.cpp:74
TLD & GetHaltFlagAndCounters()
Definition assert.cpp:550
std::ostream * STREAM_WARNINGS
Definition assert.cpp:75
void SingleThreaded()
Definition assert.cpp:39
void(*)(const std::any &, std::string &) AnyConversionFunc
Definition assert.hpp:88
SortOrder
Denotes sort order.
Side
Denotes if something is left or right.
SourceData
Denotes if the source data should be moved or copied.
Reach
Denotes the reach of something.
Recursive
Denotes whether recursion is performed/allowed or not.
Timing
Denotes if asynchronous tasks become synchronized.
Alignment
Denotes Alignments.
LineFeeds
Denotes line-feed encoding sequences "\n" and "\r\n".
ContainerOp
Denotes standard container operations.
Switch
Denotes if sth. is switched on or off.
Phase
Denotes a phase, e.g.,of a transaction.
CreateIfNotExists
Denotes whether something should be created if it does not exist.
Case
Denotes upper and lower case character treatment.
CreateDefaults
Denotes whether default entities should be created or not.
constexpr bool IsNull(const T &t)
Definition tmp.hpp:48
Whitespaces
Denotes whether a string is trimmed or not.
Caching
Denotes if a cache mechanism is enabled or disabled.
Propagation
Denotes whether a e.g a setting should be propagated.
ValueReference
Denotes if a value is interpreted as an absolute or relative number.
Safeness
Denotes whether something should be performed in a safe or unsafe fashion.
Initialization
Used, for example, with constructors that allow to suppress initialization of members.
Inclusion
Denotes how members of a set something should be taken into account.
Timezone
Denotes whether a time value represents local time or UTC.
Priority
Possible priorities of jobs assigned to an #"DedicatedWorker".
Definition jobs.hpp:166
Lock STD_IOSTREAMS_LOCK
Definition locks.cpp:17
strings::TAString< wchar, MonoAllocator > WAStringMA
Type alias in namespace #"%alib".
strings::TString< nchar > NString
Type alias in namespace #"%alib".
Definition string.hpp:2174
threads::Thread Thread
Type alias in namespace #"%alib".
Definition thread.hpp:387
strings::TCString< wchar > WCString
Type alias in namespace #"%alib".
Definition cstring.hpp:411
strings::TCString< nchar > NCString
Type alias in namespace #"%alib".
Definition cstring.hpp:408
strings::TString< wchar > WString
Type alias in namespace #"%alib".
Definition string.hpp:2177
strings::TAString< wchar, PoolAllocator > WAStringPA
Type alias in namespace #"%alib".
strings::TString< xchar > XString
Type alias in namespace #"%alib".
Definition string.hpp:2180
strings::TAString< nchar, lang::HeapAllocator > NAString
Type alias in namespace #"%alib".
boxing::Box Box
Type alias in namespace #"%alib".
Definition box.hpp:1128
strings::TAString< xchar, lang::HeapAllocator > XAString
Type alias in namespace #"%alib".
characters::nchar nchar
Type alias in namespace #"%alib".
strings::TAString< nchar, MonoAllocator > NAStringMA
Type alias in namespace #"%alib".
camp::Basecamp BASECAMP
The singleton instance of ALib Camp class #"Basecamp".
Definition basecamp.cpp:2
strings::TSubstring< nchar > NSubstring
Type alias in namespace #"%alib".
strings::TCString< xchar > XCString
Type alias in namespace #"%alib".
Definition cstring.hpp:414
bool NonCampModulesInitialized
lang::CallerInfo CallerInfo
Type alias in namespace #"%alib".
NLocalString< 256 > NString256
Type alias name for #"TLocalString;TLocalString<nchar,256>".
strings::TAString< wchar, lang::HeapAllocator > WAString
Type alias in namespace #"%alib".
strings::TAString< nchar, PoolAllocator > NAStringPA
Type alias in namespace #"%alib".
LocalString< 256 > String256
Type alias name for #"TLocalString;TLocalString<character,256>".
strings::TSubstring< wchar > WSubstring
Type alias in namespace #"%alib".
strings::util::Token Token
Type alias in namespace #"%alib".
Definition token.hpp:396
threads::RecursiveLock RecursiveLock
Type alias in namespace #"%alib".
boxing::Enum Enum
Type alias in namespace #"%alib".
Definition enum.hpp:210
#define ALIB_STRINGS_TO_NARROW( src, dest, bufSize)
const char * File
The name of the source file as given by compiler.
const std::type_info * TypeInfo
The calling type.
int Line
The line number within #".File".