ALib C++ Library
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
formatterstdimpl.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 ========================================
15#include <cmath>
16#include "alib/alib.inl"
17//============================================== Module ============================================
18#if ALIB_C20_MODULES
19 module ALib.Format.StdImplementation;
20 import ALib.Lang;
21 import ALib.EnumOps;
22 import ALib.Strings;
23 import ALib.Exceptions;
24# if ALIB_CAMP
25 import ALib.Camp.Base;
26# endif
27#else
28# include "ALib.Lang.H"
29# include "ALib.Strings.H"
30# include "ALib.Exceptions.H"
32# include "ALib.Camp.Base.H"
33#endif
34//========================================== Implementation ========================================
35// For code compatibility with ALox Java/C++
36// We have to use underscore as the start of the name and for this have to disable a compiler
37// warning. But this is a local code (cpp file) anyhow.
38#if defined(__clang__)
39 #pragma clang diagnostic push
40 #pragma clang diagnostic ignored "-Wreserved-id-macro"
41#endif
42
43#if defined(__clang__)
44 #pragma clang diagnostic pop
45#endif
46
47using namespace alib::strings;
48
49namespace alib::format {
50
51
52FormatterStdImpl::FormatterStdImpl( const String& formatterClassName )
53: formatterName( formatterClassName ) {
55
56 Formatter::AlternativeNumberFormat.ExponentSeparator= A_CHAR( "e" );
57 Formatter::AlternativeNumberFormat.NANLiteral = A_CHAR( "nan" );
58 Formatter::AlternativeNumberFormat.INFLiteral = A_CHAR( "inf" );
59
61}
62
64 const String& pFormatString,
65 const BoxesMA& pArguments,
66 int pArgOffset ) {
67 // save parameters/init state
68 targetString= &pTargetString;
69 targetStringStartLength= pTargetString.Length();
70 formatString= pFormatString;
72 arguments = &pArguments;
73 argOffset = pArgOffset;
74
75 // initialize state info
76 nextAutoIdx= 0;
77 argsConsumed= 0;
78
79 for(;;) {
80 // find start of esc
81 integer escStart= findPlaceholder();
82 if ( escStart < 0 ) {
83 // write rest of formatString string (only if we had consumed before)
84 if( argsConsumed > 0)
85 writeStringPortion( parser.Length() );
86
87 return argsConsumed;
88 }
89
90 // write string before ESC code
91 writeStringPortion( escStart );
92 parser.template ConsumeChars<NC>(1);
93
94 // get and clean field attributes
96
97 // invoke abstract method for parsing the attributes
98 if ( !parsePlaceholder() )
99 return argsConsumed;
100
101 // If no position was set in the field format string, automatically use next parameter
102 if ( placeholder.ArgIdx < 0 )
103 if ( !setArgument( -1 ) )
104 return argsConsumed;
105 ALIB_ASSERT( placeholder.Arg != nullptr, "FORMAT" )
106
107
108
109 // write field
110 if( preAndPostProcess( -1 ) ) {
111 integer actIdx= targetString->Length();
112 if ( !writeCustomFormat() ) {
113 // standard format
114 if ( ( placeholder.FormatSpec.IsNotEmpty() && !parseStdFormatSpec() )
116 return argsConsumed;
117
118 // write argument
120 }
121 preAndPostProcess( actIdx );
122 }
123 }// main loop searching next escape sequence
124}
125
126
129 placeholder.NF.Flags+= NumberFormatFlags::HexLowerCase;
131 placeholder.PreviousArgIdx = placeholder.ArgIdx;
132 placeholder.Arg = nullptr;
133 placeholder.Width = 0;
134 placeholder.ArgIdx =
135 placeholder.CutContent = -1;
136
137 placeholder.AlignmentSpecified = false;
138 placeholder.ValueAlignment = lang::Alignment::Left;
139 placeholder.SignPaddingMode = false;
140 placeholder.FillChar = ' ';
141 placeholder.WriteBinOctHexPrefix = false;
142 placeholder.IsPercentage = false;
143 placeholder.TypeCode = '\0';
144 placeholder.TypeCodePosition = -1;
145 placeholder.FormatSpec = nullptr;
146}
147
148
151 if( pos == 0 )
153 formatString, formatString.Length() - parser.Length() - 2 );
154
155 if( pos > 0 )
156 --pos;
157 }
158
159 // auto? If not, set auto to this pos + 1
160 placeholder.ArgIdx= ( pos >= 0) ? pos : nextAutoIdx++;
161
162 // store maximum value used
163 if ( argsConsumed - 1 < placeholder.ArgIdx )
164 argsConsumed= placeholder.ArgIdx + 1;
165
166 // get corresponding argument
167 int argIdx= argOffset + placeholder.ArgIdx;
168 if( argIdx >= arguments->Size() )
170 argIdx - argOffset + (argumentCountStartsWith1 ? 1 : 0),
171 arguments->Size() - argOffset,
172 formatString, formatString.Length() - parser.Length() - 1 );
173
174 placeholder.Arg= &(*arguments)[size_t(argIdx)];
175 return true;
176
177
178}
179
180
182 if( placeholder.TypeCodePosition < 0 )
183 placeholder.TypeCodePosition= int( formatString.Length() - parser.Length() - 1 );
184
185
186 // types bool and hashcode always works!
187 if( placeholder.Type == PHTypes::Bool
188 || placeholder.Type == PHTypes::HashCode )
189 return true;
190
191
192 if ( placeholder.Arg->IsFloatingPoint() ) {
193 if( placeholder.Type == PHTypes::NotGiven )
195
196 if( placeholder.Type == PHTypes::Float )
197 return true;
198
201 placeholder.TypeCode, placeholder.Type,
202 "floating point", placeholder.Arg->TypeID(),
203 formatString, placeholder.TypeCodePosition );
204 }
205
206 if( placeholder.Arg->IsSignedIntegral()
207 || placeholder.Arg->IsUnsignedIntegral()
208 #if ALIB_SIZEOF_INTEGER == 4
209 || placeholder.Arg->IsType< int64_t>()
210 || placeholder.Arg->IsType<uint64_t>()
211 #endif
212 )
213 {
214 if( placeholder.Type == PHTypes::NotGiven )
220 || placeholder.Type == PHTypes::Float
222 || placeholder.Type == PHTypes::Fill
223 )
224 {
225 return true;
226 }
227
228 // not found
231 placeholder.TypeCode, placeholder.Type,
232 "integer", placeholder.Arg->TypeID(),
233 formatString, placeholder.TypeCodePosition );
234 }
235
236 if( placeholder.Arg->IsCharacter() ) {
237 if( placeholder.Type == PHTypes::NotGiven )
239 if( placeholder.Type == PHTypes::Character )
240 return true;
241
244 placeholder.TypeCode, placeholder.Type,
245 "character", placeholder.Arg->TypeID(),
246 formatString, placeholder.TypeCodePosition );
247 }
248
249 if( placeholder.Type == PHTypes::NotGiven )
251
252 return true;
253}
254
255
257
258 // write to temp buffer first, if we have a field width given
259 AString* target;
260
261 if ( placeholder.Width > 0 ) {
262 target= &(fieldBuffer.Reset());
263
264 // set default alignment
265 if( !placeholder.AlignmentSpecified
268 placeholder.ValueAlignment= lang::Alignment::Right;
269 }
270 else
271 target= targetString;
272
273 // store actual target length to fix some float exceptional cases (inf, nan)
274 integer oldTargetLength= target->Length();
275
276
277 integer fieldStartIdx= target->Length();
278
279 // the main switch over the type
280 switch( placeholder.Type ) {
282 ALIB_ERROR( "FORMAT",
283 "Internal error: this should have been handled by method checkStdFieldAgainstArgument")
284 break;
285
286 case PHTypes::String:
288 break;
289
290 case PHTypes::Bool:
291 target->_<NC>( placeholder.Arg->Call<FIsTrue>() ? "true" : "false" );
292 break;
293
295 {
296 wchar wc= 0;
297 if (placeholder.Arg->IsCharacter ()) wc= placeholder.Arg->UnboxCharacter () ;
298 else if (placeholder.Arg->IsSignedIntegral ()) wc= static_cast<wchar>( placeholder.Arg->UnboxSignedIntegral () );
299 else if (placeholder.Arg->IsUnsignedIntegral()) wc= static_cast<wchar>( placeholder.Arg->UnboxUnsignedIntegral() );
300
301 if ( wc == 0)
302 wc= L'?';
303 target->_<NC>( wc );
304 }
305 break;
306
307 case PHTypes::Fill:
308 {
309 integer qty;
310 if( placeholder.Arg->IsSignedIntegral() ) qty= placeholder.Arg->UnboxSignedIntegral () ;
311 else if( placeholder.Arg->IsUnsignedIntegral() ) qty= integer(placeholder.Arg->UnboxUnsignedIntegral());
314 placeholder.TypeCode, placeholder.Type,
315 "Fill", placeholder.Arg->TypeID(),
316 formatString, placeholder.TypeCodePosition );
317 target->InsertChars( placeholder.FillChar, qty );
318 }
319 break;
320
321
323 {
324 if (placeholder.SignPaddingMode)
325 placeholder.NF.DecMinimumFieldWidth= int8_t( placeholder.Width );
326
327 #if ALIB_SIZEOF_INTEGER == 4
328 if( placeholder.Arg->IsType< int64_t>() ) target->_<NC>( strings::TDec<character>( placeholder.Arg->Unbox< int64_t>(), &placeholder.NF ) );
329 else if( placeholder.Arg->IsType<uint64_t>() ) target->_<NC>( strings::TDec<character>( placeholder.Arg->Unbox<uint64_t>(), &placeholder.NF ) );
330 else
331 #endif
332 if( placeholder.Arg->IsSignedIntegral()) target->_<NC>( strings::TDec<character>( placeholder.Arg->UnboxSignedIntegral() , &placeholder.NF ) );
333 else target->_<NC>( strings::TDec<character>( placeholder.Arg->UnboxUnsignedIntegral(), &placeholder.NF ) );
334 }
335 break;
336
337
340 case PHTypes::IntHex:
342 {
343 int digits= placeholder.Width;
344 if( placeholder.WriteBinOctHexPrefix ) {
345 target->_<NC>( placeholder.Type == PHTypes::IntOctal ? placeholder.NF.OctLiteralPrefix :
346 placeholder.Type == PHTypes::IntBinary ? placeholder.NF.BinLiteralPrefix :
347 placeholder.NF.HexLiteralPrefix );
348 digits-= int((target->Length() - fieldStartIdx));
349 if( placeholder.Width > 0 && digits <= 0 ) {
350 target->ShortenTo( fieldStartIdx + placeholder.Width );
351 break; // stop output, no space left
352 } }
353 if ( digits <= 0 ) {
354 if (placeholder.Type == PHTypes::HashCode )
355 digits= ALIB_SIZEOF_INTEGER * 2;
356 else if ( placeholder.Arg->IsPointer() || placeholder.Arg->IsArray() )
357 digits= int( placeholder.Arg->GetPlaceholderUsageLength()
358 * ( placeholder.Type == PHTypes::IntOctal ? 3
359 : placeholder.Type == PHTypes::IntBinary ? 8
360 : 2 ) );
361 else
362 digits= 0;
363 }
364
365 uint64_t value= placeholder.Type == PHTypes::HashCode
366 ? uint64_t( placeholder.Arg->Hashcode() )
367 : placeholder.Arg->GetPlaceholderUsageLength() == 1
368 ? placeholder.Arg->Data().Integrals.UInt8
369 : placeholder.Arg->GetPlaceholderUsageLength() == 2
370 ? placeholder.Arg->Data().Integrals.UInt16
371 : placeholder.Arg->GetPlaceholderUsageLength() == 4
372 #if ALIB_SIZEOF_INTEGER == 8
373 ? placeholder.Arg->Data().Integrals.UInt32
374 : placeholder.Arg->Data().Integrals.UInt;
375 #elif ALIB_SIZEOF_INTEGER == 4
376 ? placeholder.Arg->Data().Integrals.UInt
377 : placeholder.Arg->Data().Integrals.UInt64;
378 #endif
379
380 if( placeholder.Type == PHTypes::IntOctal) target->_<NC>( TOct<character>( value, digits, &placeholder.NF ) );
381 else if( placeholder.Type == PHTypes::IntBinary) target->_<NC>( TBin<character>( value, digits, &placeholder.NF ) );
382 else if( placeholder.Type == PHTypes::HashCode ) target->_<NC>( THex<character>( value, digits, &placeholder.NF ) );
383 else target->_<NC>( THex<character>( value, digits, &placeholder.NF ) );
384 }
385 break;
386
387 case PHTypes::Float:
388 {
389 // get value
390 double value= placeholder.Arg->IsFloatingPoint() ? placeholder.Arg->UnboxFloatingPoint()
391 : placeholder.Arg->IsSignedIntegral() ? double( placeholder.Arg->UnboxSignedIntegral() )
392 : double( placeholder.Arg->UnboxUnsignedIntegral() );
393 if( placeholder.IsPercentage )
394 value*= 100.0;
395
396 if (placeholder.SignPaddingMode) {
397 auto classification= std::fpclassify(value);
398
399 // write sign upfront and set fill character to 0
400 if( classification != FP_NAN ) {
401 bool negative= std::signbit(value);
402 if( classification == FP_ZERO && negative ) {
403 value= 0.0;
404 negative= false;
405 }
406
407 if( negative ) {
408 targetString->_<NC>( '-' );
409 --placeholder.Width;
410 value= -value;
411 }
412 else if( placeholder.NF.PlusSign != '\0' ) {
413 targetString->_<NC>( placeholder.NF.PlusSign );
414 --placeholder.Width;
415 }
416 placeholder.NF.PlusSign= '\0';
417
418 if ( !HasBits(placeholder.NF.Flags, NumberFormatFlags::WriteGroupChars) || placeholder.NF.ThousandsGroupChar == '\0')
419 placeholder.FillChar= '0';
420 else {
421 // calculate integral part width
422 if ( placeholder.Width > 0 && !HasBits(placeholder.NF.Flags, NumberFormatFlags::ForceScientific) ) {
423 placeholder.NF.IntegralPartMinimumWidth= int8_t( placeholder.Width - 1 ); // -1 == the dot
424
425 if( placeholder.NF.FractionalPartWidth >= 0 )
426 placeholder.NF.IntegralPartMinimumWidth-= placeholder.NF.FractionalPartWidth;
427
428 if( placeholder.IsPercentage )
429 --placeholder.NF.IntegralPartMinimumWidth;
430
431 // check
432 if( placeholder.NF.IntegralPartMinimumWidth <= 0)
433 placeholder.NF.IntegralPartMinimumWidth= 1;
434 } } }
435
436 }
437
438 target->_<NC>( strings::TDec<character>( value, &placeholder.NF ) );
439
440 if( placeholder.IsPercentage )
441 target->_<NC>( '%' );
442
443
444 // if nan or inf was written, we fill with spaces
445 if ( placeholder.SignPaddingMode
446 && ( target->IndexOf(placeholder.NF.NANLiteral, oldTargetLength) >= 0
447 || target->IndexOf(placeholder.NF.INFLiteral, oldTargetLength) >= 0 ) )
448 placeholder.FillChar= ' ';
449
450 }
451 break;
452
453 default: ALIB_ERROR("FORMAT", "Illegal switch state.") break;
454 } // switch( placeholder.Type )
455
456 // now do an 'intermediate post phase' processing
457 preAndPostProcess( fieldStartIdx, target );
458
459 // apply cutting
460 if ( placeholder.CutContent >= 0 ) {
461 // wchar compilation: we grant the length is the same as the number of wide characters
462 // added, although this is still not true for some unicode character combinations
463 if( std::is_same<character, wchar>::value ) {
464 // too much added?
465 if( target->Length() - oldTargetLength > placeholder.CutContent )
466 target->ShortenTo( oldTargetLength + placeholder.CutContent );
467 } else {
468 integer qtyWCharsAdded= target->Substring<NC>( oldTargetLength, target->Length() - oldTargetLength ).WStringLength();
469
470 // too much added?
471 if( qtyWCharsAdded > placeholder.CutContent ) {
472 // was not unicode?
473 if( qtyWCharsAdded == target->Length() - oldTargetLength )
474 target->ShortenTo( oldTargetLength + placeholder.CutContent );
475
476 // in the unicode case, it gets complicated: we have to convert to unicode and
477 // then convert a part of it back!
478 else if( qtyWCharsAdded < 256) {
479 WString256 wBuf;
481 wBuf.Append<NC>( target->Buffer() + oldTargetLength, target->Length() - oldTargetLength );
482 target->ShortenTo( oldTargetLength );
483 target->Append<NC>( wBuf.Buffer(),placeholder.CutContent );
484 } } } }
485
486
487 // if field mode, we have to append the field buffer as a field to the real target now
488 if( target == &fieldBuffer )
490
491}
492
494 auto* func= placeholder.Arg->GetFunction<FFormat>( lang::Reach::Local );
495 if( !func )
496 return false;
497
498 placeholder.Arg->CallDirect<FFormat>( func, placeholder.FormatSpec, placeholder.NF, *targetString );
499 return true;
500}
501
502} // namespace [alib::format]
AString * targetString
The target string as provided with method Format.
FormatterStdImpl(const String &formatterClassName)
virtual bool preAndPostProcess(integer startIdx, AString *target=nullptr)
Substring parser
The current (remaining) format string.
virtual bool parseStdFormatSpec()=0
integer targetStringStartLength
The length of the target string before adding the formatted contents.
@ Float
Outputs a number in floating point format.
@ IntBinary
Outputs a given number in base 2.
@ IntBase10
Outputs a given number in base 10. The default.
@ IntHex
Outputs a given number in base 16.
@ IntOctal
Outputs a given number in base 8.
const BoxesMA * arguments
The list of arguments provided with method Format.
AString fieldBuffer
A string buffer, used for example, when writing aligned fields.
int nextAutoIdx
Counter for auto-indexed arguments.
virtual bool parsePlaceholder()=0
virtual ALIB_DLL int format(AString &targetString, const String &formatString, const BoxesMA &arguments, int argOffset) override
String formatString
The format string as provided with method Format.
virtual void writeStringPortion(integer length)=0
int argOffset
The offset of the first argument to use. Provided with method Format.
int argsConsumed
The number of arguments consumed by the current format string.
virtual integer findPlaceholder()=0
NumberFormat DefaultNumberFormat
Definition formatter.inl:96
NumberFormat AlternativeNumberFormat
TAString & ShortenTo(integer newLength)
Definition tastring.inl:787
TAString & Append(const TCharSrc *src, integer srcLength)
Definition tastring.inl:828
void DbgDisableBufferReplacementWarning()
Definition tastring.inl:244
constexpr integer Length() const
Definition string.inl:316
constexpr const TChar * Buffer() const
Definition string.inl:311
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
Definition string.inl:384
#define ALIB_CALLER_NULLED
Definition alib.inl:1027
#define A_CHAR(STR)
#define ALIB_ASSERT(cond, domain)
Definition alib.inl:1065
#define ALIB_ERROR(domain,...)
Definition alib.inl:1062
#define ALIB_SIZEOF_INTEGER
Definition integers.inl:15
@ ArgumentIndexOutOfBounds
Argument index greater than number of arguments available.
@ ArgumentIndexIs0
Argument index '0' not allowed.
@ IncompatibleTypeCode
Incompatible type code given argument type found.
@ Local
Denotes local reach.
@ Right
Chooses right alignment.
@ Left
Chooses left alignment.
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace alib.
characters::wchar wchar
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
exceptions::Exception Exception
Type alias in namespace alib.
boxing::TBoxes< MonoAllocator > BoxesMA
Type alias in namespace alib.
Definition boxes.inl:193
boxing::FIsTrue FIsTrue
Type alias in namespace alib.
boxing::FAppend< TChar, TAllocator > FAppend
Type alias in namespace alib.
WLocalString< 256 > WString256
Type alias name for TLocalString<wchar,256>.