ALib C++ Library
Library Version: 2511 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;
21 import ALib.EnumRecords.Bootstrap;
22 import ALib.Boxing;
23# if ALIB_RESOURCES
24 import ALib.Resources;
25# endif
26 import ALib.Variables.Plugins;
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( typename TTree::Node& node, TTree& tree ) {
66 // delete node name
67 auto& cfg= static_cast<Configuration&>(tree);
68 cfg.Pool.free( const_cast<TTree::CharacterType*>(node.name.storage.Buffer()),
69 size_t(node.name.storage.Length()) * sizeof(TTree::CharacterType) );
70
71 // delete vdata
72 Entry& entry= node.data;
73 if( entry.data ) {
74 entry.meta->destruct(entry.data, cfg.Pool);
75 cfg.Pool().Free(entry.data, entry.meta->size() );
76} }
77} // namespace detail
78
79//==================================================================================================
80// Implementation of class Configuration
81//==================================================================================================
83: StringTree ( allocator, '/')
84, Pool ( allocator )
85, types ( allocator )
86, replacementDeclarations( allocator )
87, listeners ( allocator )
88, BooleanTokens ( allocator ) {
89 DbgSetDCSName("Configuration");
90
91 // Register built-in types
99
100 // read boolean false/true values from resources
101 if( createDefaults == lang::CreateDefaults::Yes) {
102 std::array<Token, 10> tokenBuf;
103 #if ALIB_CAMP
104 strings::util::LoadResourcedTokens( BASECAMP, "CFGBTF", tokenBuf.data() ALIB_DBG(, 10) );
105 for (size_t i = 0; i < tokenBuf.size(); i+= 2 )
106 BooleanTokens.emplace_back(tokenBuf[i], tokenBuf[i+1]);
107 #else
108 BooleanTokens.emplace_back(Token("False;I;1"), Token("True;I;1"));
109 BooleanTokens.emplace_back(Token( "0;I;1"), Token( "1;I;1"));
110 BooleanTokens.emplace_back(Token( "No;I;1"), Token( "Yes;I;1"));
111 BooleanTokens.emplace_back(Token( "Off;I;3"), Token( "On;I;2"));
112 BooleanTokens.emplace_back(Token( "---;I;1"), Token( "OK;I;2" ));
113 #endif
114
115 // create default plugins
116 auto& ma= GetAllocator();
117 InsertPlugin( environmentPlugin= ma().New<EnvironmentVariablesPlugin>(ma) );
118 InsertPlugin( cliPlugin= ma().New< CLIVariablesPlugin>(ma) );
119} }
120
122 // we have to delete all nodes before the invocation of the base destructor, because
123 // this would use our pool allocator on existing nodes (which is then destructed already).
124 base::Clear();
125
126 ALIB_ASSERT_WARNING( listeners.empty(), "VARIABLES",
127 "{} remaining registered listeners when destruction configuration.", listeners.size() )
128}
129
131 lang::ContainerOp insertOrRemove,
132 int event,
133 const Variable* variable,
134 const StringTree::Cursor* subTree,
135 const String& variableName,
136 const String& pathPrefixGiven,
137 const String& pathSubstring ) {
138 // checks
139 ALIB_ASSERT_ERROR( variable==nullptr || variable->IsDeclared() , "VARIABLES", "Given variable not declared.")
140 ALIB_ASSERT_ERROR( variable==nullptr || &variable->AsCursor().Tree() == this , "VARIABLES", "Given variable does not belong to this configuration.")
141 ALIB_ASSERT_ERROR( subTree ==nullptr || subTree->IsValid() , "VARIABLES", "Invalid cursor given." )
142 ALIB_ASSERT_ERROR( subTree ==nullptr || &subTree ->Tree() == this , "VARIABLES", "Given cursor does not belong to this configuration.")
143
144 // remove / from path
145 const String pathPrefix= pathPrefixGiven.CharAtStart() == StringTree::Separator()
146 ? pathPrefixGiven.Substring(1)
147 : pathPrefixGiven;
148
149 //------------------------------------------ registration ----------------------------------------
150 if( insertOrRemove == lang::ContainerOp::Insert) {
151 listeners.emplace_back( ListenerRecord{ listener,
152 event,
153 variable ? variable->AsCursor().Export() : ConstCursorHandle(),
154 subTree ? subTree-> Export() : ConstCursorHandle(),
157 AStringPA(Pool) } );
158 listeners.back().variableName << variableName;
159 listeners.back().pathPrefix << pathPrefix;
160 listeners.back().pathSubstring<< pathSubstring;
161
162 return;
163 }
164
165 //----------------------------------------------- de ---------------------------------------------
166 for (auto it= listeners.begin() ; it != listeners.end() ; ++it )
167 if( it->listener == listener
168 && it->event == event
169 && it->variable == ( variable ? variable->AsCursor().Export() : ConstCursorHandle() )
170 && it->subTree == ( subTree ? subTree ->Export() : ConstCursorHandle() )
171 && it->variableName .Equals( variableName )
172 && it->pathPrefix .Equals( pathPrefix )
173 && it->pathSubstring.Equals( pathSubstring ) )
174 {
175 (void) listeners.erase( it );
176 return;
177 }
178
179 ALIB_WARNING( "VARIABLES", "Listener with matching set of parameters not found with "
180 "deregistration." )
181
182} // Configuration::registerListener
183
184
186 // checks
187 ALIB_ASSERT_ERROR( listener!=nullptr, "VARIABLES", "Given listener is nullptr.")
188
189 //----------------------------------------------- de ---------------------------------------------
190 int cnt= 0;
191 for (auto it= listeners.begin() ; it != listeners.end() ; )
192 if( it->listener == listener ) {
193 it= listeners.erase( it );
194 ++cnt;
195 }
196 else
197 ++it;
198
199 return cnt;
200} // Configuration::registerListener
201
203 const Variable& variable,
204 const String& variablePathGiven,
205 Priority previousPriority ) {
206 String256 variablePathBuffer;
207 const String* variablePath= &variablePathGiven;
208 for (auto it= listeners.begin() ; it != listeners.end() ; ++it )
209 if( event == it->event ) {
210 // if needed generate variable path
211 if( variablePath->IsEmpty()
212 && ( it->variableName .IsNotEmpty()
213 || it->pathPrefix .IsNotEmpty()
214 || it->pathSubstring.IsNotEmpty() ) )
215 {
216 variablePathBuffer << variable;
217 variablePath= &variablePathBuffer;
218 }
219
220 if( ( it->variable .IsValid() && ( it->variable == variable.AsCursor().Export() ) )
221 || ( it->subTree .IsValid() && ( variable.AsCursor().Distance( ImportCursor(it->subTree) ) >= 0 ) )
222 || ( it->variableName .IsNotEmpty() && it->variableName.Equals(variable.AsCursor().Name()) )
223 || ( it->pathPrefix .IsNotEmpty() && variablePath->StartsWith(it->pathPrefix) )
224 || ( it->pathSubstring.IsNotEmpty() && variablePath->IndexOf(it->pathSubstring) >= 0 )
225 )
226 {
227 it->listener->Notify( variable,
229 previousPriority );
230 } }
231} // Configuration::notifyListeners
232
233
234void Configuration::presetImportString( const String& name, const String& value,
235 const StringEscaper* escaper, Priority priority ) {
236 auto cursor= Root();
237 cursor.GoToCreateChildIfNotExistent(A_CHAR("$PRESETS"));
238
239 // nullptr given? Delete a preset.
240 if( value.IsNull() ) {
241 if( cursor.GoTo(name).IsNotEmpty() )
242 return; // nothing was previously set
243
244 auto& entry= *cursor;
245 if( entry.priority > priority )
246 return; // do not delete if lower priority!
247
248 if( entry.data ) {
249 entry.meta->destruct( entry.data, Pool );
250 Pool().Free( entry.data, entry.meta->size() );
251 entry.meta= nullptr;
252 entry.data= nullptr;
253 entry.priority= Priority::NONE;
254 }
255
256 // delete this node, but only if it is a leaf.
257 if( !cursor.HasChildren() )
258 cursor.Delete();
259
260 return;
261 }
262
263
264 // set-mode
265 cursor.GoToCreatedPathIfNotExistent(name);
266
267 // create a fake variable. We do not do this using the variable class, because this would
268 // raise assertions.
269 auto& entry= *cursor;
270 if( entry.data == nullptr ) {
271 auto it= types.Find(A_CHAR("S"));
272 ALIB_ASSERT_ERROR( it != types.end(), "VARIABLES",
273 "Variable type 'S' not registered. This usually cannot happen." )
274 auto* meta= entry.meta= *it;
275
276 // initialize fields
277 entry.data = reinterpret_cast<detail::VDATA*>(Pool().Alloc( meta->size(), alignof(detail::VDATA)));
278 meta->construct( cursor->data, Pool );
279 entry.priority= priority;
280 }
281
282 if( entry.priority <= priority ) {
283 entry.priority = priority;
284 entry.declaration= reinterpret_cast<const Declaration*>( escaper );
285 (Variable(cursor))= value;
286} }
287
288//##################################################################################################
289// Declaration replacement allocation
290//##################################################################################################
292 const Box& replacements ) {
293 //-------------------------------------- prepare replacement -------------------------------------
294 String128 replace;
295
296 const Box* replacementPtr;
297 integer qtyReplacements;
298 if ( replacements.IsArrayOf<Box>() ) {
299 replacementPtr = replacements.UnboxArray<Box>();
300 qtyReplacements= replacements.UnboxLength();
301 }
302 else if ( replacements.IsType<Boxes*>() ) {
303 const auto* boxes= replacements.Unbox<Boxes*>();
304 replacementPtr = boxes->data();
305 qtyReplacements= boxes->Size();
306 }
307 else if ( replacements.IsType<BoxesMA*>() ) {
308 const auto* boxes= replacements.Unbox<BoxesMA*>();
309 replacementPtr = boxes->data();
310 qtyReplacements= boxes->Size();
311 }
312 else if ( replacements.IsType<BoxesPA*>() ) {
313 const auto* boxes= replacements.Unbox<BoxesPA*>();
314 replacementPtr = boxes->data();
315 qtyReplacements= boxes->Size();
316 } else {
317 replacementPtr = &replacements;
318 qtyReplacements= 1;
319 }
320
321 //------------------------------------------ replace name ----------------------------------------
322 String256 bufName; ALIB_DBG( bufName .DbgDisableBufferReplacementWarning() );
323 bufName << orig->EnumElementName;
324
325 for ( integer replCnt= 0; replCnt< qtyReplacements ; ++replCnt )
326 if ( !replacementPtr->IsType<void>() ) {
327 String64 search("%"); search._( replCnt + 1 );
328 replace.Reset( *( replacementPtr + replCnt) );
329 bufName .SearchAndReplace( search, replace );
330 }
331
332 // search in hashtable
333 {
334 auto it= replacementDeclarations.Find( bufName );
335 if( it != replacementDeclarations.end() )
336 return *it;
337 }
338
339 // not found, we have to create a new one
340 String1K bufComments; ALIB_DBG( bufComments .DbgDisableBufferReplacementWarning() );
341 String128 bufDefaultValue; ALIB_DBG( bufDefaultValue.DbgDisableBufferReplacementWarning() );
342 bufComments << orig->comments;
343 if ( orig->defaultValue.IsNotNull() )
344 bufDefaultValue << orig->defaultValue;
345
346 for ( integer replCnt= 0; replCnt< qtyReplacements ; ++replCnt )
347 if ( !replacementPtr->IsType<void>() ) {
348 String64 search("%"); search._( replCnt + 1 );
349 replace.Reset( *( replacementPtr + replCnt) );
350 bufComments .SearchAndReplace( search, replace );
351 bufDefaultValue.SearchAndReplace( search, replace );
352 }
353
354 //------------------------------------------ create copy -----------------------------------------
355 Declaration* result = GetAllocator()().New<Declaration>();
356 result->EnumElementName.Allocate(GetAllocator(), bufName);
357 result->typeName = orig->typeName;
358 result->defaultValue .Allocate(GetAllocator(), bufDefaultValue);
359 result->comments .Allocate(GetAllocator(), bufComments);
360 replacementDeclarations.EmplaceUnique(result);
361
362 return result;
363}
364
365//##################################################################################################
366// Other tools
367//##################################################################################################
368std::pair<bool,int8_t> Configuration::ParseBooleanToken( const String& pValue ) {
369 int8_t index= 0;
370 Substring value(pValue);
371 if( value.Trim().IsEmpty() )
372 return {false, int8_t(-1)};
373
374 for ( auto& it : BooleanTokens ) {
375 if ( it.first .Match(value) ) return {false, index};
376 if ( it.second.Match(value) ) return {true , index};
377 ++index;
378 }
379 return {false, int8_t(-1)};
380}
381
382AString& Configuration::WriteBooleanToken( bool value, int8_t idxRequested, AString& dest ) {
383 // find the right pair of tokens
384 if(idxRequested < 0)
385 idxRequested= 0;
386
387 auto it= BooleanTokens.begin();
388 for( int8_t index=0 ; index < idxRequested && it != BooleanTokens.end() ; ++index )
389 ++it;
390
391 if( it == BooleanTokens.end() )
392 it= BooleanTokens.begin();
393
394 // use first (false) or second (true) to write
395 (!value ? it->first : it->second).GetExportName( dest );
396 return dest;
397}
398
400DOX_MARKER( [DOX_VARIABLES_DELETE_SAMPLE] )
401// get root node of the tree
402Cursor cs= Root();
403
404// try to walk the givedn path. If a remainder of the string exits, exit.
405if( cs.GoTo(path).IsNotEmpty() )
406 return false;
407
408// now delete the subtree, including the node the cursor represents
409cs.Delete();
410
411// that's it
412return true;
413DOX_MARKER( [DOX_VARIABLES_DELETE_SAMPLE] )
414}
415
416//##################################################################################################
417// Implementation of parsing methods of built-in record types.
418//##################################################################################################
424
425}} // namespace [alib::variables]
426
427
428//##################################################################################################
429// struct AppendableTraits<Variable>
430//##################################################################################################
431#if !DOXYGEN
432
433namespace alib::strings {
434
436 TAString<nchar, lang::HeapAllocator>& target, const variables::Variable& variable ) {
437#if ALIB_CHARACTERS_WIDE
438 String256 name;
439 variable.Name(name);
440 target << name;
441#else
442 variable.Name(target);
443#endif
444
445}
446
448 TAString<wchar, lang::HeapAllocator>& target, const variables::Variable& variable ) {
449#if ALIB_CHARACTERS_WIDE
450 variable.Name(target);
451#else
452 String256 name;
453 variable.Name(name);
454 target << name;
455#endif
456}
457
458
459} // namespace [alib::strings]
460#endif // DOXYGEN
Placeholder data
The data that we encapsulate.
Definition box.inl:40
bool IsArrayOf() const
Definition box.inl:557
bool IsType() const
TValue Unbox() const
Definition box.inl:595
TElementType * UnboxArray() const
Definition box.inl:817
integer UnboxLength() const
Definition box.inl:842
StringTree(AllocatorType &allocator, CharacterType pathSeparator)
void InsertPlugin(ConfigurationPlugin *plugin, lang::Responsibility responsibility=lang::Responsibility::KeepWithSender)
Definition plugins.inl:124
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:244
constexpr bool IsEmpty() const
Definition string.inl:365
TChar CharAtStart() const
Definition string.inl:433
integer IndexOf(TChar needle, integer startIdx=0) const
Definition string.inl:815
constexpr bool IsNotNull() const
Definition string.inl:355
void Allocate(TAllocator &allocator, const TString< TChar > &copy)
Definition string.inl:1745
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
Definition string.inl:384
constexpr bool IsNull() const
Definition string.inl:350
bool StartsWith(const TString &needle) const
Definition string.inl:751
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.
ListMA< std::pair< Token, Token >, Recycling::None > BooleanTokens
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.
ListMA< ListenerRecord > listeners
The list of registered listeners.
ALIB_DLL ~Configuration()
Destructor.
ALIB_DLL void presetImportString(const String &name, const String &value, const StringEscaper *escaper, Priority priority)
ALIB_DLL Configuration(MonoAllocator &allocator, lang::CreateDefaults createDefaults=lang::CreateDefaults::Yes)
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:1063
#define ALIB_ASSERT_WARNING(cond, domain,...)
Definition alib.inl:1067
#define ALIB_DBG(...)
Definition alib.inl:853
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1066
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:183
strings::util::Token Token
Type alias in namespace alib.
Definition token.inl:398
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>.
boxing::TBoxes< lang::HeapAllocator > Boxes
Type alias in namespace alib.
Definition boxes.inl:189
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
boxing::TBoxes< PoolAllocator > BoxesPA
Type alias in namespace alib.
Definition boxes.inl:196
monomem::TMonoAllocator< lang::HeapAllocator > MonoAllocator
boxing::Box Box
Type alias in namespace alib.
Definition box.inl:1149
LocalString< 1024 > String1K
Type alias name for TLocalString<character,1024>.
boxing::TBoxes< MonoAllocator > BoxesMA
Type alias in namespace alib.
Definition boxes.inl:193
strings::TString< character > String
Type alias in namespace alib.
Definition string.inl:2189
strings::TAString< character, PoolAllocator > AStringPA
Type alias in namespace alib.
strings::TSubstring< character > Substring
Type alias in namespace alib.
String EnumElementName
The name of the enum element.
Definition records.inl:430
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::Node &node, TTree &tree)