ALib C++ Library
Library Version: 2510 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
configuration.cpp
1// #################################################################################################
2// ALib C++ Library
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 "Symbol 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// =========================================== Module ==========================================
18#if ALIB_C20_MODULES
19 module ALib.Variables;
20 import ALib.EnumRecords;
22 import ALib.Boxing;
23# if ALIB_RESOURCES
24 import ALib.Resources;
25# endif
27# if ALIB_CAMP
28 import ALib.Camp;
29# endif
30 import ALib.Camp.Base;
31#else
33# include "ALib.Boxing.H"
34# include "ALib.Resources.H"
36# include "ALib.Camp.H"
37# include "ALib.Camp.Base.H"
38# include "ALib.Variables.H"
39#endif
40// ====================================== Implementation =======================================
41
45
46
47namespace alib {
48
49//==================================================================================================
50/// This is the namespace of\alibmod, which implements access, evaluation and writing of
51/// configuration variables. Variable data can be load from different sources, for example
52/// configuration files, command line parameters, environment variables, or any custom storage type.
53///
54/// Please consult \ref alib_mod_variables "ALib Module Config - Programmer's Manual"
55/// for further information.
56//==================================================================================================
57namespace variables {
58
59
60// =================================================================================================
61// Implementation of class detail::NodeMaintainer
62// =================================================================================================
63namespace detail
64{
65void ConfigNodeHandler::FreeNode( TTree& tree, typename TTree::Node& node )
66{
67 // delete node name
68 auto& cfg= static_cast<Configuration&>(tree);
69 cfg.Pool.free( const_cast<TTree::CharacterType*>(node.name.storage.Buffer()),
70 size_t(node.name.storage.Length()) * sizeof(TTree::CharacterType) );
71
72 // delete vdata
73 Entry& entry= node.data;
74 if( entry.data )
75 {
76 entry.meta->destruct(entry.data, cfg.Pool);
77 cfg.Pool().Free(entry.data, entry.meta->size() );
78 }
79}
80} // namespace detail
81
82// =================================================================================================
83// Implementation of class Configuration
84// =================================================================================================
86: StringTree ( allocator, '/')
87, Pool ( allocator )
88, types ( allocator )
89, replacementDeclarations( allocator )
90, listeners ( allocator )
91, BooleanTokens ( allocator )
92{
93 DbgSetDCSName("Configuration");
94
95 // Register built-in types
103
104 // read boolean false/true values from resources
105 if( createDefaults == lang::CreateDefaults::Yes)
106 {
107 std::array<Token, 10> tokenBuf;
108 #if ALIB_CAMP
109 strings::util::LoadResourcedTokens( BASECAMP, "CFGBTF", tokenBuf.data() ALIB_DBG(, 10) );
110 for (size_t i = 0; i < tokenBuf.size(); i+= 2 )
111 BooleanTokens.emplace_back(tokenBuf[i], tokenBuf[i+1]);
112 #else
113 BooleanTokens.emplace_back(Token("False;I;1"), Token("True;I;1"));
114 BooleanTokens.emplace_back(Token( "0;I;1"), Token( "1;I;1"));
115 BooleanTokens.emplace_back(Token( "No;I;1"), Token( "Yes;I;1"));
116 BooleanTokens.emplace_back(Token( "Off;I;3"), Token( "On;I;2"));
117 BooleanTokens.emplace_back(Token( "---;I;1"), Token( "OK;I;2" ));
118 #endif
119
120 // create default plugins
121 auto& ma= GetAllocator();
122 InsertPlugin( environmentPlugin= ma().New<EnvironmentVariablesPlugin>(ma) );
123 InsertPlugin( cliPlugin= ma().New< CLIVariablesPlugin>(ma) );
124 }
125}
126
128{
129 // we have to delete all nodes before the invocation of the base destructor, because
130 // this would use our pool allocator on existing nodes (which is then destructed already).
131 base::Clear();
132
133 ALIB_ASSERT_WARNING( listeners.empty(), "VARIABLES",
134 "{} remaining registered listeners when destruction configuration.", listeners.size() )
135}
136
138 lang::ContainerOp insertOrRemove,
139 int event,
140 const Variable* variable,
141 const StringTree::Cursor* subTree,
142 const String& variableName,
143 const String& pathPrefixGiven,
144 const String& pathSubstring )
145{
146 // checks
147 ALIB_ASSERT_ERROR( variable==nullptr || variable->IsDeclared() , "VARIABLES", "Given variable not declared.")
148 ALIB_ASSERT_ERROR( variable==nullptr || &variable->AsCursor().Tree() == this , "VARIABLES", "Given variable does not belong to this configuration.")
149 ALIB_ASSERT_ERROR( subTree ==nullptr || subTree->IsValid() , "VARIABLES", "Invalid cursor given." )
150 ALIB_ASSERT_ERROR( subTree ==nullptr || &subTree ->Tree() == this , "VARIABLES", "Given cursor does not belong to this configuration.")
151
152 // remove / from path
153 const String pathPrefix= pathPrefixGiven.CharAtStart() == StringTree::Separator()
154 ? pathPrefixGiven.Substring(1)
155 : pathPrefixGiven;
156
157 // ---------------- registration ---------------------
158 if( insertOrRemove == lang::ContainerOp::Insert)
159 {
160 listeners.emplace_back( ListenerRecord{ listener,
161 event,
162 variable ? variable->AsCursor().Export() : ConstCursorHandle(),
163 subTree ? subTree-> Export() : ConstCursorHandle(),
166 AStringPA(Pool) } );
167 listeners.back().variableName << variableName;
168 listeners.back().pathPrefix << pathPrefix;
169 listeners.back().pathSubstring<< pathSubstring;
170
171 return;
172 }
173
174 // ---------------- de-registration ---------------------
175 for (auto it= listeners.begin() ; it != listeners.end() ; ++it )
176 if( it->listener == listener
177 && it->event == event
178 && it->variable == ( variable ? variable->AsCursor().Export() : ConstCursorHandle() )
179 && it->subTree == ( subTree ? subTree ->Export() : ConstCursorHandle() )
180 && it->variableName .Equals( variableName )
181 && it->pathPrefix .Equals( pathPrefix )
182 && it->pathSubstring.Equals( pathSubstring ) )
183 {
184 (void) listeners.erase( it );
185 return;
186 }
187
188 ALIB_WARNING( "VARIABLES", "Listener with matching set of parameters not found with "
189 "deregistration." )
190
191} // Configuration::registerListener
192
193
195{
196 // checks
197 ALIB_ASSERT_ERROR( listener!=nullptr, "VARIABLES", "Given listener is nullptr.")
198
199 // ---------------- de-registration ---------------------
200 int cnt= 0;
201 for (auto it= listeners.begin() ; it != listeners.end() ; )
202 if( it->listener == listener )
203 {
204 it= listeners.erase( it );
205 ++cnt;
206 }
207 else
208 ++it;
209
210 return cnt;
211} // Configuration::registerListener
212
214 const Variable& variable,
215 const String& variablePathGiven,
216 Priority previousPriority )
217{
218 String256 variablePathBuffer;
219 const String* variablePath= &variablePathGiven;
220 for (auto it= listeners.begin() ; it != listeners.end() ; ++it )
221 if( event == it->event )
222 {
223 // if needed generate variable path
224 if( variablePath->IsEmpty()
225 && ( it->variableName .IsNotEmpty()
226 || it->pathPrefix .IsNotEmpty()
227 || it->pathSubstring.IsNotEmpty() ) )
228 {
229 variablePathBuffer << variable;
230 variablePath= &variablePathBuffer;
231 }
232
233 if( ( it->variable .IsValid() && ( it->variable == variable.AsCursor().Export() ) )
234 || ( it->subTree .IsValid() && ( variable.AsCursor().Distance( ImportCursor(it->subTree) ) >= 0 ) )
235 || ( it->variableName .IsNotEmpty() && it->variableName.Equals(variable.AsCursor().Name()) )
236 || ( it->pathPrefix .IsNotEmpty() && variablePath->StartsWith(it->pathPrefix) )
237 || ( it->pathSubstring.IsNotEmpty() && variablePath->IndexOf(it->pathSubstring) >= 0 )
238 )
239 {
240 it->listener->Notify( variable,
242 previousPriority );
243 }
244 }
245} // Configuration::notifyListeners
246
247
248void Configuration::presetImportString(const String& name, const String& value,
249 const StringEscaper* escaper, Priority priority)
250{
251 auto cursor= Root();
252 cursor.GoToCreateChildIfNotExistent(A_CHAR("$PRESETS"));
253
254 // nullptr given? Delete a preset.
255 if( value.IsNull() )
256 {
257 if( cursor.GoTo(name).IsNotEmpty() )
258 return; // nothing was previously set
259
260 auto& entry= *cursor;
261 if( entry.priority > priority )
262 return; // do not delete if lower priority!
263
264 if( entry.data )
265 {
266 entry.meta->destruct( entry.data, Pool );
267 Pool().Free( entry.data, entry.meta->size() );
268 entry.meta= nullptr;
269 entry.data= nullptr;
270 entry.priority= Priority::NONE;
271 }
272
273 // delete this node, but only if it is a leaf.
274 if( !cursor.HasChildren() )
275 cursor.Delete();
276
277 return;
278 }
279
280
281 // set-mode
282 cursor.GoToCreatedPathIfNotExistent(name);
283
284 // create a fake variable. We do not do this using the variable class, because this would
285 // raise assertions.
286 auto& entry= *cursor;
287 if( entry.data == nullptr )
288 {
289 auto it= types.Find(A_CHAR("S"));
290 ALIB_ASSERT_ERROR( it != types.end(), "VARIABLES",
291 "Variable type 'S' not registered. This usually cannot happen." )
292 auto* meta= entry.meta= *it;
293
294 // initialize fields
295 entry.data = reinterpret_cast<detail::VDATA*>(Pool().Alloc( meta->size(), alignof(detail::VDATA)));
296 meta->construct( cursor->data, Pool );
297 entry.priority= priority;
298 }
299
300 if( entry.priority <= priority )
301 {
302 entry.priority = priority;
303 entry.declaration= reinterpret_cast<const Declaration*>( escaper );
304 (Variable(cursor))= value;
305 }
306}
307
308// #############################################################################################
309// Declaration replacement allocation
310// #############################################################################################
311const Declaration* Configuration::StoreDeclaration( const Declaration* orig, const Box& replacements )
312{
313 //------------- prepare replacement -------------
314 String128 replace;
315
316 const Box* replacementPtr;
317 integer qtyReplacements;
318 if ( replacements.IsArrayOf<Box>() )
319 {
320 replacementPtr = replacements.UnboxArray<Box>();
321 qtyReplacements= replacements.UnboxLength();
322 }
323 else if ( replacements.IsType<BoxesHA*>() )
324 {
325 const auto* boxes= replacements.Unbox<BoxesHA*>();
326 replacementPtr = boxes->data();
327 qtyReplacements= boxes->Size();
328 }
329 else if ( replacements.IsType<BoxesMA*>() )
330 {
331 const auto* boxes= replacements.Unbox<BoxesMA*>();
332 replacementPtr = boxes->data();
333 qtyReplacements= boxes->Size();
334 }
335 else if ( replacements.IsType<BoxesPA*>() )
336 {
337 const auto* boxes= replacements.Unbox<BoxesPA*>();
338 replacementPtr = boxes->data();
339 qtyReplacements= boxes->Size();
340 }
341 else
342 {
343 replacementPtr = &replacements;
344 qtyReplacements= 1;
345 }
346
347 //------------- replace name -------------
348 String256 bufName; ALIB_DBG( bufName .DbgDisableBufferReplacementWarning() );
349 bufName << orig->EnumElementName;
350
351 for ( integer replCnt= 0; replCnt< qtyReplacements ; ++replCnt )
352 if ( !replacementPtr->IsType<void>() )
353 {
354 String64 search("%"); search._( replCnt + 1 );
355 replace.Reset( *( replacementPtr + replCnt) );
356 bufName .SearchAndReplace( search, replace );
357 }
358
359 // search in hashtable
360 {
361 auto it= replacementDeclarations.Find( bufName );
362 if( it != replacementDeclarations.end() )
363 return *it;
364 }
365
366 // not found, we have to create a new one
367 String1K bufComments; ALIB_DBG( bufComments .DbgDisableBufferReplacementWarning() );
368 String128 bufDefaultValue; ALIB_DBG( bufDefaultValue.DbgDisableBufferReplacementWarning() );
369 bufComments << orig->comments;
370 if ( orig->defaultValue.IsNotNull() )
371 bufDefaultValue << orig->defaultValue;
372
373 for ( integer replCnt= 0; replCnt< qtyReplacements ; ++replCnt )
374 if ( !replacementPtr->IsType<void>() )
375 {
376 String64 search("%"); search._( replCnt + 1 );
377 replace.Reset( *( replacementPtr + replCnt) );
378 bufComments .SearchAndReplace( search, replace );
379 bufDefaultValue.SearchAndReplace( search, replace );
380 }
381
382 // --------------------- create copy ---------------------
383 Declaration* result = GetAllocator()().New<Declaration>();
384 result->EnumElementName.Allocate(GetAllocator(), bufName);
385 result->typeName = orig->typeName;
386 result->defaultValue .Allocate(GetAllocator(), bufDefaultValue);
387 result->comments .Allocate(GetAllocator(), bufComments);
388 replacementDeclarations.EmplaceUnique(result);
389
390 return result;
391}
392
393// #############################################################################################
394// Other tools
395// #############################################################################################
396std::pair<bool,int8_t> Configuration::ParseBooleanToken( const String& pValue )
397{
398 int8_t index= 0;
399 Substring value(pValue);
400 if( value.Trim().IsEmpty() )
401 return {false, int8_t(-1)};
402
403 for ( auto& it : BooleanTokens )
404 {
405 if ( it.first .Match(value) ) return {false, index};
406 if ( it.second.Match(value) ) return {true , index};
407 ++index;
408 }
409 return {false, int8_t(-1)};
410}
411
412AString& Configuration::WriteBooleanToken( bool value, int8_t idxRequested, AString& dest )
413{
414 // find the right pair of tokens
415 if(idxRequested < 0)
416 idxRequested= 0;
417
418 auto it= BooleanTokens.begin();
419 for( int8_t index=0 ; index < idxRequested && it != BooleanTokens.end() ; ++index )
420 ++it;
421
422 if( it == BooleanTokens.end() )
423 it= BooleanTokens.begin();
424
425 // use first (false) or second (true) to write
426 (!value ? it->first : it->second).GetExportName( dest );
427 return dest;
428}
429
431{
432DOX_MARKER( [DOX_VARIABLES_DELETE_SAMPLE] )
433// get root node of the tree
434Cursor cs= Root();
435
436// try to walk the givedn path. If a remainder of the string exits, exit.
437if( cs.GoTo(path).IsNotEmpty() )
438 return false;
439
440// now delete the subtree, including the node the cursor represents
441cs.Delete();
442
443// that's it
444return true;
445DOX_MARKER( [DOX_VARIABLES_DELETE_SAMPLE] )
446}
447
448// #################################################################################################
449// Implementation of parsing methods of built-in record types.
450// #################################################################################################
456
457}} // namespace [alib::variables]
458
459
460// #################################################################################################
461// struct AppendableTraits<Variable>
462// #################################################################################################
463#if !DOXYGEN
464
465namespace alib::strings {
466
467void AppendableTraits<variables::Variable,nchar, lang::HeapAllocator>::operator()( TAString<nchar, lang::HeapAllocator>& target, const variables::Variable& variable )
468{
469#if ALIB_CHARACTERS_WIDE
470 String256 name;
471 variable.Name(name);
472 target << name;
473#else
474 variable.Name(target);
475#endif
476
477}
478
480{
481#if ALIB_CHARACTERS_WIDE
482 variable.Name(target);
483#else
484 String256 name;
485 variable.Name(name);
486 target << name;
487#endif
488}
489
490
491} // namespace [alib::strings]
492#endif // DOXYGEN
493
494
Placeholder data
The data that we encapsulate.
Definition box.inl:40
bool IsArrayOf() const
Definition box.inl:587
bool IsType() const
TValue Unbox() const
Definition box.inl:635
TElementType * UnboxArray() const
Definition box.inl:867
integer UnboxLength() const
Definition box.inl:893
StringTree(AllocatorType &allocator, CharacterType pathSeparator)
void InsertPlugin(ConfigurationPlugin *plugin, lang::Responsibility responsibility=lang::Responsibility::KeepWithSender)
Definition plugins.inl:129
void free(void *mem, size_t size)
ALIB_DLL integer SearchAndReplace(TChar needle, TChar replacement, integer startIdx=0, integer endIdx=strings::MAX_LEN)
TAString & _(const TAppendable &src)
void DbgDisableBufferReplacementWarning()
Definition tastring.inl:245
constexpr bool IsEmpty() const
Definition string.inl:367
TChar CharAtStart() const
Definition string.inl:440
integer IndexOf(TChar needle, integer startIdx=0) const
Definition string.inl:844
constexpr bool IsNotNull() const
Definition string.inl:357
void Allocate(TAllocator &allocator, const TString< TChar > &copy)
Definition string.inl:1870
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
Definition string.inl:386
constexpr bool IsNull() const
Definition string.inl:352
bool StartsWith(const TString &needle) const
Definition string.inl:772
TSubstring & Trim(const TCString< TChar > &whiteSpaces=CStringConstantsTraits< TChar >::DefaultWhitespaces())
ALIB_DLL bool DeletePath(const String &path)
ALIB_DLL void notifyListeners(int event, const Variable &variable, const String &variablePath, Priority previousPriority)
ALIB_DLL AString & WriteBooleanToken(bool value, int8_t index, AString &dest)
ConfigurationPlugin * cliPlugin
A default plugin created and inserted with construction.
TypesHashTable types
A hashtable for the registered types. Key is the type name, value is the VMeta singleton.
ALIB_DLL const Declaration * StoreDeclaration(const Declaration *orig, const Box &replacements)
ALIB_DLL int MonitorStop(ConfigurationListener *listener)
ConfigurationPlugin * environmentPlugin
A default plugin created and inserted with construction.
friend class Variable
Type alias in namespace alib.
ALIB_DLL ~Configuration()
Destructor.
ALIB_DLL void presetImportString(const String &name, const String &value, const StringEscaper *escaper, Priority priority)
List< MonoAllocator, ListenerRecord > listeners
The list of registered listeners.
ALIB_DLL Configuration(MonoAllocator &allocator, lang::CreateDefaults createDefaults=lang::CreateDefaults::Yes)
List< MonoAllocator, std::pair< Token, Token >, Recycling::None > BooleanTokens
ALIB_DLL std::pair< bool, int8_t > ParseBooleanToken(const String &src)
ALIB_DLL void registerListener(ConfigurationListener *listener, lang::ContainerOp insertOrRemove, int event, const Variable *variable, const StringTree::Cursor *subTree, const String &variableName, const String &pathPrefix, const String &pathSubstring)
VMeta * meta
The virtual handler instance for this variable.
VDATA * data
The list hook of values.
#define ALIB_BOXING_VTABLE_DEFINE(TMapped, Identifier)
#define A_CHAR(STR)
#define ALIB_WARNING(domain,...)
Definition alib.inl:1046
#define ALIB_ASSERT_WARNING(cond, domain,...)
Definition alib.inl:1050
#define ALIB_DBG(...)
Definition alib.inl:836
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1049
ContainerOp
Denotes standard container operations.
@ Insert
Denotes insertions.
CreateDefaults
Denotes whether default entities should be created or not.
@ Yes
Create default values.
void LoadResourcedTokens(camp::Camp &module, const NString &resourceName, strings::util::Token *target, int dbgSizeVerifier, character outerSeparator=',', character innerSeparator=' ')
VData< void * > VDATA
Definition vmeta.inl:45
containers::detail::StringTreeBase< MonoAllocator, Entry, ConfigNodeHandler, Recycling::Private > TTree
A shortcut to the base class of the base class of class Configuration.
strings::util::StringEscaper StringEscaper
Type alias in namespace alib.
Definition escaper.inl:199
strings::util::Token Token
Type alias in namespace alib.
Definition token.inl:401
LocalString< 256 > String256
Type alias name for TLocalString<character,256>.
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace alib.
camp::Basecamp BASECAMP
The singleton instance of ALib Camp class Basecamp.
Definition basecamp.cpp:81
LocalString< 128 > String128
Type alias name for TLocalString<character,128>.
LocalString< 64 > String64
Type alias name for TLocalString<character,64>.
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
boxing::TBoxes< PoolAllocator > BoxesPA
Type alias in namespace alib.
Definition boxes.inl:248
monomem::TMonoAllocator< lang::HeapAllocator > MonoAllocator
boxing::Box Box
Type alias in namespace alib.
Definition box.inl:1216
LocalString< 1024 > String1K
Type alias name for TLocalString<character,1024>.
boxing::TBoxes< MonoAllocator > BoxesMA
Type alias in namespace alib.
Definition boxes.inl:245
strings::TString< character > String
Type alias in namespace alib.
Definition string.inl:2381
strings::TAString< character, PoolAllocator > AStringPA
Type alias in namespace alib.
boxing::TBoxes< lang::HeapAllocator > BoxesHA
Type alias in namespace alib.
Definition boxes.inl:241
strings::TSubstring< character > Substring
Type alias in namespace alib.
String EnumElementName
The name of the enum element.
Definition records.inl:466
static ALIB_DLL void Get(String &result, bool isLastField=false)
void operator()(TAString< TChar > &target, const TAppendable &src)
Event
The type of change that imposes the notification of a listener.
Record used to manage registered listeners.
ALIB_DLL void Parse()
Implementation of EnumRecordPrototype::Parse.
int Priority
The precedence of an operator in respect to other binary operators.
virtual size_t size()=0
virtual void destruct(void *memory, PoolAllocator &pool)=0
static ALIB_DLL void FreeNode(TTree &tree, TTree::Node &node)