@@ -19,6 +19,10 @@ template <typename T>
19
19
using is_format_integral =
20
20
std::conjunction<std::is_integral<T>, std::negation<std::is_same<T, bool >>>;
21
21
22
+ template <typename T>
23
+ // NOLINTNEXTLINE(readability-identifier-naming)
24
+ static inline constexpr bool is_format_integral_v = is_format_integral<T>::value;
25
+
22
26
/* *
23
27
* Helper class to format and stream generic values into a std::basic_string's output stream type.
24
28
*
@@ -111,16 +115,25 @@ class BasicStringFormatter
111
115
FormatParameters<ParameterTypes...> &¶meters);
112
116
113
117
/* *
114
- * Format a single replacement field with the provided value.
118
+ * Format a single replacement field with the provided value. If the replacement field's width
119
+ * or precision options are nested replacement fields, the callback provide may be invoked to
120
+ * retrieve the value of the corresponding format parameter.
115
121
*
116
122
* @tparam T The type of the value to format.
123
+ * @tparam NestedSpecifierVisitorType The type of the callback to resolve nested replacement
124
+ * fields.
117
125
*
118
126
* @param stream The stream to insert the formatted value into.
119
127
* @param specifier The replacement field to format.
120
128
* @param value The value to format.
129
+ * @param nested_specifier_visitor Callback to resolve nested replacement fields.
121
130
*/
122
- template <typename T>
123
- static void format_value (ostream_type &stream, FormatSpecifier &&specifier, const T &value);
131
+ template <typename T, typename NestedSpecifierVisitorType>
132
+ static void format_value (
133
+ ostream_type &stream,
134
+ FormatSpecifier &&specifier,
135
+ const T &value,
136
+ NestedSpecifierVisitorType &&nested_specifier_visitor);
124
137
125
138
/* *
126
139
* Format a single replacement field with the provided boolean value.
@@ -227,9 +240,28 @@ void BasicStringFormatter<StringType>::format_internal(
227
240
FormatString<ParameterTypes...> &&fmt,
228
241
FormatParameters<ParameterTypes...> &¶meters)
229
242
{
230
- auto formatter = [&stream](auto &&specifier, const auto &value)
243
+ auto formatter = [&stream, ¶meters ](auto &&specifier, const auto &value)
231
244
{
232
- format_value (stream, std::move (specifier), value);
245
+ auto nested_specifier_visitor = [¶meters](std::size_t position) -> std::streamsize
246
+ {
247
+ std::streamsize result = -1 ;
248
+
249
+ parameters.visit (
250
+ FormatSpecifier {.m_position = position},
251
+ [&result](auto &&, const auto &nested_value)
252
+ {
253
+ // Note: this will only ever be entered with integer types, but the compiler
254
+ // will generate the below code for other types, so it must be protected.
255
+ if constexpr (is_format_integral_v<std::remove_cvref_t <decltype (nested_value)>>)
256
+ {
257
+ result = static_cast <std::streamsize>(nested_value);
258
+ }
259
+ });
260
+
261
+ return result;
262
+ };
263
+
264
+ format_value (stream, std::move (specifier), value, std::move (nested_specifier_visitor));
233
265
};
234
266
235
267
const view_type view = fmt.view ();
@@ -268,11 +300,12 @@ void BasicStringFormatter<StringType>::format_internal(
268
300
269
301
// ==================================================================================================
270
302
template <typename StringType>
271
- template <typename T>
303
+ template <typename T, typename NestedSpecifierVisitorType >
272
304
void BasicStringFormatter<StringType>::format_value(
273
305
ostream_type &stream,
274
306
FormatSpecifier &&specifier,
275
- const T &value)
307
+ const T &value,
308
+ NestedSpecifierVisitorType &&nested_specifier_visitor)
276
309
{
277
310
using U = std::remove_cvref_t <T>;
278
311
@@ -328,26 +361,40 @@ void BasicStringFormatter<StringType>::format_value(
328
361
329
362
if (specifier.m_width )
330
363
{
331
- stream.width (static_cast <int >(*specifier.m_width ));
364
+ stream.width (static_cast <std::streamsize >(*specifier.m_width ));
332
365
}
366
+ else if (specifier.m_width_position )
367
+ {
368
+ if (auto width = nested_specifier_visitor (*specifier.m_width_position ); width > 0 )
369
+ {
370
+ stream.width (width);
371
+ }
372
+ }
373
+
374
+ const std::streamsize precision = specifier.m_precision ?
375
+ static_cast <std::streamsize>(*specifier.m_precision ) :
376
+ (specifier.m_precision_position ?
377
+ nested_specifier_visitor (*specifier.m_precision_position ) :
378
+ -1 );
333
379
334
- if (specifier. m_precision )
380
+ if (precision >= 0 )
335
381
{
336
382
if constexpr (detail::is_like_supported_string_v<U>)
337
383
{
338
384
using traits_type = BasicStringTraits<detail::is_like_supported_string_t <U>>;
385
+ const auto sized_precision = static_cast <std::size_t >(precision);
339
386
340
387
// Neither std::setw nor std::setprecision will limit the number of characters from the
341
388
// string that are written to the stream. Instead, stream a substring view if needed.
342
- if (typename traits_type::view_type view (value); *specifier. m_precision < view.size ())
389
+ if (typename traits_type::view_type view (value); sized_precision < view.size ())
343
390
{
344
- streamer::stream_value (stream, view.substr (0 , *specifier. m_precision ));
391
+ streamer::stream_value (stream, view.substr (0 , sized_precision ));
345
392
return ;
346
393
}
347
394
}
348
395
else
349
396
{
350
- stream.precision (static_cast < int >(*specifier. m_precision ) );
397
+ stream.precision (precision );
351
398
}
352
399
}
353
400
0 commit comments