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