diff --git a/stl/inc/format b/stl/inc/format index addbe13cdf7..a94aa8f8496 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -46,6 +46,11 @@ concept _Parse_spec_callbacks = requires(_Ty _At, basic_string_view<_CharT> _Sv, { _At._On_width(_STD declval()) } -> same_as; { _At._On_dynamic_width(_STD declval()) } -> same_as; { _At._On_dynamic_width(_STD declval<_Auto_id_tag>()) } -> same_as; + { _At._On_precision(_STD declval()) } -> same_as; + { _At._On_dynamic_precision(_STD declval()) } -> same_as; + { _At._On_dynamic_precision(_STD declval<_Auto_id_tag>()) } -> same_as; + + /* { _At._On_type(_STD declval<_CharT>()) } -> same_as; */ }; template concept _Parse_arg_id_callbacks = requires(_Ty _At) { @@ -86,6 +91,7 @@ constexpr const _CharT* _Parse_arg_id(const _CharT* _Begin, const _CharT* _End, _Callbacks._On_auto_id(); return _Begin; } + if (_Ch >= '0' && _Ch <= '9') { int _Index = 0; // arg_id is not allowed to have any leading zeros, but is allowed to be @@ -161,7 +167,7 @@ template _Callbacks_type> struct _Width_adapter { _Callbacks_type& _Callbacks; - explicit constexpr _Width_adapter(_Callbacks_type& _Handler) : _Callbacks(_Handler) {} + constexpr explicit _Width_adapter(_Callbacks_type& _Handler) : _Callbacks(_Handler) {} constexpr void _On_auto_id() { _Callbacks._On_dynamic_width(_Auto_id_tag{}); @@ -171,6 +177,22 @@ struct _Width_adapter { } }; +// Adapts a type modeling _Parse_spec_callbacks to model _Parse_arg_id_callbacks. +// Used in _Parse_precision so that _Parse_arg_id can be used to parse dynamic precisions. +template _Callbacks_type> +struct _Precision_adapter { + _Callbacks_type& _Callbacks; + + constexpr explicit _Precision_adapter(_Callbacks_type& _Handler) : _Callbacks(_Handler) {} + + constexpr void _On_auto_id() { + _Callbacks._On_dynamic_precision(_Auto_id_tag{}); + } + constexpr void _On_manual_id(int _Id) { + _Callbacks._On_dynamic_precision(_Id); + } +}; + template _Callbacks_type> constexpr const _CharT* _Parse_width(const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { _STL_INTERNAL_CHECK(_Begin != _End); @@ -181,7 +203,7 @@ constexpr const _CharT* _Parse_width(const _CharT* _Begin, const _CharT* _End, _ } else if (*_Begin == '{') { ++_Begin; if (_Begin != _End) { - _Begin = _Parse_arg_id(_Begin, _End, _Width_adapter<_CharT, _Callbacks_type>(_Callbacks)); + _Begin = _Parse_arg_id(_Begin, _End, _Width_adapter<_CharT, _Callbacks_type>{_Callbacks}); } if (_Begin == _End || *_Begin != '}') { throw format_error("Invalid format string."); @@ -191,6 +213,33 @@ constexpr const _CharT* _Parse_width(const _CharT* _Begin, const _CharT* _End, _ return _Begin; } +template _Callbacks_type> +constexpr const _CharT* _Parse_precision(const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { + ++_Begin; + _CharT _Ch = '\0'; + if (_Begin != _End) { + _Ch = *_Begin; + } + + if ('0' <= _Ch && _Ch <= '9') { + int _Precision = 0; + _Begin = _Parse_nonnegative_integer(_Begin, _End, _Precision); + _Callbacks._On_precision(_Precision); + } else if (_Ch == '{') { + ++_Begin; + if (_Begin != _End) { + _Begin = _Parse_arg_id(_Begin, _End, _Precision_adapter<_CharT, _Callbacks_type>{_Callbacks}); + } + + if (_Begin == _End || *_Begin != '}') { + throw format_error("Invalid format string."); + } + } else { + throw format_error("Missing precision specifier."); + } + return _Begin; +} + template struct formatter; diff --git a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp index 89aec01765a..187dca249b5 100644 --- a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp @@ -33,9 +33,12 @@ template struct testing_callbacks { _Align expected_alignment = _Align::_None; basic_string_view expected_fill; - int expected_width = -1; - int expected_dynamic_width = -1; - bool expected_auto_dynamic_width = false; + int expected_width = -1; + int expected_dynamic_width = -1; + bool expected_auto_dynamic_width = false; + int expected_precision = -1; + int expected_dynamic_precision = -1; + bool expected_auto_dynamic_precision = false; constexpr void _On_align(_Align aln) { assert(aln == expected_alignment); } @@ -51,6 +54,15 @@ struct testing_callbacks { constexpr void _On_dynamic_width(_Auto_id_tag) { assert(expected_auto_dynamic_width); } + constexpr void _On_precision(int pre) { + assert(pre == expected_precision); + } + constexpr void _On_dynamic_precision(int id) { + assert(id == expected_dynamic_precision); + } + constexpr void _On_dynamic_precision(_Auto_id_tag) { + assert(expected_auto_dynamic_precision); + } }; template testing_callbacks(_Align, basic_string_view) -> testing_callbacks; @@ -81,10 +93,10 @@ constexpr bool test_parse_align() { auto parse_align_fn = _Parse_align>; using view_typ = basic_string_view; - auto s0 = view_typ(TYPED_LITERAL(CharT, "")); - auto s1 = view_typ(TYPED_LITERAL(CharT, "*<")); - auto s2 = view_typ(TYPED_LITERAL(CharT, "*>")); - auto s3 = view_typ(TYPED_LITERAL(CharT, "*^")); + view_typ s0(TYPED_LITERAL(CharT, "")); + view_typ s1(TYPED_LITERAL(CharT, "*<")); + view_typ s2(TYPED_LITERAL(CharT, "*>")); + view_typ s3(TYPED_LITERAL(CharT, "*^")); test_parse_helper(parse_align_fn, s0, false, view_typ::npos, {_Align::_None, view_typ(TYPED_LITERAL(CharT, ""))}); test_parse_helper(parse_align_fn, s1, false, view_typ::npos, {_Align::_Left, view_typ(TYPED_LITERAL(CharT, "*"))}); @@ -108,12 +120,12 @@ constexpr bool test_parse_width() { auto parse_width_fn = _Parse_width>; using view_typ = basic_string_view; - auto s0 = view_typ(TYPED_LITERAL(CharT, "1")); - auto s1 = view_typ(TYPED_LITERAL(CharT, "{1}")); - auto s2 = view_typ(TYPED_LITERAL(CharT, "{0}")); - auto s3 = view_typ(TYPED_LITERAL(CharT, "{}")); - auto i0 = view_typ(TYPED_LITERAL(CharT, "0")); - auto i1 = view_typ(TYPED_LITERAL(CharT, "01")); + view_typ s0(TYPED_LITERAL(CharT, "1")); + view_typ s1(TYPED_LITERAL(CharT, "{1}")); + view_typ s2(TYPED_LITERAL(CharT, "{0}")); + view_typ s3(TYPED_LITERAL(CharT, "{}")); + view_typ i0(TYPED_LITERAL(CharT, "0")); + view_typ i1(TYPED_LITERAL(CharT, "01")); test_parse_helper(parse_width_fn, s0, false, view_typ::npos, {.expected_width = 1}); test_parse_helper(parse_width_fn, s1, false, view_typ::npos, {.expected_dynamic_width = 1}); @@ -130,14 +142,14 @@ constexpr bool test_parse_arg_id() { using view_typ = basic_string_view; // note that parse arg id starts with the arg id itself, not the { beginning of the // format spec - auto s0 = view_typ(TYPED_LITERAL(CharT, "}")); - auto s1 = view_typ(TYPED_LITERAL(CharT, ":")); - auto s2 = view_typ(TYPED_LITERAL(CharT, ":}")); - auto s3 = view_typ(TYPED_LITERAL(CharT, "0:}")); - auto s4 = view_typ(TYPED_LITERAL(CharT, "0:")); - auto s5 = view_typ(TYPED_LITERAL(CharT, "1}")); - auto i0 = view_typ(TYPED_LITERAL(CharT, "01}")); - auto i1 = view_typ(TYPED_LITERAL(CharT, "0")); + view_typ s0(TYPED_LITERAL(CharT, "}")); + view_typ s1(TYPED_LITERAL(CharT, ":")); + view_typ s2(TYPED_LITERAL(CharT, ":}")); + view_typ s3(TYPED_LITERAL(CharT, "0:}")); + view_typ s4(TYPED_LITERAL(CharT, "0:")); + view_typ s5(TYPED_LITERAL(CharT, "1}")); + view_typ i0(TYPED_LITERAL(CharT, "01}")); + view_typ i1(TYPED_LITERAL(CharT, "0")); test_parse_helper(parse_arg_id_fn, s0, false, 0); test_parse_helper(parse_arg_id_fn, s1, false, 0); @@ -156,6 +168,38 @@ constexpr bool test_parse_arg_id() { return true; } +template +constexpr bool test_parse_precision() { + auto parse_pre_fn = _Parse_precision>; + using view_typ = basic_string_view; + + view_typ s0(TYPED_LITERAL(CharT, ".0")); + view_typ s1(TYPED_LITERAL(CharT, ".1")); + view_typ s2(TYPED_LITERAL(CharT, ".12")); + view_typ s3(TYPED_LITERAL(CharT, ".{1}")); + view_typ s4(TYPED_LITERAL(CharT, ".{}")); + + view_typ i0(TYPED_LITERAL(CharT, ".{ }")); + view_typ i1(TYPED_LITERAL(CharT, ".")); + view_typ i2(TYPED_LITERAL(CharT, ".{ |")); + view_typ i3(TYPED_LITERAL(CharT, ".{")); + + test_parse_helper(parse_pre_fn, s0, false, view_typ::npos, {.expected_precision = 0}); + test_parse_helper(parse_pre_fn, s1, false, view_typ::npos, {.expected_precision = 1}); + test_parse_helper(parse_pre_fn, s2, false, view_typ::npos, {.expected_precision = 12}); + test_parse_helper(parse_pre_fn, s3, false, view_typ::npos, {.expected_dynamic_precision = 1}); + test_parse_helper(parse_pre_fn, s4, false, view_typ::npos, {.expected_auto_dynamic_precision = true}); + + if (!is_constant_evaluated()) { + test_parse_helper(parse_pre_fn, i0, true); + test_parse_helper(parse_pre_fn, i1, true); + test_parse_helper(parse_pre_fn, i2, true); + test_parse_helper(parse_pre_fn, i3, true); + } + + return true; +} + int main() { test_parse_align(); test_parse_align(); @@ -169,5 +213,9 @@ int main() { test_parse_width(); static_assert(test_parse_width()); static_assert(test_parse_width()); + test_parse_precision(); + test_parse_precision(); + static_assert(test_parse_precision()); + static_assert(test_parse_precision()); return 0; }