From 34dca24915db0b8d7941450ec95ca846ba68e411 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Thu, 1 Oct 2020 14:22:14 -0700 Subject: [PATCH 1/5] add parse_precision. --- stl/inc/format | 46 ++++++++++++++++++ .../P0645R10_text_formatting_parsing/test.cpp | 47 +++++++++++++++++-- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index addbe13cdf7..4794b498da6 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) { @@ -171,6 +176,22 @@ struct _Width_adapter { } }; +// Adapts a type modeling _Parse_spec_callbacks to module _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; + + explicit constexpr _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); @@ -191,6 +212,31 @@ 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..eff674a8419 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_dynamic_width); + } }; template testing_callbacks(_Align, basic_string_view) -> testing_callbacks; @@ -156,6 +168,33 @@ 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; + + auto s0 = view_typ(TYPED_LITERAL(CharT, ".0")); + auto s1 = view_typ(TYPED_LITERAL(CharT, ".1")); + auto s2 = view_typ(TYPED_LITERAL(CharT, ".12")); + auto s3 = view_typ(TYPED_LITERAL(CharT, ".{1}")); + auto s4 = view_typ(TYPED_LITERAL(CharT, ".{}")); + + auto i0 = view_typ(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); + } + + + return true; +} + int main() { test_parse_align(); test_parse_align(); @@ -169,5 +208,7 @@ int main() { test_parse_width(); static_assert(test_parse_width()); static_assert(test_parse_width()); + test_parse_precision(); + test_parse_precision(); return 0; } From 9740d4a863d10a3dc5a7d0e884305fcbcdb253f9 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Mon, 12 Oct 2020 13:44:44 -0700 Subject: [PATCH 2/5] adopt style review comments. --- stl/inc/format | 10 ++++++---- .../tests/P0645R10_text_formatting_parsing/test.cpp | 1 - 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 4794b498da6..dddefa50062 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -91,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 @@ -166,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{}); @@ -176,13 +177,13 @@ struct _Width_adapter { } }; -// Adapts a type modeling _Parse_spec_callbacks to module _Parse_arg_id_callbacks. +// 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; - explicit constexpr _Precision_adapter(_Callbacks_type& _Handler) : _Callbacks(_Handler) {} + constexpr explicit _Precision_adapter(_Callbacks_type& _Handler) : _Callbacks(_Handler) {} constexpr void _On_auto_id() { _Callbacks._On_dynamic_precision(_Auto_id_tag{}); @@ -219,6 +220,7 @@ constexpr const _CharT* _Parse_precision(const _CharT* _Begin, const _CharT* _En if (_Begin != _End) { _Ch = *_Begin; } + if ('0' <= _Ch && _Ch <= '9') { int _Precision = 0; _Begin = _Parse_nonnegative_integer(_Begin, _End, _Precision); @@ -232,7 +234,7 @@ constexpr const _CharT* _Parse_precision(const _CharT* _Begin, const _CharT* _En throw format_error("Invalid format string."); } } else { - throw format_error("missing precision specifier"); + throw format_error("Missing precision specifier."); } return _Begin; } diff --git a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp index eff674a8419..67790f84045 100644 --- a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp @@ -191,7 +191,6 @@ constexpr bool test_parse_precision() { test_parse_helper(parse_pre_fn, i0, true); } - return true; } From a20789fdff4a874d8fa81f8b88400dbeecbc6c4b Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Mon, 12 Oct 2020 14:05:40 -0700 Subject: [PATCH 3/5] add more tests for exceptions. --- tests/std/tests/P0645R10_text_formatting_parsing/test.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp index 67790f84045..9f43e1fa00f 100644 --- a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp @@ -180,6 +180,9 @@ constexpr bool test_parse_precision() { auto s4 = view_typ(TYPED_LITERAL(CharT, ".{}")); auto i0 = view_typ(TYPED_LITERAL(CharT, ".{ }")); + auto i1 = view_typ(TYPED_LITERAL(CharT, ".")); + auto i2 = view_typ(TYPED_LITERAL(CharT, ".{ |")); + auto i3 = view_typ(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}); @@ -189,6 +192,9 @@ constexpr bool test_parse_precision() { 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; From f17a461c1539945fe488d6db0d0e9e3070919c36 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Mon, 12 Oct 2020 14:13:05 -0700 Subject: [PATCH 4/5] test constexpr precision too --- tests/std/tests/P0645R10_text_formatting_parsing/test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp index 9f43e1fa00f..78b9d784949 100644 --- a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp @@ -215,5 +215,7 @@ int main() { static_assert(test_parse_width()); test_parse_precision(); test_parse_precision(); + static_assert(test_parse_precision()); + static_assert(test_parse_precision()); return 0; } From 570d63c5717b5ff5d6956e06bc063ea56924d573 Mon Sep 17 00:00:00 2001 From: Charles Barto Date: Tue, 13 Oct 2020 17:55:36 -0700 Subject: [PATCH 5/5] address some more review comments --- stl/inc/format | 5 +- .../P0645R10_text_formatting_parsing/test.cpp | 56 +++++++++---------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index dddefa50062..a94aa8f8496 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -203,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."); @@ -228,8 +228,9 @@ constexpr const _CharT* _Parse_precision(const _CharT* _Begin, const _CharT* _En } else if (_Ch == '{') { ++_Begin; if (_Begin != _End) { - _Begin = _Parse_arg_id(_Begin, _End, _Precision_adapter<_CharT, _Callbacks_type>(_Callbacks)); + _Begin = _Parse_arg_id(_Begin, _End, _Precision_adapter<_CharT, _Callbacks_type>{_Callbacks}); } + if (_Begin == _End || *_Begin != '}') { throw format_error("Invalid format string."); } diff --git a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp index 78b9d784949..187dca249b5 100644 --- a/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_parsing/test.cpp @@ -61,7 +61,7 @@ struct testing_callbacks { assert(id == expected_dynamic_precision); } constexpr void _On_dynamic_precision(_Auto_id_tag) { - assert(expected_dynamic_width); + assert(expected_auto_dynamic_precision); } }; template @@ -93,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, "*"))}); @@ -120,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}); @@ -142,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); @@ -173,16 +173,16 @@ constexpr bool test_parse_precision() { auto parse_pre_fn = _Parse_precision>; using view_typ = basic_string_view; - auto s0 = view_typ(TYPED_LITERAL(CharT, ".0")); - auto s1 = view_typ(TYPED_LITERAL(CharT, ".1")); - auto s2 = view_typ(TYPED_LITERAL(CharT, ".12")); - auto s3 = view_typ(TYPED_LITERAL(CharT, ".{1}")); - auto s4 = view_typ(TYPED_LITERAL(CharT, ".{}")); + 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, ".{}")); - auto i0 = view_typ(TYPED_LITERAL(CharT, ".{ }")); - auto i1 = view_typ(TYPED_LITERAL(CharT, ".")); - auto i2 = view_typ(TYPED_LITERAL(CharT, ".{ |")); - auto i3 = view_typ(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});