ALib C++ Library
Library Version: 2510 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
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 )
54{
56
57 Formatter::AlternativeNumberFormat.ExponentSeparator= A_CHAR( "e" );
58 Formatter::AlternativeNumberFormat.NANLiteral = A_CHAR( "nan" );
59 Formatter::AlternativeNumberFormat.INFLiteral = A_CHAR( "inf" );
60
62}
63
65 const String& pFormatString,
66 const BoxesMA& pArguments,
67 int pArgOffset )
68{
69 // save parameters/init state
70 targetString= &pTargetString;
71 targetStringStartLength= pTargetString.Length();
72 formatString= pFormatString;
74 arguments = &pArguments;
75 argOffset = pArgOffset;
76
77 // initialize state info
78 nextAutoIdx= 0;
79 argsConsumed= 0;
80
81 for(;;)
82 {
83 // find start of esc
84 integer escStart= findPlaceholder();
85 if ( escStart < 0 )
86 {
87 // write rest of formatString string (only if we had consumed before)
88 if( argsConsumed > 0)
89 writeStringPortion( parser.Length() );
90
91 return argsConsumed;
92 }
93
94 // write string before ESC code
95 writeStringPortion( escStart );
96 parser.template ConsumeChars<NC>(1);
97
98 // get and clean field attributes
100
101 // invoke abstract method for parsing the attributes
102 if ( !parsePlaceholder() )
103 return argsConsumed;
104
105 // If no position was set in the field format string, automatically use next parameter
106 if ( placeholder.ArgIdx < 0 )
107 if ( !setArgument( -1 ) )
108 return argsConsumed;
109 ALIB_ASSERT( placeholder.Arg != nullptr, "FORMAT" )
110
111
112
113 // write field
114 if( preAndPostProcess( -1 ) )
115 {
116 integer actIdx= targetString->Length();
117 if ( !writeCustomFormat() )
118 {
119 // standard format
120 if ( ( placeholder.FormatSpec.IsNotEmpty() && !parseStdFormatSpec() )
122 return argsConsumed;
123
124 // write argument
126 }
127 preAndPostProcess( actIdx );
128 }
129 }// main loop searching next escape sequence
130}
131
132
134{
136 placeholder.NF.Flags+= NumberFormatFlags::HexLowerCase;
138 placeholder.PreviousArgIdx = placeholder.ArgIdx;
139 placeholder.Arg = nullptr;
140 placeholder.Width = 0;
141 placeholder.ArgIdx =
142 placeholder.CutContent = -1;
143
144 placeholder.AlignmentSpecified = false;
145 placeholder.ValueAlignment = lang::Alignment::Left;
146 placeholder.SignPaddingMode = false;
147 placeholder.FillChar = ' ';
148 placeholder.WriteBinOctHexPrefix = false;
149 placeholder.IsPercentage = false;
150 placeholder.TypeCode = '\0';
151 placeholder.TypeCodePosition = -1;
152 placeholder.FormatSpec = nullptr;
153}
154
155
157{
159 {
160 if( pos == 0 )
162 formatString, formatString.Length() - parser.Length() - 2 );
163
164 if( pos > 0 )
165 --pos;
166 }
167
168 // auto? If not, set auto to this pos + 1
169 placeholder.ArgIdx= ( pos >= 0) ? pos : nextAutoIdx++;
170
171 // store maximum value used
172 if ( argsConsumed - 1 < placeholder.ArgIdx )
173 argsConsumed= placeholder.ArgIdx + 1;
174
175 // get corresponding argument
176 int argIdx= argOffset + placeholder.ArgIdx;
177 if( argIdx >= arguments->Size() )
179 argIdx - argOffset + (argumentCountStartsWith1 ? 1 : 0),
180 arguments->Size() - argOffset,
181 formatString, formatString.Length() - parser.Length() - 1 );
182
183 placeholder.Arg= &(*arguments)[size_t(argIdx)];
184 return true;
185
186
187}
188
189
191{
192 if( placeholder.TypeCodePosition < 0 )
193 placeholder.TypeCodePosition= int( formatString.Length() - parser.Length() - 1 );
194
195
196 // types bool and hashcode always works!
197 if( placeholder.Type == PHTypes::Bool
198 || placeholder.Type == PHTypes::HashCode )
199 return true;
200
201
202 if ( placeholder.Arg->IsFloatingPoint() )
203 {
204 if( placeholder.Type == PHTypes::NotGiven )
206
207 if( placeholder.Type == PHTypes::Float )
208 return true;
209
212 placeholder.TypeCode, placeholder.Type,
213 "floating point", placeholder.Arg->TypeID(),
214 formatString, placeholder.TypeCodePosition );
215 }
216
217 if( placeholder.Arg->IsSignedIntegral()
218 || placeholder.Arg->IsUnsignedIntegral()
219 #if ALIB_SIZEOF_INTEGER == 4
220 || placeholder.Arg->IsType< int64_t>()
221 || placeholder.Arg->IsType<uint64_t>()
222 #endif
223 )
224 {
225 if( placeholder.Type == PHTypes::NotGiven )
231 || placeholder.Type == PHTypes::Float
233 || placeholder.Type == PHTypes::Fill
234 )
235 {
236 return true;
237 }
238
239 // not found
242 placeholder.TypeCode, placeholder.Type,
243 "integer", placeholder.Arg->TypeID(),
244 formatString, placeholder.TypeCodePosition );
245 }
246
247 if( placeholder.Arg->IsCharacter() )
248 {
249 if( placeholder.Type == PHTypes::NotGiven )
251 if( placeholder.Type == PHTypes::Character )
252 return true;
253
256 placeholder.TypeCode, placeholder.Type,
257 "character", placeholder.Arg->TypeID(),
258 formatString, placeholder.TypeCodePosition );
259 }
260
261 if( placeholder.Type == PHTypes::NotGiven )
263
264 return true;
265}
266
267
269{
270
271 // write to temp buffer first, if we have a field width given
272 AString* target;
273
274 if ( placeholder.Width > 0 )
275 {
276 target= &(fieldBuffer.Reset());
277
278 // set default alignment
279 if( !placeholder.AlignmentSpecified
282 placeholder.ValueAlignment= lang::Alignment::Right;
283 }
284 else
285 target= targetString;
286
287 // store actual target length to fix some float exceptional cases (inf, nan)
288 integer oldTargetLength= target->Length();
289
290
291 integer fieldStartIdx= target->Length();
292
293 // the main switch over the type
294 switch( placeholder.Type )
295 {
297 ALIB_ERROR( "FORMAT",
298 "Internal error: this should have been handled by method checkStdFieldAgainstArgument")
299 break;
300
301 case PHTypes::String:
303 break;
304
305 case PHTypes::Bool:
306 target->_<NC>( placeholder.Arg->Call<FIsTrue>() ? "true" : "false" );
307 break;
308
310 {
311 wchar wc= 0;
312 if (placeholder.Arg->IsCharacter ()) wc= placeholder.Arg->UnboxCharacter () ;
313 else if (placeholder.Arg->IsSignedIntegral ()) wc= static_cast<wchar>( placeholder.Arg->UnboxSignedIntegral () );
314 else if (placeholder.Arg->IsUnsignedIntegral()) wc= static_cast<wchar>( placeholder.Arg->UnboxUnsignedIntegral() );
315
316 if ( wc == 0)
317 wc= L'?';
318 target->_<NC>( wc );
319 }
320 break;
321
322 case PHTypes::Fill:
323 {
324 integer qty;
325 if( placeholder.Arg->IsSignedIntegral() ) qty= placeholder.Arg->UnboxSignedIntegral () ;
326 else if( placeholder.Arg->IsUnsignedIntegral() ) qty= integer(placeholder.Arg->UnboxUnsignedIntegral());
329 placeholder.TypeCode, placeholder.Type,
330 "Fill", placeholder.Arg->TypeID(),
331 formatString, placeholder.TypeCodePosition );
332 target->InsertChars( placeholder.FillChar, qty );
333 }
334 break;
335
336
338 {
339 if (placeholder.SignPaddingMode)
340 placeholder.NF.DecMinimumFieldWidth= int8_t( placeholder.Width );
341
342 #if ALIB_SIZEOF_INTEGER == 4
343 if( placeholder.Arg->IsType< int64_t>() ) target->_<NC>( strings::TDec<character>( placeholder.Arg->Unbox< int64_t>(), &placeholder.NF ) );
344 else if( placeholder.Arg->IsType<uint64_t>() ) target->_<NC>( strings::TDec<character>( placeholder.Arg->Unbox<uint64_t>(), &placeholder.NF ) );
345 else
346 #endif
347 if( placeholder.Arg->IsSignedIntegral()) target->_<NC>( strings::TDec<character>( placeholder.Arg->UnboxSignedIntegral() , &placeholder.NF ) );
348 else target->_<NC>( strings::TDec<character>( placeholder.Arg->UnboxUnsignedIntegral(), &placeholder.NF ) );
349 }
350 break;
351
352
355 case PHTypes::IntHex:
357 {
358 int digits= placeholder.Width;
359 if( placeholder.WriteBinOctHexPrefix )
360 {
361 target->_<NC>( placeholder.Type == PHTypes::IntOctal ? placeholder.NF.OctLiteralPrefix :
362 placeholder.Type == PHTypes::IntBinary ? placeholder.NF.BinLiteralPrefix :
363 placeholder.NF.HexLiteralPrefix );
364 digits-= int((target->Length() - fieldStartIdx));
365 if( placeholder.Width > 0 && digits <= 0 )
366 {
367 target->ShortenTo( fieldStartIdx + placeholder.Width );
368 break; // stop output, no space left
369 }
370 }
371 if ( digits <= 0 )
372 {
373 if (placeholder.Type == PHTypes::HashCode )
374 digits= ALIB_SIZEOF_INTEGER * 2;
375 else if ( placeholder.Arg->IsPointer() || placeholder.Arg->IsArray() )
376 digits= int( placeholder.Arg->GetPlaceholderUsageLength()
377 * ( placeholder.Type == PHTypes::IntOctal ? 3
378 : placeholder.Type == PHTypes::IntBinary ? 8
379 : 2 ) );
380 else
381 digits= 0;
382 }
383
384 uint64_t value= placeholder.Type == PHTypes::HashCode
385 ? uint64_t( placeholder.Arg->Hashcode() )
386 : placeholder.Arg->GetPlaceholderUsageLength() == 1
387 ? placeholder.Arg->Data().Integrals.UInt8
388 : placeholder.Arg->GetPlaceholderUsageLength() == 2
389 ? placeholder.Arg->Data().Integrals.UInt16
390 : placeholder.Arg->GetPlaceholderUsageLength() == 4
391 #if ALIB_SIZEOF_INTEGER == 8
392 ? placeholder.Arg->Data().Integrals.UInt32
393 : placeholder.Arg->Data().Integrals.UInt;
394 #elif ALIB_SIZEOF_INTEGER == 4
395 ? placeholder.Arg->Data().Integrals.UInt
396 : placeholder.Arg->Data().Integrals.UInt64;
397 #endif
398
399 if( placeholder.Type == PHTypes::IntOctal) target->_<NC>( TOct<character>( value, digits, &placeholder.NF ) );
400 else if( placeholder.Type == PHTypes::IntBinary) target->_<NC>( TBin<character>( value, digits, &placeholder.NF ) );
401 else if( placeholder.Type == PHTypes::HashCode ) target->_<NC>( THex<character>( value, digits, &placeholder.NF ) );
402 else target->_<NC>( THex<character>( value, digits, &placeholder.NF ) );
403 }
404 break;
405
406 case PHTypes::Float:
407 {
408 // get value
409 double value= placeholder.Arg->IsFloatingPoint() ? placeholder.Arg->UnboxFloatingPoint()
410 : placeholder.Arg->IsSignedIntegral() ? double( placeholder.Arg->UnboxSignedIntegral() )
411 : double( placeholder.Arg->UnboxUnsignedIntegral() );
412 if( placeholder.IsPercentage )
413 value*= 100.0;
414
415 if (placeholder.SignPaddingMode)
416 {
417 auto classification= std::fpclassify(value);
418
419 // write sign upfront and set fill character to 0
420 if( classification != FP_NAN )
421 {
422 bool negative= std::signbit(value);
423 if( classification == FP_ZERO && negative )
424 {
425 value= 0.0;
426 negative= false;
427 }
428
429 if( negative )
430 {
431 targetString->_<NC>( '-' );
432 --placeholder.Width;
433 value= -value;
434 }
435 else if( placeholder.NF.PlusSign != '\0' )
436 {
437 targetString->_<NC>( placeholder.NF.PlusSign );
438 --placeholder.Width;
439 }
440 placeholder.NF.PlusSign= '\0';
441
442 if ( !HasBits(placeholder.NF.Flags, NumberFormatFlags::WriteGroupChars) || placeholder.NF.ThousandsGroupChar == '\0')
443 placeholder.FillChar= '0';
444 else
445 {
446 // calculate integral part width
447 if ( placeholder.Width > 0 && !HasBits(placeholder.NF.Flags, NumberFormatFlags::ForceScientific) )
448 {
449 placeholder.NF.IntegralPartMinimumWidth= int8_t( placeholder.Width - 1 ); // -1 == the dot
450
451 if( placeholder.NF.FractionalPartWidth >= 0 )
452 placeholder.NF.IntegralPartMinimumWidth-= placeholder.NF.FractionalPartWidth;
453
454 if( placeholder.IsPercentage )
455 --placeholder.NF.IntegralPartMinimumWidth;
456
457 // check
458 if( placeholder.NF.IntegralPartMinimumWidth <= 0)
459 placeholder.NF.IntegralPartMinimumWidth= 1;
460 }
461 }
462 }
463
464 }
465
466 target->_<NC>( strings::TDec<character>( value, &placeholder.NF ) );
467
468 if( placeholder.IsPercentage )
469 target->_<NC>( '%' );
470
471
472 // if nan or inf was written, we fill with spaces
473 if ( placeholder.SignPaddingMode
474 && ( target->IndexOf(placeholder.NF.NANLiteral, oldTargetLength) >= 0
475 || target->IndexOf(placeholder.NF.INFLiteral, oldTargetLength) >= 0 ) )
476 placeholder.FillChar= ' ';
477
478 }
479 break;
480
481 default: ALIB_ERROR("FORMAT", "Illegal switch state.") break;
482 } // switch( placeholder.Type )
483
484 // now do an 'intermediate post phase' processing
485 preAndPostProcess( fieldStartIdx, target );
486
487 // apply cutting
488 if ( placeholder.CutContent >= 0 )
489 {
490 // wchar compilation: we grant the length is the same as the number of wide characters
491 // added, although this is still not true for some unicode character combinations
492 if( std::is_same<character, wchar>::value )
493 {
494 // too much added?
495 if( target->Length() - oldTargetLength > placeholder.CutContent )
496 target->ShortenTo( oldTargetLength + placeholder.CutContent );
497 }
498 else
499 {
500 integer qtyWCharsAdded= target->Substring<NC>( oldTargetLength, target->Length() - oldTargetLength ).WStringLength();
501
502 // too much added?
503 if( qtyWCharsAdded > placeholder.CutContent )
504 {
505 // was not unicode?
506 if( qtyWCharsAdded == target->Length() - oldTargetLength )
507 target->ShortenTo( oldTargetLength + placeholder.CutContent );
508
509 // in the unicode case, it gets complicated: we have to convert to unicode and
510 // then convert a part of it back!
511 else if( qtyWCharsAdded < 256)
512 {
513 WString256 wBuf;
515 wBuf.Append<NC>( target->Buffer() + oldTargetLength, target->Length() - oldTargetLength );
516 target->ShortenTo( oldTargetLength );
517 target->Append<NC>( wBuf.Buffer(),placeholder.CutContent );
518 }
519 }
520 }
521 }
522
523
524 // if field mode, we have to append the field buffer as a field to the real target now
525 if( target == &fieldBuffer )
527
528}
529
531{
532 auto* func= placeholder.Arg->GetFunction<FFormat>( lang::Reach::Local );
533 if( !func )
534 return false;
535
536 placeholder.Arg->CallDirect<FFormat>( func, placeholder.FormatSpec, placeholder.NF, *targetString );
537 return true;
538}
539
540} // namespace [alib::format]
541
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:816
TAString & Append(const TCharSrc *src, integer srcLength)
Definition tastring.inl:851
void DbgDisableBufferReplacementWarning()
Definition tastring.inl:245
constexpr integer Length() const
Definition string.inl:318
constexpr const TChar * Buffer() const
Definition string.inl:313
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
Definition string.inl:386
#define ALIB_CALLER_NULLED
Definition alib.inl:1010
#define A_CHAR(STR)
#define ALIB_ASSERT(cond, domain)
Definition alib.inl:1048
#define ALIB_ERROR(domain,...)
Definition alib.inl:1045
#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:245
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>.