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