From a9e5773f520bed4099950b5f4b42a17151c2ef36 Mon Sep 17 00:00:00 2001 From: Mark de Wever Date: Sun, 3 Apr 2022 17:43:52 +0200 Subject: [PATCH] [libc++][format] Implements formatting pointer. The feature is applied as DR instead of a normal paper. MSVC STL and libstdc++ will do the same. Implements - P2510R3 Formatting pointers Depends on D153192 Reviewed By: #libc, ldionne Differential Revision: https://reviews.llvm.org/D153195 --- libcxx/docs/ReleaseNotes.rst | 1 + libcxx/docs/Status/Cxx2c.rst | 4 ++++ libcxx/docs/Status/Cxx2cPapers.csv | 2 +- libcxx/docs/Status/FormatIssues.csv | 2 +- libcxx/docs/UsingLibcxx.rst | 3 +++ libcxx/include/__format/formatter_pointer.h | 8 ++++++-- libcxx/include/__format/parser_std_format_spec.h | 13 +++++++++---- .../container.adaptors.format/format.functions.tests.h | 6 ++++-- .../std/utilities/format/format.functions/format_tests.h | 16 ++++++++++++---- .../format.range.fmtset/format.functions.tests.h | 6 ++++-- .../format.range.formatter/format.functions.tests.h | 6 ++++-- .../format/format.tuple/format.functions.tests.h | 10 +++++----- libcxx/test/support/format.functions.common.h | 2 +- libcxx/utils/generate_feature_test_macro_components.py | 2 +- 14 files changed, 56 insertions(+), 25 deletions(-) diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst index 0dd9cde..cfed0b0 100644 --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -47,6 +47,7 @@ Implemented Papers - P2505R5 - Monadic operations for ``std::expected`` - P2711R1 - Making Multi-Param Constructors Of views explicit (``join_with_view`` is not done yet) - P2572R1 - ``std::format`` fill character allowances +- P2510R3 - Formatting pointers Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2c.rst b/libcxx/docs/Status/Cxx2c.rst index 8f5ca4f..a7ebc46 100644 --- a/libcxx/docs/Status/Cxx2c.rst +++ b/libcxx/docs/Status/Cxx2c.rst @@ -37,6 +37,10 @@ Paper Status :header-rows: 1 :widths: auto +.. note:: + + .. [#note-P2510R3] This paper is applied as DR against C++20. (MSVC STL and libstdc++ will do the same.) + .. _issues-status-cxx2c: Library Working Group Issues Status diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 19836a6..f00a123 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -7,7 +7,7 @@ "`P2530R3 `__","LWG","Hazard Pointers for C++26","Varna June 2023","","","" "`P2538R1 `__","LWG","ADL-proof ``std::projected``","Varna June 2023","","","|ranges|" "`P2495R3 `__","LWG","Interfacing ``stringstreams`` with ``string_view``","Varna June 2023","","","" -"`P2510R3 `__","LWG","Formatting pointers","Varna June 2023","|In Progress|","","|format|" +"`P2510R3 `__","LWG","Formatting pointers","Varna June 2023","|Complete| [#note-P2510R3]_","17.0","|format|" "`P2198R7 `__","LWG","Freestanding Feature-Test Macros and Implementation-Defined Extensions","Varna June 2023","","","" "`P2338R4 `__","LWG","Freestanding Library: Character primitives and the C library","Varna June 2023","","","" "`P2013R5 `__","LWG","Freestanding Language: Optional ``::operator new``","Varna June 2023","","","" diff --git a/libcxx/docs/Status/FormatIssues.csv b/libcxx/docs/Status/FormatIssues.csv index 72c9a92..5fc53ec 100644 --- a/libcxx/docs/Status/FormatIssues.csv +++ b/libcxx/docs/Status/FormatIssues.csv @@ -14,7 +14,7 @@ Number,Name,Standard,Assignee,Status,First released version "`P2675R1 `__","``format``'s width estimation is too approximate and not forward compatible","C++23","Mark de Wever","|Complete|",17.0 "`P2572R1 `__","``std::format`` fill character allowances","C++23","Mark de Wever","|Complete|",17.0 "`P2693R1 `__","Formatting ``thread::id`` and ``stacktrace``","C++23","Mark de Wever","|In progress|" -"`P2510R3 `__","Formatting pointers","C++26","Mark de Wever","|In Progress|", +"`P2510R3 `__","Formatting pointers","C++26","Mark de Wever","|Complete|", 17.0 "`P2757R3 `__","Type-checking format args","C++26","","", "`P2637R3 `__","Member ``visit``","C++26","","", `P1361 `_,"Integration of chrono with text formatting","C++20",Mark de Wever,|In Progress|, diff --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst index 96fee66..4cc649d 100644 --- a/libcxx/docs/UsingLibcxx.rst +++ b/libcxx/docs/UsingLibcxx.rst @@ -537,6 +537,9 @@ versions. This allows the library to have better estimates for newly introduced Unicode code points, without requiring the user to use the latest C++ version in their code base. +In C++26 formatting pointers gained a type ``P`` and allows to use +zero-padding. These options have been retroactively applied to C++20. + Turning off ASan annotation in containers ----------------------------------------- diff --git a/libcxx/include/__format/formatter_pointer.h b/libcxx/include/__format/formatter_pointer.h index ab699ba..18aaee1 100644 --- a/libcxx/include/__format/formatter_pointer.h +++ b/libcxx/include/__format/formatter_pointer.h @@ -43,11 +43,15 @@ public: _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator format(const void* __ptr, _FormatContext& __ctx) const { __format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx); __specs.__std_.__alternate_form_ = true; - __specs.__std_.__type_ = __format_spec::__type::__hexadecimal_lower_case; + __specs.__std_.__type_ = + __specs.__std_.__type_ == __format_spec::__type::__pointer_upper_case + ? __format_spec::__type::__hexadecimal_upper_case + : __format_spec::__type::__hexadecimal_lower_case; + return __formatter::__format_integer(reinterpret_cast(__ptr), __ctx, __specs); } - __format_spec::__parser<_CharT> __parser_{.__alignment_ = __format_spec::__alignment::__right}; + __format_spec::__parser<_CharT> __parser_; }; // [format.formatter.spec]/2.4 diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h index 25f2567..30fecff 100644 --- a/libcxx/include/__format/parser_std_format_spec.h +++ b/libcxx/include/__format/parser_std_format_spec.h @@ -151,7 +151,7 @@ inline constexpr __fields __fields_floating_point{ .__locale_specific_form_ = true, .__type_ = true}; inline constexpr __fields __fields_string{.__precision_ = true, .__type_ = true}; -inline constexpr __fields __fields_pointer{.__type_ = true}; +inline constexpr __fields __fields_pointer{.__zero_padding_ = true, .__type_ = true}; # if _LIBCPP_STD_VER >= 23 inline constexpr __fields __fields_tuple{.__use_range_fill_ = true}; @@ -189,7 +189,8 @@ enum class _LIBCPP_ENUM_VIS __type : uint8_t { __decimal, __hexadecimal_lower_case, __hexadecimal_upper_case, - __pointer, + __pointer_lower_case, + __pointer_upper_case, __char, __hexfloat_lower_case, __hexfloat_upper_case, @@ -675,7 +676,10 @@ private: __type_ = __type::__octal; break; case 'p': - __type_ = __type::__pointer; + __type_ = __type::__pointer_lower_case; + break; + case 'P': + __type_ = __type::__pointer_upper_case; break; case 's': __type_ = __type::__string; @@ -840,7 +844,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_floating_point(__parser<_C _LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_pointer(__format_spec::__type __type) { switch (__type) { case __format_spec::__type::__default: - case __format_spec::__type::__pointer: + case __format_spec::__type::__pointer_lower_case: + case __format_spec::__type::__pointer_upper_case: break; default: diff --git a/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h index 584ac2b..2497bc8 100644 --- a/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h +++ b/libcxx/test/std/containers/container.adaptors/container.adaptors.format/format.functions.tests.h @@ -717,7 +717,9 @@ void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& inpu check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input); // *** zero-padding *** - check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input); + check(SV("[0x0000]"), SV("{::06}"), input); + check(SV("[0x0000]"), SV("{::06p}"), input); + check(SV("[0X0000]"), SV("{::06P}"), input); // *** precision *** check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); @@ -726,7 +728,7 @@ void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& inpu check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input); // *** type *** - for (std::basic_string_view fmt : fmt_invalid_nested_types("p")) + for (std::basic_string_view fmt : fmt_invalid_nested_types("pP")) check_exception("The format-spec type has a type not supported for a pointer argument", fmt, input); // ***** Both have a format-spec diff --git a/libcxx/test/std/utilities/format/format.functions/format_tests.h b/libcxx/test/std/utilities/format/format.functions/format_tests.h index c798b36..19ef587 100644 --- a/libcxx/test/std/utilities/format/format.functions/format_tests.h +++ b/libcxx/test/std/utilities/format/format.functions/format_tests.h @@ -37,9 +37,9 @@ case #T[0]: break; #if TEST_STD_VER > 20 - constexpr std::string_view types = "aAbBcdeEfFgGopsxX?"; + constexpr std::string_view types = "aAbBcdeEfFgGopPsxX?"; #else - constexpr std::string_view types = "aAbBcdeEfFgGopsxX"; + constexpr std::string_view types = "aAbBcdeEfFgGopPsxX"; #endif for (auto type : types) { @@ -61,6 +61,7 @@ case #T[0]: CASE(G) CASE(o) CASE(p) + CASE(P) CASE(s) CASE(x) CASE(X) @@ -2547,6 +2548,11 @@ void format_test_pointer(TestFunction check, ExceptionTest check_exception) { check(SV("answer is '0x0:::'"), SV("answer is '{::<6}'"), P(nullptr)); check(SV("answer is ':0x0::'"), SV("answer is '{::^6}'"), P(nullptr)); + // Test whether zero padding is ignored + check(SV("answer is ':::0x0'"), SV("answer is '{::>06}'"), P(nullptr)); + check(SV("answer is '0x0:::'"), SV("answer is '{::<06}'"), P(nullptr)); + check(SV("answer is ':0x0::'"), SV("answer is '{::^06}'"), P(nullptr)); + // *** Sign *** check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), P(nullptr)); check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), P(nullptr)); @@ -2556,7 +2562,9 @@ void format_test_pointer(TestFunction check, ExceptionTest check_exception) { check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), P(nullptr)); // *** zero-padding *** - check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), P(nullptr)); + check(SV("answer is '0x0000'"), SV("answer is '{:06}'"), P(nullptr)); + check(SV("answer is '0x0000'"), SV("answer is '{:06p}'"), P(nullptr)); + check(SV("answer is '0X0000'"), SV("answer is '{:06P}'"), P(nullptr)); // *** precision *** check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), P(nullptr)); @@ -2565,7 +2573,7 @@ void format_test_pointer(TestFunction check, ExceptionTest check_exception) { check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), P(nullptr)); // *** type *** - for (const auto& fmt : invalid_types("p")) + for (const auto& fmt : invalid_types("pP")) check_exception("The format-spec type has a type not supported for a pointer argument", fmt, P(nullptr)); } diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h index bbb49e2..671785f 100644 --- a/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h +++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h @@ -886,7 +886,9 @@ void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& inpu check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input); // *** zero-padding *** - check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input); + check(SV("{0x0000}"), SV("{::06}"), input); + check(SV("{0x0000}"), SV("{::06p}"), input); + check(SV("{0X0000}"), SV("{::06P}"), input); // *** precision *** check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); @@ -895,7 +897,7 @@ void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& inpu check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input); // *** type *** - for (std::basic_string_view fmt : fmt_invalid_nested_types("p")) + for (std::basic_string_view fmt : fmt_invalid_nested_types("pP")) check_exception("The format-spec type has a type not supported for a pointer argument", fmt, input); // ***** Both have a format-spec diff --git a/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h b/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h index 57f1861..550450d 100644 --- a/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h +++ b/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h @@ -700,7 +700,9 @@ void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& inpu check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input); // *** zero-padding *** - check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input); + check(SV("[0x0000]"), SV("{::06}"), input); + check(SV("[0x0000]"), SV("{::06p}"), input); + check(SV("[0X0000]"), SV("{::06P}"), input); // *** precision *** check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); @@ -709,7 +711,7 @@ void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& inpu check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input); // *** type *** - for (std::basic_string_view fmt : fmt_invalid_nested_types("p")) + for (std::basic_string_view fmt : fmt_invalid_nested_types("pP")) check_exception("The format-spec type has a type not supported for a pointer argument", fmt, input); // ***** Both have a format-spec diff --git a/libcxx/test/std/utilities/format/format.tuple/format.functions.tests.h b/libcxx/test/std/utilities/format/format.tuple/format.functions.tests.h index 2c21f6c..e649543 100644 --- a/libcxx/test/std/utilities/format/format.tuple/format.functions.tests.h +++ b/libcxx/test/std/utilities/format/format.tuple/format.functions.tests.h @@ -67,7 +67,7 @@ void test_tuple_or_pair_int_int(TestFunction check, ExceptionTest check_exceptio check(SV("__42: 99___"), SV("{:_^11m}"), input); check(SV("__42, 99___"), SV("{:_^11n}"), input); - for (CharT c : SV("aAbBcdeEfFgGopsxX?")) { + for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) { check_exception("The format-spec should consume the input or end with a '}'", std::basic_string_view{STR("{:") + c + STR("}")}, input); @@ -114,7 +114,7 @@ void test_tuple_or_pair_int_string(TestFunction check, ExceptionTest check_excep check(SV("__42: \"hello\"___"), SV("{:_^16m}"), input); check(SV("__42, \"hello\"___"), SV("{:_^16n}"), input); - for (CharT c : SV("aAbBcdeEfFgGopsxX?")) { + for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) { check_exception("The format-spec should consume the input or end with a '}'", std::basic_string_view{STR("{:") + c + STR("}")}, input); @@ -204,7 +204,7 @@ void test_tuple_int(TestFunction check, ExceptionTest check_exception) { check_exception("The format specifier m requires a pair or a two-element tuple", SV("{:m}"), input); check(SV("__42___"), SV("{:_^7n}"), input); - for (CharT c : SV("aAbBcdeEfFgGopsxX?")) { + for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) { check_exception("The format-spec should consume the input or end with a '}'", std::basic_string_view{STR("{:") + c + STR("}")}, input); @@ -253,7 +253,7 @@ void test_tuple_int_string_color(TestFunction check, ExceptionTest check_excepti check_exception("The format specifier m requires a pair or a two-element tuple", SV("{:m}"), input); check(SV("__42, \"hello\", \"red\"___"), SV("{:_^23n}"), input); - for (CharT c : SV("aAbBcdeEfFgGopsxX?")) { + for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) { check_exception("The format-spec should consume the input or end with a '}'", std::basic_string_view{STR("{:") + c + STR("}")}, input); @@ -326,7 +326,7 @@ void test_nested(TestFunction check, ExceptionTest check_exception, Nested&& inp check(SV("__42: (\"hello\", \"red\")___"), SV("{:_^25m}"), input); check(SV("__42, (\"hello\", \"red\")___"), SV("{:_^25n}"), input); - for (CharT c : SV("aAbBcdeEfFgGopsxX?")) { + for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) { check_exception("The format-spec should consume the input or end with a '}'", std::basic_string_view{STR("{:") + c + STR("}")}, input); diff --git a/libcxx/test/support/format.functions.common.h b/libcxx/test/support/format.functions.common.h index e979a20..656ac99 100644 --- a/libcxx/test/support/format.functions.common.h +++ b/libcxx/test/support/format.functions.common.h @@ -205,7 +205,7 @@ std::basic_string get_colons() { } constexpr std::string_view get_format_types() { - return "aAbBcdeEfFgGopsxX" + return "aAbBcdeEfFgGopPsxX" #if TEST_STD_VER > 20 "?" #endif diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 3f4ba0e..759badf 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -462,7 +462,7 @@ feature_test_macros = [ # "c++23": 202207, Not implemented P2419R2 Clarify handling of encodings in localized formatting of chrono types }, # Note these three papers are adopted at the June 2023 meeting and have sequential numbering - # 202304 P2510R3 Formatting pointers + # 202304 P2510R3 Formatting pointers (Implemented) # 202305 P2757R3 Type-checking format args # 202306 P2637R3 Member Visit "headers": ["format"], -- 2.7.4