1
1
#pragma once
2
2
3
+ #include " fly/traits/traits.hpp"
3
4
#include " fly/types/string/detail/string_formatter_types.hpp"
4
5
#include " fly/types/string/detail/string_streamer.hpp"
5
6
#include " fly/types/string/detail/string_traits.hpp"
11
12
12
13
namespace fly ::detail {
13
14
15
+ /* *
16
+ * Helper trait to classify a type as an integer, excluding boolean types.
17
+ */
18
+ template <typename T>
19
+ using is_format_integral =
20
+ std::conjunction<std::is_integral<T>, std::negation<std::is_same<T, bool >>>;
21
+
14
22
/* *
15
23
* Helper class to format and stream generic values into a std::basic_string's output stream type.
16
24
*
@@ -22,6 +30,7 @@ class BasicStringFormatter
22
30
{
23
31
using traits = BasicStringTraits<StringType>;
24
32
using streamer = BasicStringStreamer<StringType>;
33
+ using stream_modifiers = BasicStreamModifiers<StringType>;
25
34
using size_type = typename traits::size_type;
26
35
using char_type = typename traits::char_type;
27
36
using view_type = typename traits::view_type;
@@ -113,6 +122,74 @@ class BasicStringFormatter
113
122
template <typename T>
114
123
static void format_value (ostream_type &stream, FormatSpecifier &&specifier, const T &value);
115
124
125
+ /* *
126
+ * Format a single replacement field with the provided boolean value.
127
+ *
128
+ * @tparam T The type of the value to format.
129
+ *
130
+ * @param stream The stream to insert the formatted value into.
131
+ * @param modifiers The active stream manipulator container.
132
+ * @param specifier The replacement field to format.
133
+ * @param value The value to format.
134
+ */
135
+ template <typename T, fly::enable_if<std::is_same<T, bool >> = 0 >
136
+ static void format_value_for_type (
137
+ ostream_type &stream,
138
+ stream_modifiers &&modifiers,
139
+ FormatSpecifier &&specifier,
140
+ const T &value);
141
+
142
+ /* *
143
+ * Format a single replacement field with the provided non-boolean integral value.
144
+ *
145
+ * @tparam T The type of the value to format.
146
+ *
147
+ * @param stream The stream to insert the formatted value into.
148
+ * @param modifiers The active stream manipulator container.
149
+ * @param specifier The replacement field to format.
150
+ * @param value The value to format.
151
+ */
152
+ template <typename T, fly::enable_if<is_format_integral<T>> = 0 >
153
+ static void format_value_for_type (
154
+ ostream_type &stream,
155
+ stream_modifiers &&modifiers,
156
+ FormatSpecifier &&specifier,
157
+ const T &value);
158
+
159
+ /* *
160
+ * Format a single replacement field with the provided floating point value.
161
+ *
162
+ * @tparam T The type of the value to format.
163
+ *
164
+ * @param stream The stream to insert the formatted value into.
165
+ * @param modifiers The active stream manipulator container.
166
+ * @param specifier The replacement field to format.
167
+ * @param value The value to format.
168
+ */
169
+ template <typename T, fly::enable_if<std::is_floating_point<T>> = 0 >
170
+ static void format_value_for_type (
171
+ ostream_type &stream,
172
+ stream_modifiers &&modifiers,
173
+ FormatSpecifier &&specifier,
174
+ const T &value);
175
+
176
+ /* *
177
+ * Format a single replacement field with the provided generic value.
178
+ *
179
+ * @tparam T The type of the value to format.
180
+ *
181
+ * @param stream The stream to insert the formatted value into.
182
+ * @param modifiers The active stream manipulator container.
183
+ * @param specifier The replacement field to format.
184
+ * @param value The value to format.
185
+ */
186
+ template <typename T, fly::enable_if_none<std::is_integral<T>, std::is_floating_point<T>> = 0 >
187
+ static void format_value_for_type (
188
+ ostream_type &stream,
189
+ stream_modifiers &&modifiers,
190
+ FormatSpecifier &&specifier,
191
+ const T &value);
192
+
116
193
static constexpr const auto s_left_brace = FLY_CHR(char_type, ' {' );
117
194
static constexpr const auto s_right_brace = FLY_CHR(char_type, ' }' );
118
195
static constexpr const auto s_zero = FLY_CHR(streamed_char_type, ' 0' );
@@ -199,8 +276,7 @@ void BasicStringFormatter<StringType>::format_value(
199
276
{
200
277
using U = std::remove_cvref_t <T>;
201
278
202
- BasicStreamModifiers<StringType> modifiers (stream);
203
- std::ios_base::fmtflags flags {};
279
+ stream_modifiers modifiers (stream);
204
280
205
281
if (specifier.m_fill )
206
282
{
@@ -210,40 +286,43 @@ void BasicStringFormatter<StringType>::format_value(
210
286
switch (specifier.m_alignment )
211
287
{
212
288
case FormatSpecifier::Alignment::Left:
213
- flags |= std::ios_base::left;
289
+ stream. setf ( std::ios_base::left) ;
214
290
break ;
291
+
215
292
case FormatSpecifier::Alignment::Right:
216
- flags |= std::ios_base::right;
293
+ stream. setf ( std::ios_base::right) ;
217
294
break ;
295
+
218
296
case FormatSpecifier::Alignment::Center: // TODO: Implement center-alignment.
219
297
case FormatSpecifier::Alignment::Default:
220
- flags |= specifier.is_numeric () ? std::ios_base::right : std::ios_base::left;
298
+ stream. setf ( specifier.is_numeric () ? std::ios_base::right : std::ios_base::left) ;
221
299
break ;
222
300
}
223
301
224
302
switch (specifier.m_sign )
225
303
{
226
304
case FormatSpecifier::Sign::Always:
227
- flags |= std::ios_base::showpos;
305
+ stream. setf ( std::ios_base::showpos) ;
228
306
break ;
307
+
229
308
case FormatSpecifier::Sign::NegativeOnlyWithPositivePadding:
230
309
modifiers.template locale <PositivePaddingFacet<streamed_char_type>>();
231
- flags |= std::ios_base::showpos;
310
+ stream. setf ( std::ios_base::showpos) ;
232
311
break ;
312
+
233
313
default :
234
314
break ;
235
315
}
236
316
237
317
if (specifier.m_alternate_form )
238
318
{
239
- flags |= std::ios_base::showbase;
240
- flags |= std::ios_base::showpoint;
319
+ stream. setf ( std::ios_base::showbase) ;
320
+ stream. setf ( std::ios_base::showpoint) ;
241
321
}
242
322
243
323
if (specifier.m_zero_padding )
244
324
{
245
- flags &= ~std::ios_base::adjustfield;
246
- flags |= std::ios_base::internal;
325
+ stream.setf (std::ios_base::internal, std::ios_base::adjustfield);
247
326
stream.fill (s_zero);
248
327
}
249
328
@@ -262,7 +341,6 @@ void BasicStringFormatter<StringType>::format_value(
262
341
// string that are written to the stream. Instead, stream a substring view if needed.
263
342
if (typename traits_type::view_type view (value); *specifier.m_precision < view.size ())
264
343
{
265
- stream.flags (flags);
266
344
streamer::stream_value (stream, view.substr (0 , *specifier.m_precision ));
267
345
return ;
268
346
}
@@ -275,71 +353,139 @@ void BasicStringFormatter<StringType>::format_value(
275
353
276
354
if (specifier.m_case == FormatSpecifier::Case::Upper)
277
355
{
278
- flags |= std::ios_base::uppercase;
356
+ stream. setf ( std::ios_base::uppercase) ;
279
357
}
280
358
359
+ format_value_for_type (stream, std::move (modifiers), std::move (specifier), value);
360
+ }
361
+
362
+ // ==================================================================================================
363
+ template <typename StringType>
364
+ template <typename T, fly::enable_if<std::is_same<T, bool >>>
365
+ void BasicStringFormatter<StringType>::format_value_for_type(
366
+ ostream_type &stream,
367
+ stream_modifiers &&modifiers,
368
+ FormatSpecifier &&specifier,
369
+ const T &value)
370
+ {
281
371
switch (specifier.m_type )
282
372
{
283
373
case FormatSpecifier::Type::String:
284
- if constexpr (std::is_same_v<U, bool >)
285
- {
286
- flags |= std::ios_base::boolalpha;
287
- }
374
+ stream.setf (std::ios_base::boolalpha);
288
375
break ;
376
+
289
377
case FormatSpecifier::Type::Binary:
290
378
modifiers.template locale <BinaryFacet<streamed_char_type>>();
291
379
break ;
380
+
292
381
case FormatSpecifier::Type::Octal:
293
- flags |= std::ios_base::oct;
382
+ stream. setf ( std::ios_base::oct) ;
294
383
break ;
384
+
295
385
case FormatSpecifier::Type::Hex:
296
- flags |= std::ios_base::hex;
386
+ stream. setf ( std::ios_base::hex) ;
297
387
break ;
298
- case FormatSpecifier::Type::HexFloat:
299
- flags |= std::ios_base::fixed | std::ios_base::scientific;
388
+
389
+ default :
300
390
break ;
301
- case FormatSpecifier::Type::Scientific:
302
- flags |= std::ios_base::scientific;
391
+ }
392
+
393
+ if (specifier.m_type == FormatSpecifier::Type::Character)
394
+ {
395
+ // TODO: Validate the value fits into streamed_char_type / convert Unicode encoding.
396
+ streamer::template stream_value<decltype (value), streamed_char_type>(stream, value);
397
+ }
398
+ else
399
+ {
400
+ streamer::stream_value (stream, value);
401
+ }
402
+ }
403
+
404
+ // ==================================================================================================
405
+ template <typename StringType>
406
+ template <typename T, fly::enable_if<is_format_integral<T>>>
407
+ void BasicStringFormatter<StringType>::format_value_for_type(
408
+ ostream_type &stream,
409
+ stream_modifiers &&modifiers,
410
+ FormatSpecifier &&specifier,
411
+ const T &value)
412
+ {
413
+ switch (specifier.m_type )
414
+ {
415
+ case FormatSpecifier::Type::Binary:
416
+ modifiers.template locale <BinaryFacet<streamed_char_type>>();
303
417
break ;
304
- case FormatSpecifier::Type::Fixed :
305
- // Note: this branch will only ever be entered with floating point types, but the
306
- // compiler will generate the below code for other types, so it must be protected.
307
- if constexpr (std::is_floating_point_v<U>)
308
- {
309
- // Only Apple's Clang seems to respect std::uppercase with std::fixed values. To
310
- // ensure consistency, format these values as general types.
311
- if (!std::isnan (value) && !std::isinf (value))
312
- {
313
- flags |= std::ios_base::fixed;
314
- }
315
- }
418
+
419
+ case FormatSpecifier::Type::Octal:
420
+ stream.setf (std::ios_base::oct);
421
+ break ;
422
+
423
+ case FormatSpecifier::Type::Hex:
424
+ stream.setf (std::ios_base::hex);
316
425
break ;
426
+
317
427
default :
318
428
break ;
319
429
}
320
430
321
- stream.flags (flags);
322
-
323
431
if (specifier.m_type == FormatSpecifier::Type::Character)
324
432
{
325
433
// TODO: Validate the value fits into streamed_char_type / convert Unicode encoding.
326
434
streamer::template stream_value<decltype (value), streamed_char_type>(stream, value);
327
435
}
328
- else if (specifier. is_integral ())
436
+ else
329
437
{
330
- // Note: this branch will only ever be entered with integral types, but the compiler will
331
- // generate the below code for other types, so it must be protected.
332
- if constexpr (std::is_integral_v<U>)
333
- {
334
- using integral_type = std::
335
- conditional_t <std::numeric_limits<U>::is_signed, std::intmax_t , std::uintmax_t >;
336
- streamer::template stream_value<decltype (value), integral_type>(stream, value);
337
- }
438
+ using integral_type =
439
+ std::conditional_t <std::numeric_limits<T>::is_signed, std::intmax_t , std::uintmax_t >;
440
+ streamer::template stream_value<decltype (value), integral_type>(stream, value);
338
441
}
339
- else
442
+ }
443
+
444
+ // ==================================================================================================
445
+ template <typename StringType>
446
+ template <typename T, fly::enable_if<std::is_floating_point<T>>>
447
+ void BasicStringFormatter<StringType>::format_value_for_type(
448
+ ostream_type &stream,
449
+ stream_modifiers &&,
450
+ FormatSpecifier &&specifier,
451
+ const T &value)
452
+ {
453
+ switch (specifier.m_type )
340
454
{
341
- streamer::stream_value (stream, value);
455
+ case FormatSpecifier::Type::HexFloat:
456
+ stream.setf (std::ios_base::fixed | std::ios_base::scientific);
457
+ break ;
458
+
459
+ case FormatSpecifier::Type::Scientific:
460
+ stream.setf (std::ios_base::scientific);
461
+ break ;
462
+
463
+ case FormatSpecifier::Type::Fixed :
464
+ // Only Apple's Clang seems to respect std::uppercase with std::fixed values. To ensure
465
+ // consistency, format these values as general types.
466
+ if (!std::isnan (value) && !std::isinf (value))
467
+ {
468
+ stream.setf (std::ios_base::fixed);
469
+ }
470
+ break ;
471
+
472
+ default :
473
+ break ;
342
474
}
475
+
476
+ streamer::stream_value (stream, value);
477
+ }
478
+
479
+ // ==================================================================================================
480
+ template <typename StringType>
481
+ template <typename T, fly::enable_if_none<std::is_integral<T>, std::is_floating_point<T>>>
482
+ inline void BasicStringFormatter<StringType>::format_value_for_type(
483
+ ostream_type &stream,
484
+ stream_modifiers &&,
485
+ FormatSpecifier &&,
486
+ const T &value)
487
+ {
488
+ streamer::stream_value (stream, value);
343
489
}
344
490
345
491
} // namespace fly::detail
0 commit comments