ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
Loading...
Searching...
No Matches
inifile.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2024 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
8
9#if !defined(ALIB_DOX)
10#if !defined (HPP_ALIB_CONFIG_INI_FILE)
12#endif
13
14
15#if !defined(HPP_ALIB_CONFIG_CONFIG)
16# include "alib/config/config.hpp"
17#endif
18#if !defined(HPP_ALIB_CONFIG_CONFIGURATION)
20#endif
21
22#if !defined (HPP_ALIB_CAMP_DIRECTORY)
24#endif
25#if !defined (HPP_ALIB_CAMP_PROCESSINFO)
27#endif
28#if !defined (HPP_ALIB_STRINGS_UTIL_TOKENIZER)
30#endif
31#if !defined (HPP_ALIB_STRINGS_UTIL_SPACES)
33#endif
34#if !defined (HPP_ALIB_LANG_FORMAT_PARAGRAPHS)
36#endif
37#if !defined (HPP_ALIB_COMPATIBILITY_STD_STRINGS_IOSTREAM)
39#endif
40
41#if !defined (_GLIBCXX_FSTREAM) && !defined(_FSTREAM_)
42# include <fstream>
43#endif
44#if defined(_MSC_VER) && !defined(_ALGORITHM_)
45# include <algorithm>
46#endif
47#endif // !defined(ALIB_DOX)
48
49
50namespace alib { namespace config {
51
52// #################################################################################################
53// static/globals
54// #################################################################################################
56
57// #################################################################################################
58// class InMemoryPlugin::Entry
59// #################################################################################################
60void IniFile::ToVariable( Entry& entry, Variable& variable ) const
61{
62 // if this had been done before, use base method
63 if ( entry.Value.IsNotEmpty() )
64 {
65 InMemoryPlugin::ToVariable( entry, variable );
66 return;
67 }
68
69 // store delim and comment
70 entry.Delim= variable.Delim();
71 variable.ReplaceComments( entry.Comments );
72
73 // parse the INI file content
74
75 //----- remove INI-File specific from raw value -----
76 String512 raw;
78 raw._( entry.RawValue );
79
80 // remove '='
81 raw.TrimStart();
82 if ( raw.CharAtStart() == '=' )
83 raw.DeleteStart(1).TrimStart();
84 else
85 {
86 ALIB_WARNING( "CONFIG", "No equal sign in INI-file variable {!Q}.", variable.Fullname() )
87 }
88
89 // remove "\\n"
90 integer startIdx= 0;
91 while ( (startIdx= raw.IndexOf( '\n', startIdx )) >= 0 )
92 {
93 // find \\n and trim around this location
94 int delLen= 2;
95 if ( raw.CharAt( --startIdx) == '\r' )
96 {
97 delLen= 3;
98 --startIdx;
99 }
100 ALIB_ASSERT( raw.CharAt(startIdx) == '\\' )
101 raw.Delete( startIdx, delLen );
102
103 startIdx= raw.TrimAt( startIdx );
104 if( startIdx >= raw.Length() )
105 break;
106
107 // remove endquote of first line and start quote of second
108 if( startIdx >0 && raw[startIdx-1] == '"' && raw[startIdx] == '"' )
109 {
110 --startIdx;
111 raw.Delete( startIdx, 2);
112 }
113
114
115 // if now the next value is starting with a comment symbol, we remove to the next \n
116 for(;;)
117 {
118 character c= raw.CharAt( startIdx );
119 if ( c != '#'
120 && c != ';'
121 && ( c != '/' || raw.CharAt( startIdx + 1 ) != '/' ) )
122 break;
123
124 integer idx= raw.IndexOf( '\n', startIdx );
125 if (idx < 0 ) idx= raw.Length();
126 raw.Delete( startIdx, idx - startIdx + 1 );
127 if( startIdx >= raw.Length() )
128 break;
129 startIdx= raw.TrimAt( startIdx );
130 }
131 }
132
133 // now convert
134 StringConverter->LoadFromString( variable, raw );
135
136 // copy the parsed values back to our entry
137 auto varSize= variable.Size();
138 if( varSize > 0 )
139 {
140 auto valueIt= entry.SetValueCount( varSize );
141 entry.Value.Reset( variable.GetString(0) );
142 int idx = 1;
143 while( valueIt != entry.AdditionalValues.end() )
144 {
145 valueIt->Reset( variable.GetString( idx++ ) );
146 ++valueIt;
147 }
148 }
149}
150
151void IniFile::FromVariable( Entry& entry, Variable& variable ) const
152{
153 entry.RawValue.Reset();
154 InMemoryPlugin::FromVariable( entry, variable );
155}
156
157
158// #################################################################################################
159// Constructor/Destructor
160// #################################################################################################
161IniFile::IniFile( const String& fileName )
163, LinesWithReadErrors( allocator )
164{
165 // don't read anything
166 if ( fileName.StartsWith( A_CHAR("*") ) )
167 {
168 AutoSave= false;
169 return;
170 }
171
172 if ( fileName.CharAtStart() != '/'
173 && fileName.CharAtStart() != '.'
174 #if defined(_WIN32)
175 && fileName.CharAtStart() != '\\'
176 && (fileName.Length() < 2 || fileName.CharAt(1) != ':' )
177 #endif
178 )
179 {
181 FileName._( dir.Path )
183 }
184
185 if ( fileName.IsEmpty() )
186 {
188
189 #if defined(_WIN32)
190 if( FileName.EndsWith( A_CHAR(".exe") ) )
191 FileName.DeleteEnd( 4 );
192 #endif
193
195 }
196 else
197 FileName << fileName;
198
199 ReadFile();
200
201}
202
209
210
211// #################################################################################################
212// Read/Write files
213// #################################################################################################
214
215//! @cond NO_DOX
216bool startsWithCommentSymbol( Substring& subs );
217bool startsWithCommentSymbol( Substring& subs )
218{
219 integer i= String(A_CHAR("#;/")).IndexOfOrLength( subs.CharAtStart() );
220 return i < 2
221 || ( i == 2 && subs.Length() > 1 && subs[1] == '/' );
222}
223//! @endcond
224
226{
227 Clear();
229
230 // open file
231 ALIB_STRINGS_TO_NARROW(FileName, nFileName, 1024)
232 std::ifstream file( nFileName );
233
234
235 if ( !file.is_open() )
236 {
237 int errNo= errno;
238
239 // file does not exist ?
240 if ( errNo == UnderlyingIntegral(SystemErrors::enoent) )
241 return;
242
243 // other errors: throw
246 A_CHAR("INI-"), FileName );
247 throw e;
248 }
249
250 AString lineAS;
251 String128 name;
252 AString value;
253 AString comments;
254 Section* actSection= &sections.Front();
255
256 int lineNo= 0;
257 bool fileHeaderRead= false;
258
259 StringReader reader;
260 reader.SetStream( &file );
261
262 String16 separatorCharacters( '=' );
263 separatorCharacters._( DefaultWhitespaces() );
264
265 while( !reader.IsEOF() )
266 {
267 reader.Read( lineAS );
268 ++lineNo;
269 Substring line( lineAS );
270
271 bool isEmpty= line.Trim().IsEmpty();
272 bool isCommentLine= startsWithCommentSymbol( line );
273
274 if ( isCommentLine )
275 {
276 if ( comments.IsNotEmpty() )
277 comments.NewLine();
278 comments._(line);
279 continue;
280 }
281
282 // still processing file header?
283 if ( !fileHeaderRead )
284 {
285 fileHeaderRead= true;
286 FileComments= comments;
287 comments.Reset();
288 }
289
290 // empty line?
291 if ( isEmpty )
292 {
293 if ( comments.IsNotEmpty() )
294 comments.NewLine();
295 continue;
296 }
297
298 // section line
299 if ( line.ConsumeChar('[') )
300 {
301 fileHeaderRead= true;
302
303 if( !line.ConsumeCharFromEnd( ']' ) )
305
306 actSection= SearchOrCreateSection( line ).first;
307 actSection->Comments << comments;
308
309 comments.Reset();
310 continue;
311 }
312
313 // Variable line
314 value.Reset();
315 integer idx= line.IndexOfAny<lang::Inclusion::Include>( separatorCharacters );
316 if( idx < 0 )
317 {
318 name.Reset( line );
319 line.Clear();
320 }
321 else
322 {
323 name.Reset()._( line, 0, idx );
324 line.ConsumeChars( idx );
325 value._(line);
326 }
327
328 // read continues as long as lines end with '\' (must not be '\\')
329 while ( line.CharAtEnd() == '\\'
330 && (line.Length() == 1 || line.CharAt<false>( line.Length() -2 ) != '\\' ) )
331 {
332 value.NewLine();
333 reader.Read( lineAS );
334 if ( reader.IsEOF() )
335 {
336 // last line of the file ended with '\' !
337 line.Clear();
338 break;
339 }
340 lineAS.TrimEnd();
341 line= lineAS;
342
343 value._( lineAS );
344 }
345
346 // insert entry with raw value
347 {
348 auto* entry= searchEntry( actSection->Name(), name );
349 if( entry == nullptr)
350 entry= createEntry( actSection, name );
351 entry->Value .Reset();
352 entry->AdditionalValues.Reset();
353 entry->Comments .Reset( comments );
354 entry->RawValue .Reset( value );
355 }
356
357 comments.Reset();
358
359 }
360 file.close();
361}
362
363void IniFile::writeComments( StringWriter& writer, const AString& comments )
364{
365 // is empty when trimmed?
366 if ( Substring(comments).Trim().IsEmpty() )
367 return;
368
369 // tokenize by NEWLINE character
370 Tokenizer tknzr( comments, '\n' );
371 tknzr.TrimChars= A_CHAR(" \r\t"); // \n is not a whitespace
372
373 while( tknzr.Next(lang::Whitespaces::Keep).IsNotNull() )
374 {
375 if ( !startsWithCommentSymbol( tknzr.Actual ) )
376 writer.Write( DefaultCommentPrefix );
377 writer.Write( tknzr.Actual );
378 *writer.GetStream() << std::endl;
379 }
380
382}
383
384//! @cond NO_DOX
385namespace {
386 int getAssignmentPos( const AString& value, const String& alignmentSeparator )
387 {
388 integer idx= value.IndexOf( alignmentSeparator );
389 if( idx > 0 )
390 {
391 integer idxQuote= value.IndexOfOrLength( '"' );
392 if ( idxQuote > idx )
393 return static_cast<int>(idx);
394 }
395 return -1;
396 }
397}
398//! @endcond
399
401{
402 // read all variables
403 ALIB_STRINGS_TO_NARROW(FileName,nFileName, 1024)
404 std::ofstream outputFileStream ( nFileName );
405 if ( !outputFileStream.is_open() )
406 {
407 int errNo= errno;
410 throw e;
411 }
412
413 StringWriter writer;
414 writer.SetStream( &outputFileStream );
415
416 // write file header
417 if ( FileComments.IsNotEmpty() )
418 {
419 writeComments( writer, FileComments );
420 outputFileStream << std::endl;
421 }
422
423 // loop over all sections
424 int cntVars= 0;
425 for ( InMemoryPlugin::Section& section : sections )
426 {
427 // comments, name
428 if ( cntVars > 0 )
429 outputFileStream << std::endl;
430
431 // write section comments and name
432 writeComments( writer, section.Comments );
433 if ( section.Name().IsNotEmpty() )
434 writer.Write( NString256() << '[' << section.Name() << ']' << NewLine() );
435
436 // variables
437 integer maxVarLength= 0;
438 for ( auto& entry : section.Entries() )
439 maxVarLength= (std::max)( maxVarLength, entry.Name().Length() );
440
441 bool previousVarHasComments= true;
442 for ( auto& entry : section.Entries() )
443 {
444 ++cntVars;
445
446 // write comments
447 if( entry.Comments.IsNotEmpty() )
448 {
449 // we make an extra empty line if previous var had no comments
450 if( !previousVarHasComments)
451 outputFileStream << std::endl;
452
453 writeComments(writer, entry.Comments );
454 }
455
456 // write name =
457 writer.Write( entry.Name() );
458
459 // either write raw value (if it was not changed by the application, which
460 // clears the original value)
461 if ( entry.RawValue.IsNotEmpty() )
462 writer.Write( entry.RawValue );
463
464 // or write the values parsed by the software
465 else
466 {
467 outputFileStream << '=';
468 Spaces::Write( outputFileStream, maxVarLength - entry.Name().Length() + 1 );
469
470 String256 externalizedValue;
471 externalizedValue.DbgDisableBufferReplacementWarning();
472 auto qtyValues= entry.AdditionalValues.Size() + 1;
473 auto entryIt = entry.AdditionalValues.begin();
474
475 //-------- write as single-line ----------
476 if ( !HasBits( entry.FmtHints, FormatHints::MultiLine ) )
477 {
478 bool delimSpaces= (! HasBits( entry.FmtHints, FormatHints::NoDelimSpaces ) );
479
480 for ( integer idx= 0; idx < qtyValues; ++idx )
481 {
482 const String32* value;
483 if ( idx == 0 )
484 value= &entry.Value;
485
486 else
487 {
488 value= &*entryIt;
489 ++entryIt;
490
491 ALIB_ASSERT_ERROR( entry.Delim != 0, "CONFIG",
492 "No delimiter given for multi-value variable {!Q}.",
493 entry.Name() )
494
495 if( delimSpaces && FormatSpaceBeforeDelim)
496 outputFileStream << ' ';
497
498 writer.Write( NString16() << entry.Delim );
499
500 if( delimSpaces && FormatSpaceAfterDelim)
501 outputFileStream << ' ';
502 }
503
504 // externalize value
505 Substring src( *value );
506 externalizedValue.Reset();
507 StringConverter->ExternalizeValue( src, externalizedValue, entry.Delim );
508 writer.Write( externalizedValue );
509 }
510 }
511
512 // ---------- write as multi-line ----------
513 else
514 {
515 integer backSlashPos= 0;
516 integer lastLineLen= 0;
517
518 // Get maximum position of attribute assignment char '=' or ':' (if exists)
519 int maxAttributeAssignPos = 0;
520 bool allAttrHavePrecedingBlanks= true;
521 if (entry.FormatAttrAlignment.IsNotEmpty() )
522 {
523 auto entryIt2 = entry.AdditionalValues.begin();
524 for ( integer idx= 0; idx < qtyValues; ++idx )
525 {
526 const String32* value;
527 if ( idx == 0 )
528 value= &entry.Value;
529
530 // write delim and backslash of previous line, newline and then spaces of actual line
531 else
532 {
533 value= &*entryIt2;
534 ++entryIt2;
535 }
536
537 int attributeAssignPos= getAssignmentPos( *value, entry.FormatAttrAlignment );
538 if ( attributeAssignPos > 0 )
539 {
540 if ( maxAttributeAssignPos < attributeAssignPos )
541 maxAttributeAssignPos= attributeAssignPos;
542 allAttrHavePrecedingBlanks&= value->CharAt( attributeAssignPos - 1 ) == ' ';
543 }
544 }
545 if ( !allAttrHavePrecedingBlanks )
546 maxAttributeAssignPos += 1;
547 }
548
549 // loop over values of entry
550 for ( integer idx= 0; idx < qtyValues; ++idx )
551 {
552 const String32* value;
553 if ( idx == 0 )
554 value= &entry.Value;
555
556 // write delim and backslash of previous line, newline and then spaces of actual line
557 else
558 {
559 value= &*entryIt;
560 ++entryIt;
561 ALIB_ASSERT_ERROR( entry.Delim != 0, "CONFIG",
562 "No delimiter given for multi-value variable {!Q}.",
563 entry.Name() )
564 writer.Write( String8() << entry.Delim );
565 ++lastLineLen;
566
567 if ( backSlashPos < lastLineLen + 1 )
568 backSlashPos= lastLineLen + 4;
569
570 Spaces::Write( outputFileStream, backSlashPos - lastLineLen );
571
572 outputFileStream << '\\' << std::endl;
573
574 Spaces::Write( outputFileStream, maxVarLength + 2 ); // 2 for "= "
575 }
576
577 // externalize value
578 Substring src( *value );
579 externalizedValue.Reset();
580 StringConverter->ExternalizeValue( src, externalizedValue, entry.Delim );
581
582 // if first character is a INI comment char, then escape it
583 character firstChar= externalizedValue.CharAt(0);
584 if( idx != 0 && (firstChar == '#' || firstChar == ';' ) )
585 externalizedValue.InsertAt(A_CHAR("\\"), 0 );
586
587 // if assignment, insert spaces to align assignments
588 if (entry.FormatAttrAlignment.IsNotEmpty() )
589 {
590 int attributeAssignPos= getAssignmentPos( externalizedValue, entry.FormatAttrAlignment );
591 if ( attributeAssignPos > 0 && attributeAssignPos < maxAttributeAssignPos )
592 externalizedValue.InsertChars( ' ',
593 maxAttributeAssignPos-attributeAssignPos,
594 attributeAssignPos + (FormatIncludeDelimInAttrAlignment ?
595 0 : entry.FormatAttrAlignment.Length() )
596 );
597 }
598 writer.Write( externalizedValue );
599
600 lastLineLen= maxVarLength + 2 + externalizedValue.Length();
601 }
602 }
603 }
604 outputFileStream << std::endl;
605
606 // add an empty line if we have comments
607 if( (previousVarHasComments= entry.Comments.IsNotEmpty() ) == true )
608 outputFileStream << std::endl;
609 }
610 }
611
612 // close file
613 outputFileStream.close();
614}
615
617 ResourcePool& resourcePool,
618 const NString& resourceCategory,
619 const NString& resourceNamePrefix )
620{
621 // add section comments from resources to INI-file
622 for ( integer i= 0; i < config.CountPlugins(); ++i)
623 {
624 IniFile* iniFile= dynamic_cast<IniFile*>( config.GetPlugin( i ) );
625
626 if( !iniFile )
627 continue;
628 for( auto& section : iniFile->sections )
629 if( section.Comments.IsNull() )
630 {
631 auto& comment= resourcePool.Get( resourceCategory,
632 NString128() << resourceNamePrefix << section.Name()
633 ALIB_DBG(, false));
634 if( comment.IsNull() )
635 continue;
636
637 Paragraphs text;
638 text.LineWidth= iniFile->LineWidth;
639 text.AddMarked( comment );
640 text.RemoveLastNewLine();
641 section.Comments << text.Buffer;
642 }
643 }
644
645}
646
647}} // namespace [alib::config]
TValueList::Iterator SetValueCount(integer requestedSize)
virtual ALIB_API void ToVariable(Entry &entry, Variable &variable) const
ALIB_API Entry * createEntry(Section *section, const String &name)
ALIB_API std::pair< Section *, bool > SearchOrCreateSection(const String &sectionName)
virtual Section * createSection(const String &sectionName)
virtual ALIB_API void Clear()
virtual ALIB_API void FromVariable(Entry &entry, Variable &variable) const
Entry * searchEntry(const String &section, const String &name)
virtual ALIB_API void ToVariable(Entry &entry, Variable &variable) const override
Definition inifile.cpp:60
ALIB_API void WriteFile()
Definition inifile.cpp:400
static String DefaultFileExtension
Definition inifile.hpp:113
ALIB_API void ReadFile()
Definition inifile.cpp:225
virtual ALIB_API void FromVariable(Entry &entry, Variable &variable) const override
Definition inifile.cpp:151
static ALIB_API void AddResourcedSectionComments(Configuration &config, ResourcePool &resourcePool, const NString &resourceCategory, const NString &resourceNamePrefix)
Definition inifile.cpp:616
ALIB_API IniFile(const String &filePathAndName=nullptr)
Definition inifile.cpp:161
ALIB_API void writeComments(strings::compatibility::std::StringWriter &os, const AString &comments)
Definition inifile.cpp:363
virtual String Name() const override
Definition inifile.hpp:250
virtual ALIB_API void Clear() override
Definition inifile.cpp:203
bool FormatIncludeDelimInAttrAlignment
Definition inifile.hpp:147
List< integer > LinesWithReadErrors
Definition inifile.hpp:128
integer Size() const
Definition variable.hpp:708
void ReplaceComments(const String &newValue)
Definition variable.hpp:668
ALIB_API const String & Fullname()
Definition variable.cpp:266
character Delim() const
Definition variable.hpp:506
const String & GetString(int idx=0)
Definition variable.hpp:780
virtual ALIB_API void ExternalizeValue(const String &src, AString &dest, character delim)
Definition plugins.cpp:76
virtual ALIB_API void LoadFromString(Variable &variable, const String &src)
Definition plugins.cpp:112
Exception & Add(const NCString &file, int line, const NCString &func, TEnum type, TArgs &&... args)
TPlugin * GetPlugin(integer number)
ALIB_API void AddMarked(Boxes &args)
virtual const String & Get(const NString &category, const NString &name, bool dbgAssert)=0
static ALIB_API const ProcessInfo & Current()
T & EmplaceBack(TArgs &&... args)
Definition list.hpp:718
ALIB_API integer TrimAt(integer idx, const TCString< TChar > &trimChars=TT_StringConstants< TChar >::DefaultWhitespaces())
Definition astring.cpp:283
TAString & Delete(integer regionStart, integer regionLength=MAX_LEN)
Definition astring.hpp:1490
TAString & DeleteEnd(integer regionLength)
Definition astring.hpp:1589
TAString & TrimStart(const TCString< TChar > &trimChars=TT_StringConstants< TChar >::DefaultWhitespaces())
Definition astring.hpp:1655
TAString & InsertChars(TChar c, integer qty)
Definition astring.hpp:1405
TAString & TrimEnd(const TCString< TChar > &trimChars=TT_StringConstants< TChar >::DefaultWhitespaces())
Definition astring.hpp:1679
TAString & _(const TString< TChar > &src, integer regionStart, integer regionLength=MAX_LEN)
Definition astring.hpp:1056
TAString & DeleteStart(integer regionLength)
Definition astring.hpp:1547
void DbgDisableBufferReplacementWarning()
Definition astring.hpp:353
TAString & InsertAt(const TString< TChar > &src, integer pos)
Definition astring.hpp:1366
integer IndexOf(TChar needle, integer startIdx=0) const
Definition string.hpp:889
constexpr bool IsEmpty() const
Definition string.hpp:414
TChar CharAt(integer idx) const
Definition string.hpp:437
constexpr bool IsNotEmpty() const
Definition string.hpp:420
constexpr integer Length() const
Definition string.hpp:357
TChar CharAtStart() const
Definition string.hpp:459
integer IndexOfAny(const TString &needles, integer startIdx=0) const
Definition string.hpp:1083
integer IndexOfOrLength(TChar needle) const
Definition string.hpp:965
TChar CharAtEnd() const
Definition string.hpp:481
bool EndsWith(const TString &needle) const
Definition string.hpp:847
bool StartsWith(const TString &needle) const
Definition string.hpp:813
TSubstring & Trim(const TCString< TChar > &whiteSpaces=TT_StringConstants< TChar >::DefaultWhitespaces())
integer ConsumeChars(integer regionLength, TSubstring *target=nullptr)
bool ConsumeCharFromEnd(TChar consumable)
static ALIB_API void Write(std::basic_ostream< char > &os, integer qty)
Definition spaces.cpp:49
TLocalString< TChar, 8 > TrimChars
Definition tokenizer.hpp:90
ALIB_API TSubstring< TChar > & Next(lang::Whitespaces trimming=lang::Whitespaces::Trim, TChar newDelim='\0')
Definition tokenizer.cpp:18
TSubstring< TChar > Actual
Definition tokenizer.hpp:84
#define ALIB_WARNING(...)
Definition alib.hpp:981
#define ALIB_CALLER_NULLED
Definition alib.hpp:846
#define A_CHAR(STR)
#define ALIB_STRINGS_TO_NARROW( src, dest, bufSize)
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:984
#define ALIB_DBG(...)
Definition alib.hpp:457
#define ALIB_ASSERT(cond)
Definition alib.hpp:983
Exception CreateSystemException(const NCString &file, int line, const NCString &func, int errNo)
@ Include
Chooses inclusion.
@ Keep
Keep whitespaces in string.
Definition alib.cpp:57
constexpr CString DefaultWhitespaces()
Definition cstring.hpp:554
NLocalString< 16 > NString16
Type alias name for TLocalString<nchar,16> .
NLocalString< 128 > NString128
Type alias name for TLocalString<nchar,128> .
constexpr CString NewLine()
Definition cstring.hpp:528
constexpr nchar DirectorySeparator
strings::TSubstring< character > Substring
Type alias in namespace alib.
constexpr CString EmptyString()
Definition cstring.hpp:502
constexpr String NullString()
Definition string.hpp:2498
NLocalString< 256 > NString256
Type alias name for TLocalString<nchar,256> .
LocalString< 8 > String8
Type alias name for TLocalString<character,8> .
characters::character character
Type alias in namespace alib.
strings::TString< character > String
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:286