ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
Loading...
Searching...
No Matches
formatterstdimpl.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_LANG_FORMAT_FORMATTER_STD)
12# endif
13# if !defined (HPP_ALIB_STRINGS_FORMAT)
15# endif
16# if !defined(HPP_ALIB_LANG_FORMAT_EXCEPTIONS)
18# endif
19#endif // !defined(ALIB_DOX)
20
21#if !defined (_GLIBCXX_CMATH) && !defined (_CMATH_)
22# include <cmath>
23#endif
24
25
26// For code compatibility with ALox Java/C++
27// We have to use underscore as the start of the name and for this have to disable a compiler
28// warning. But this is a local code (cpp file) anyhow.
29#if defined(__clang__)
30 #pragma clang diagnostic push
31 #pragma clang diagnostic ignored "-Wreserved-id-macro"
32#endif
33
34#if defined(__clang__)
35 #pragma clang diagnostic pop
36#endif
37
38using namespace alib::strings;
39
40namespace alib::lang::format {
41
42
54
56 const String& pFormatString,
57 const Boxes& pArguments,
58 int pArgOffset )
59{
60 // save parameters/init state
61 targetString= &pTargetString;
62 targetStringStartLength= pTargetString.Length();
63 formatString= pFormatString;
65 arguments = &pArguments;
66 argOffset = pArgOffset;
67
68 // initialize state info
69 nextAutoIdx= 0;
70 argsConsumed= 0;
71
72 for(;;)
73 {
74 // find start of esc
75 integer escStart= findPlaceholder();
76 if ( escStart < 0 )
77 {
78 // write rest of formatString string (only if we had consumed before)
79 if( argsConsumed > 0)
81
82 return argsConsumed;
83 }
84
85 // write string before ESC code
86 writeStringPortion( escStart );
87 parser.template ConsumeChars<false>(1);
88
89 // get and clean field attributes
91
92 // invoke abstract method for parsing the attributes
93 if ( !parsePlaceholder() )
94 return argsConsumed;
95
96 // If no position was set in the field format string, automatically use next parameter
97 if ( placeholder.ArgIdx < 0 )
98 if ( !setArgument( -1 ) )
99 return argsConsumed;
100 ALIB_ASSERT( placeholder.Arg != nullptr )
101
102
103
104 // write field
105 if( preAndPostProcess( -1 ) )
106 {
107 integer actIdx= targetString->Length();
108 if ( !writeCustomFormat() )
109 {
110 // standard format
113 return argsConsumed;
114
115 // write argument
117 }
118 preAndPostProcess( actIdx );
119 }
120 }// main loop searching next escape sequence
121}
122
123
145
146
148{
150 {
151 if( pos == 0 )
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 )
165
166 // get corresponding argument
167 int argIdx= argOffset + placeholder.ArgIdx;
168 if( argIdx >= arguments->Size() )
173
174 placeholder.Arg= &(*arguments)[static_cast<size_t>(argIdx)];
175 return true;
176
177
178}
179
180
182{
183 const Box* arg= placeholder.Arg;
184
186 placeholder.TypeCodePosition= static_cast<int>( formatString.Length() - parser.Length() - 1 );
187
188
189 // types bool and hashcode always works!
192 return true;
193
194
195 if ( arg->IsFloatingPoint() )
196 {
199
201 return true;
202
205 "floating point",
207 }
208
209 if( arg->IsSignedIntegral()
210 || arg->IsUnsignedIntegral()
211 #if ALIB_SIZEOF_INTEGER == 4
212 || arg->IsType< int64_t>()
213 || arg->IsType<uint64_t>()
214 #endif
215 )
216 {
226 )
227 {
228 return true;
229 }
230
231 // not found
234 "integer",
236 }
237
238 if( arg->IsCharacter() )
239 {
243 return true;
244
247 "character",
249 }
250
253
254 return true;
255}
256
257
259{
260
261 // write to temp buffer first, if we have a field width given
262 AString* target;
263
264 if ( placeholder.Width > 0 )
265 {
266 target= &(fieldBuffer.Reset());
267
268 // set default alignment
273 }
274 else
275 target= targetString;
276
277 // store actual target length to fix some float exceptional cases (inf, nan)
278 integer oldTargetLength= target->Length();
279
280
281 integer fieldStartIdx= target->Length();
282
283 // the main switch over the type
284 switch( placeholder.Type )
285 {
287 ALIB_ERROR( "FMT", "Internal error: this should have been handled by method checkStdFieldAgainstArgument" )
288 break;
289
290 case PHTypes::String:
292 break;
293
294 case PHTypes::Bool:
295 target->_<false>( placeholder.Arg->Call<FIsTrue>() ? "true" : "false" );
296 break;
297
299 {
300 wchar wc= 0;
302 else if (placeholder.Arg->IsSignedIntegral ()) wc= static_cast<wchar>( placeholder.Arg->UnboxSignedIntegral () );
303 else if (placeholder.Arg->IsUnsignedIntegral()) wc= static_cast<wchar>( placeholder.Arg->UnboxUnsignedIntegral() );
304
305 if ( wc == 0)
306 wc= L'?';
307 target->_<false>( wc );
308 }
309 break;
310
311 case PHTypes::Fill:
312 {
315 : static_cast<integer>( placeholder.Arg->UnboxUnsignedIntegral() );
316
317 target->InsertChars( placeholder.FillChar, qty );
318 }
319 break;
320
321
323 {
325 placeholder.NF.DecMinimumFieldWidth= static_cast<int8_t>( placeholder.Width );
326
327 #if ALIB_SIZEOF_INTEGER == 4
328 if( placeholder.Arg->IsType< int64_t>() ) target->_<false>( strings::TFormat<character>( placeholder.Arg->Unbox< int64_t>(), &placeholder.NF ) );
329 else if( placeholder.Arg->IsType<uint64_t>() ) target->_<false>( strings::TFormat<character>( placeholder.Arg->Unbox<uint64_t>(), &placeholder.NF ) );
330 else
331 #endif
334 }
335 break;
336
337
340 case PHTypes::IntHex:
342 {
343 int digits= placeholder.Width;
345 {
349 digits-= static_cast<int>((target->Length() - fieldStartIdx));
350 if( placeholder.Width > 0 && digits <= 0 )
351 {
352 target->ShortenTo( fieldStartIdx + placeholder.Width );
353 break; // stop output, no space left
354 }
355 }
356 if ( digits <= 0 )
357 {
359 digits= ALIB_SIZEOF_INTEGER * 2;
360 else if ( placeholder.Arg->IsPointer() || placeholder.Arg->IsArray() )
361 digits= static_cast<int>( placeholder.Arg->GetPlaceholderUsageLength()
364 : 2 )
365
366 );
367 else
368 digits= 0;
369 }
370
371 uint64_t value= placeholder.Type == PHTypes::HashCode
372 ? static_cast<uint64_t>( placeholder.Arg->Hashcode() )
378 #if ALIB_SIZEOF_INTEGER == 8
381 #elif ALIB_SIZEOF_INTEGER == 4
384 #endif
385
386 if( placeholder.Type == PHTypes::IntOctal) target->_<false>( typename strings::TFormat<character>::Oct( value, digits, &placeholder.NF ) );
387 else if( placeholder.Type == PHTypes::IntBinary) target->_<false>( typename strings::TFormat<character>::Bin( value, digits, &placeholder.NF ) );
388 else if( placeholder.Type == PHTypes::HashCode ) target->_<false>( typename strings::TFormat<character>::Hex( value, digits, &placeholder.NF ) );
389 else target->_<false>( typename strings::TFormat<character>::Hex( value, digits, &placeholder.NF ) );
390 }
391 break;
392
393 case PHTypes::Float:
394 {
395 // get value
397 : placeholder.Arg->IsSignedIntegral() ? static_cast<double>( placeholder.Arg->UnboxSignedIntegral() )
398 : static_cast<double>( placeholder.Arg->UnboxUnsignedIntegral() );
400 value*= 100.0;
401
403 {
404 auto classification= std::fpclassify(value);
405
406 // write sign upfront and set fill character to 0
407 if( classification != FP_NAN )
408 {
409 bool negative= std::signbit(value);
410 if( classification == FP_ZERO && negative )
411 {
412 value= 0.0;
413 negative= false;
414 }
415
416 if( negative )
417 {
418 targetString->_<false>( '-' );
420 value= -value;
421 }
422 else if( placeholder.NF.PlusSign != '\0' )
423 {
426 }
427 placeholder.NF.PlusSign= '\0';
428
429 if ( !HasBits(placeholder.NF.Flags, NumberFormatFlags::WriteGroupChars) || placeholder.NF.ThousandsGroupChar == '\0')
431 else
432 {
433 // calculate integral part width
434 if ( placeholder.Width > 0 && !HasBits(placeholder.NF.Flags, NumberFormatFlags::ForceScientific) )
435 {
436 placeholder.NF.IntegralPartMinimumWidth= static_cast<int8_t>( placeholder.Width - 1 ); // -1 == the dot
437
440
443
444 // check
447 }
448 }
449 }
450
451 }
452
453 target->_<false>( strings::TFormat<character>( value, &placeholder.NF ) );
454
456 target->_<false>( '%' );
457
458
459 // if nan or inf was written, we fill with spaces
461 && ( target->IndexOf(placeholder.NF.NANLiteral, oldTargetLength) >= 0
462 || target->IndexOf(placeholder.NF.INFLiteral, oldTargetLength) >= 0 ) )
464
465 }
466 break;
467 }
468
469 // now do an 'intermediate post phase' processing
470 preAndPostProcess( fieldStartIdx, target );
471
472 // apply cutting
473 if ( placeholder.CutContent >= 0 )
474 {
475 // wchar compilation: we grant the length is the same as the number of wide characters
476 // added, although this is still not true for some unicode character combinations
477 if( std::is_same<character, wchar>::value )
478 {
479 // too much added?
480 if( target->Length() - oldTargetLength > placeholder.CutContent )
481 target->ShortenTo( oldTargetLength + placeholder.CutContent );
482 }
483 else
484 {
485 integer qtyWCharsAdded= target->Substring<false>( oldTargetLength, target->Length() - oldTargetLength ).WStringLength();
486
487 // too much added?
488 if( qtyWCharsAdded > placeholder.CutContent )
489 {
490 // was not unicode?
491 if( qtyWCharsAdded == target->Length() - oldTargetLength )
492 target->ShortenTo( oldTargetLength + placeholder.CutContent );
493
494 // in the unicode case, it gets complicated: we have to convert to unicode and
495 // then convert a part of it back!
496 else if( qtyWCharsAdded < 256)
497 {
498 WString256 wBuf;
501 wBuf.Append<false>( target->Buffer() + oldTargetLength, target->Length() - oldTargetLength );
503 target->ShortenTo( oldTargetLength );
504 target->Append<false>( wBuf.Buffer(),placeholder.CutContent );
505 }
506 }
507 }
508 }
509
510
511 // if field mode, we have to append the field buffer as a field to the real target now
512 if( target == &fieldBuffer )
514
515}
516
518{
520 if( !func )
521 return false;
522
524 return true;
525}
526
527
528
529} // namespace [alib::lang::format]
decltype(std::declval< typename TFDecl::Signature >()(std::declval< Box & >(), std::declval< TArgs >()...)) CallDirect(typename TFDecl::Signature function, TArgs &&... args) const
Definition box.inl:1161
uinteger UnboxUnsignedIntegral() const
Definition box.inl:626
bool IsPointer() const
Definition box.inl:736
wchar UnboxCharacter() const
Definition box.inl:668
bool IsFloatingPoint() const
Definition boxing.cpp:168
TFDecl::Signature GetFunction(Reach searchScope, bool isInvocation=false) const
bool IsUnsignedIntegral() const
Definition box.inl:592
ALIB_API size_t Hashcode() const
Definition boxing.cpp:195
bool IsSignedIntegral() const
Definition box.inl:574
bool IsType() const
unsigned int GetPlaceholderUsageLength() const
Definition box.inl:878
ALIB_API double UnboxFloatingPoint() const
Definition boxing.cpp:180
decltype(std::declval< typename TFDecl::Signature >()(std::declval< Box & >(), std::declval< TArgs >()...)) Call(TArgs &&... args) const
Definition box.inl:1135
const Placeholder & Data() const
Definition box.inl:843
integer UnboxSignedIntegral() const
Definition box.inl:609
const TUnboxable Unbox() const
bool IsArray() const
Definition box.inl:711
bool IsCharacter() const
Definition box.inl:651
integer Size() const
Definition boxes.inl:258
@ Float
Outputs a number in floating point format.
@ HashCode
Writes raw box data as hex.
@ 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.
virtual ALIB_API int format(AString &targetString, const String &formatString, const Boxes &arguments, int argOffset) override
virtual bool preAndPostProcess(integer startIdx, AString *target=nullptr)
virtual void writeStringPortion(integer length)=0
FormatterStdImpl(const String &formatterClassName)
TAString & ShortenTo(integer newLength)
Definition astring.hpp:908
TAString & Append(const TCharSrc *src, integer srcLength)
TAString & _(const TString< TChar > &src, integer regionStart, integer regionLength=MAX_LEN)
Definition astring.hpp:1056
void DbgDisableBufferReplacementWarning()
Definition astring.hpp:353
constexpr bool IsNotEmpty() const
Definition string.hpp:420
constexpr integer Length() const
Definition string.hpp:357
TString< TChar > Substring(integer regionStart, integer regionLength=MAX_LEN) const
Definition string.hpp:314
constexpr const TChar * Buffer() const
Definition string.hpp:350
#define ALIB_CALLER_NULLED
Definition alib.hpp:846
#define A_CHAR(STR)
#define ALIB_WARNINGS_RESTORE
Definition alib.hpp:715
#define ALIB_ERROR(...)
Definition alib.hpp:980
#define ALIB_WARNINGS_ALLOW_UNSAFE_BUFFER_USAGE
Definition alib.hpp:644
#define ALIB_ASSERT(cond)
Definition alib.hpp:983
#define ALIB_SIZEOF_INTEGER
Definition prepro.dox:26
platform_specific integer
Definition integers.hpp:50
@ Local
Denotes local reach.
@ Right
Chooses right alignment.
@ Left
Chooses left alignment.
lang::Exception Exception
Type alias in namespace alib.
characters::wchar wchar
Type alias in namespace alib.
TCString< TChar > BinLiteralPrefix
TCString< TChar > OctLiteralPrefix
TCString< TChar > HexLiteralPrefix
TCString< TChar > ExponentSeparator
void Set(TNumberFormat *other=nullptr)
detail::UnionIntegrals Integrals
Collection of integrals of different sizes.
uint8_t UInt8
8-bit unsigned integral.
uinteger UInt
Unsigned integral of platform-dependent size.
uint16_t UInt16
16-bit unsigned integral.
uint32_t UInt32
32-bit unsigned integral. Available only if platform is not of 32-bit.
uint64_t UInt64
64-bit unsigned integral. Available only if platform is not of 64-bit.