diff options
author | Mark de Wever <koraq@xs4all.nl> | 2024-03-25 20:26:33 +0100 |
---|---|---|
committer | Mark de Wever <koraq@xs4all.nl> | 2024-03-30 19:52:56 +0100 |
commit | ac458a68865883b6f96bb68201ee6e627a8c0a54 (patch) | |
tree | b6e0cb0fb3a380b5cf35f3631204eb8bd44a8033 | |
parent | 6aa53888a8e8a6e3f0bd279539703f4d4701b4e7 (diff) |
[libc++][format] Implements P3107R5 in <format>.upstream/users/mordante/P3107R5_improves_print__format
This adds the new std::enable_nonlocking_formatter_optimization trait in
<format>. This trait will be used in std::print to implement the
performance benefits.
Implements parts of
- P3107R5 - Permit an efficient implementation of ``std::print``
-rw-r--r-- | libcxx/include/__format/formatter.h | 3 | ||||
-rw-r--r-- | libcxx/include/__format/formatter_bool.h | 6 | ||||
-rw-r--r-- | libcxx/include/__format/formatter_char.h | 10 | ||||
-rw-r--r-- | libcxx/include/__format/formatter_floating_point.h | 8 | ||||
-rw-r--r-- | libcxx/include/__format/formatter_integer.h | 33 | ||||
-rw-r--r-- | libcxx/include/__format/formatter_pointer.h | 8 | ||||
-rw-r--r-- | libcxx/include/__format/formatter_string.h | 27 | ||||
-rw-r--r-- | libcxx/include/format | 15 | ||||
-rw-r--r-- | libcxx/modules/std/format.inc | 2 | ||||
-rw-r--r-- | libcxx/test/std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp | 235 |
10 files changed, 337 insertions, 10 deletions
diff --git a/libcxx/include/__format/formatter.h b/libcxx/include/__format/formatter.h index 47e35789b817..0316354ba74a 100644 --- a/libcxx/include/__format/formatter.h +++ b/libcxx/include/__format/formatter.h @@ -41,6 +41,9 @@ struct _LIBCPP_TEMPLATE_VIS formatter { # if _LIBCPP_STD_VER >= 23 template <class _Tp> +constexpr bool enable_nonlocking_formatter_optimization = false; + +template <class _Tp> _LIBCPP_HIDE_FROM_ABI constexpr void __set_debug_format(_Tp& __formatter) { if constexpr (requires { __formatter.set_debug_format(); }) __formatter.set_debug_format(); diff --git a/libcxx/include/__format/formatter_bool.h b/libcxx/include/__format/formatter_bool.h index 5e3daff7b3db..9d9fcd2469ce 100644 --- a/libcxx/include/__format/formatter_bool.h +++ b/libcxx/include/__format/formatter_bool.h @@ -70,7 +70,11 @@ public: __format_spec::__parser<_CharT> __parser_; }; -#endif //_LIBCPP_STD_VER >= 20 +# if _LIBCPP_STD_VER >= 23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<bool> = true; +# endif //_LIBCPP_STD_VER >= 23 +#endif //_LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__format/formatter_char.h b/libcxx/include/__format/formatter_char.h index 3358d422252f..aa9c56538efd 100644 --- a/libcxx/include/__format/formatter_char.h +++ b/libcxx/include/__format/formatter_char.h @@ -84,9 +84,17 @@ struct _LIBCPP_TEMPLATE_VIS formatter<char, wchar_t> : public __formatter_char<w template <> struct _LIBCPP_TEMPLATE_VIS formatter<wchar_t, wchar_t> : public __formatter_char<wchar_t> {}; - # endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS +# if _LIBCPP_STD_VER >= 23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<char> = true; +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<wchar_t> = true; +# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS +# endif //_LIBCPP_STD_VER >= 23 + #endif //_LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__format/formatter_floating_point.h b/libcxx/include/__format/formatter_floating_point.h index 1d94cc349c0d..11aafab0db1f 100644 --- a/libcxx/include/__format/formatter_floating_point.h +++ b/libcxx/include/__format/formatter_floating_point.h @@ -774,6 +774,14 @@ struct _LIBCPP_TEMPLATE_VIS formatter<double, _CharT> : public __formatter_float template <__fmt_char_type _CharT> struct _LIBCPP_TEMPLATE_VIS formatter<long double, _CharT> : public __formatter_floating_point<_CharT> {}; +# if _LIBCPP_STD_VER >= 23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<float> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<double> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<long double> = true; +# endif //_LIBCPP_STD_VER >= 23 #endif //_LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__format/formatter_integer.h b/libcxx/include/__format/formatter_integer.h index d57082b3881b..d63617233d76 100644 --- a/libcxx/include/__format/formatter_integer.h +++ b/libcxx/include/__format/formatter_integer.h @@ -89,7 +89,38 @@ template <__fmt_char_type _CharT> struct _LIBCPP_TEMPLATE_VIS formatter<__uint128_t, _CharT> : public __formatter_integer<_CharT> {}; # endif -#endif //_LIBCPP_STD_VER >= 20 +# if _LIBCPP_STD_VER >= 23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<signed char> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<short> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<int> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<long> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<long long> = true; +# ifndef _LIBCPP_HAS_NO_INT128 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<__int128_t> = true; +# endif + +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<unsigned char> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<unsigned short> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<unsigned> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<unsigned long> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<unsigned long long> = true; +# ifndef _LIBCPP_HAS_NO_INT128 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<__uint128_t> = true; +# endif +# endif //_LIBCPP_STD_VER >= 23 +#endif //_LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__format/formatter_pointer.h b/libcxx/include/__format/formatter_pointer.h index 3373996ec3d5..26788b944db1 100644 --- a/libcxx/include/__format/formatter_pointer.h +++ b/libcxx/include/__format/formatter_pointer.h @@ -66,6 +66,14 @@ struct _LIBCPP_TEMPLATE_VIS formatter<void*, _CharT> : public __formatter_pointe template <__fmt_char_type _CharT> struct _LIBCPP_TEMPLATE_VIS formatter<const void*, _CharT> : public __formatter_pointer<_CharT> {}; +# if _LIBCPP_STD_VER >= 23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<nullptr_t> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<void*> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<const void*> = true; +# endif //_LIBCPP_STD_VER >= 23 #endif //_LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__format/formatter_string.h b/libcxx/include/__format/formatter_string.h index d1ccfb9b5f7d..7da5d63d4487 100644 --- a/libcxx/include/__format/formatter_string.h +++ b/libcxx/include/__format/formatter_string.h @@ -144,7 +144,32 @@ struct _LIBCPP_TEMPLATE_VIS formatter<basic_string_view<_CharT, _Traits>, _CharT } }; -#endif //_LIBCPP_STD_VER >= 20 +# if _LIBCPP_STD_VER >= 23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<char*> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<const char*> = true; +template <size_t _Size> +inline constexpr bool enable_nonlocking_formatter_optimization<char[_Size]> = true; +template <class _Traits, class _Allocator> +inline constexpr bool enable_nonlocking_formatter_optimization<basic_string<char, _Traits, _Allocator>> = true; +template <class _Traits> +inline constexpr bool enable_nonlocking_formatter_optimization<basic_string_view<char, _Traits>> = true; + +# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<wchar_t*> = true; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<const wchar_t*> = true; +template <size_t _Size> +inline constexpr bool enable_nonlocking_formatter_optimization<wchar_t[_Size]> = true; +template <class _Traits, class _Allocator> +inline constexpr bool enable_nonlocking_formatter_optimization<basic_string<wchar_t, _Traits, _Allocator>> = true; +template <class _Traits> +inline constexpr bool enable_nonlocking_formatter_optimization<basic_string_view<wchar_t, _Traits>> = true; +# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS +# endif //_LIBCPP_STD_VER >= 23 +#endif //_LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/format b/libcxx/include/format index f1e87de0f830..e037880a3053 100644 --- a/libcxx/include/format +++ b/libcxx/include/format @@ -126,6 +126,9 @@ namespace std { // [format.formatter], formatter template<class T, class charT = char> struct formatter; + template<class T> + constexpr bool enable_nonlocking_formatter_optimization = false; // since C++23 + // [format.parse.ctx], class template basic_format_parse_context template<class charT> class basic_format_parse_context; using format_parse_context = basic_format_parse_context<char>; @@ -133,7 +136,7 @@ namespace std { // [format.range], formatting of ranges // [format.range.fmtkind], variable template format_kind - enum class range_format { // since C++23 + enum class range_format { // since C++23 disabled, map, set, @@ -143,20 +146,20 @@ namespace std { }; template<class R> - constexpr unspecified format_kind = unspecified; // since C++23 + constexpr unspecified format_kind = unspecified; // since C++23 template<ranges::input_range R> requires same_as<R, remove_cvref_t<R>> - constexpr range_format format_kind<R> = see below; // since C++23 + constexpr range_format format_kind<R> = see below; // since C++23 // [format.range.formatter], class template range_formatter template<class T, class charT = char> requires same_as<remove_cvref_t<T>, T> && formattable<T, charT> - class range_formatter; // since C++23 + class range_formatter; // since C++23 // [format.range.fmtdef], class template range-default-formatter template<range_format K, ranges::input_range R, class charT> - struct range-default-formatter; // exposition only, since C++23 + struct range-default-formatter; // exposition only, since C++23 // [format.range.fmtmap], [format.range.fmtset], [format.range.fmtstr], // specializations for maps, sets, and strings @@ -173,7 +176,7 @@ namespace std { see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg); // Deprecated in C++26 // [format.arg.store], class template format-arg-store - template<class Context, class... Args> struct format-arg-store; // exposition only + template<class Context, class... Args> struct format-arg-store; // exposition only template<class Context = format_context, class... Args> format-arg-store<Context, Args...> diff --git a/libcxx/modules/std/format.inc b/libcxx/modules/std/format.inc index 743a43811005..09aa03ad73e3 100644 --- a/libcxx/modules/std/format.inc +++ b/libcxx/modules/std/format.inc @@ -46,6 +46,8 @@ export namespace std { using std::formatter; #if _LIBCPP_STD_VER >= 23 + using std::enable_nonlocking_formatter_optimization; + // [format.formattable], concept formattable using std::formattable; #endif diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp new file mode 100644 index 000000000000..fb078fdc7559 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp @@ -0,0 +1,235 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <format> + +// template<class T> +// constexpr bool enable_nonlocking_formatter_optimization = false; + +// Remarks: Pursuant to [namespace.std], users may specialize +// enable_nonlocking_formatter_optimization for cv-unqualified program-defined +// types. Such specializations shall be usable in constant expressions +// ([expr.const]) and have type const bool. + +// [format.formatter.spec] +// In addition, for each type T for which a formatter specialization is provided +// above, each of the headers provides the following specialization: +// +// template<> +// inline constexpr bool enable_nonlocking_formatter_optimization<T> = true; + +#include <array> +#include <bitset> +#include <bitset> +#include <chrono> +#include <complex> +#include <concepts> +#include <deque> +#include <filesystem> +#include <format> +#include <forward_list> +#include <list> +#include <map> +#include <memory> +#include <optional> +#include <queue> +#include <set> +#include <span> +#include <stack> +#include <system_error> +#include <tuple> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> +#include <valarray> +#include <variant> +#include <vector> + +#include "test_macros.h" +#include "min_allocator.h" + +#ifndef TEST_HAS_NO_LOCALIZATION +# include <regex> +#endif +#ifndef TEST_HAS_NO_THREADS +# include <thread> +#endif + +// Tests for P0645 Text Formatting +template <class CharT> +void test_P0645() { + static_assert(std::enable_nonlocking_formatter_optimization<CharT>); + + static_assert(std::enable_nonlocking_formatter_optimization<CharT*>); + static_assert(std::enable_nonlocking_formatter_optimization<const CharT*>); + static_assert(std::enable_nonlocking_formatter_optimization<CharT[42]>); + + static_assert(std::enable_nonlocking_formatter_optimization<std::basic_string<CharT>>); + static_assert(std::enable_nonlocking_formatter_optimization<std::basic_string_view<CharT>>); + + static_assert(std::enable_nonlocking_formatter_optimization<bool>); + + static_assert(std::enable_nonlocking_formatter_optimization<signed char>); + static_assert(std::enable_nonlocking_formatter_optimization<signed short>); + static_assert(std::enable_nonlocking_formatter_optimization<signed int>); + static_assert(std::enable_nonlocking_formatter_optimization<signed long>); + static_assert(std::enable_nonlocking_formatter_optimization<signed long long>); +#ifndef TEST_HAS_NO_INT128 + static_assert(std::enable_nonlocking_formatter_optimization<__int128_t>); +#endif + + static_assert(std::enable_nonlocking_formatter_optimization<unsigned char>); + static_assert(std::enable_nonlocking_formatter_optimization<unsigned short>); + static_assert(std::enable_nonlocking_formatter_optimization<unsigned int>); + static_assert(std::enable_nonlocking_formatter_optimization<unsigned long>); + static_assert(std::enable_nonlocking_formatter_optimization<unsigned long long>); +#ifndef TEST_HAS_NO_INT128 + static_assert(std::enable_nonlocking_formatter_optimization<__uint128_t>); +#endif + + static_assert(std::enable_nonlocking_formatter_optimization<float>); + static_assert(std::enable_nonlocking_formatter_optimization<double>); + static_assert(std::enable_nonlocking_formatter_optimization<long double>); + + static_assert(std::enable_nonlocking_formatter_optimization<std::nullptr_t>); + static_assert(std::enable_nonlocking_formatter_optimization<void*>); + static_assert(std::enable_nonlocking_formatter_optimization<const void*>); +} + +// Tests for P1361 Integration of chrono with text formatting +// +// Some tests are commented out since these types haven't been implemented in +// chrono yet. After P1361 has been implemented these formatters should be all +// enabled. +void test_P1361() { +// The chrono formatters require localization support. +// [time.format]/7 +// If the chrono-specs is omitted, the chrono object is formatted as if by +// streaming it to std::ostringstream os with the formatting +// locale imbued and copying os.str() through the output iterator of the +// context with additional padding and adjustments as specified by the format +// specifiers. +// In libc++ std:::ostringstream requires localization support. +#ifndef TEST_HAS_NO_LOCALIZATION + + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::microseconds>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::sys_time<std::chrono::microseconds>>); + //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::utc_time<std::chrono::microseconds>>); + //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::tai_time<std::chrono::microseconds>>); + //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::gps_time<std::chrono::microseconds>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::file_time<std::chrono::microseconds>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::local_time<std::chrono::microseconds>>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::day>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::month>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::weekday>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::weekday_indexed>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::weekday_last>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::month_day>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::month_day_last>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::month_weekday>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::month_weekday_last>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year_month>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year_month_day>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year_month_day_last>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year_month_weekday>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::year_month_weekday_last>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::hh_mm_ss<std::chrono::microseconds>>); + + //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::sys_info>); + //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::local_info>); + + //static_assert(!std::enable_nonlocking_formatter_optimization<std::chrono::zoned_time>); + +#endif // TEST_HAS_NO_LOCALIZATION +} + +// Tests for P1636 Formatters for library types +// +// The paper hasn't been voted in so currently all formatters are disabled. +// Note the paper has been abandoned, the types are kept since other papers may +// introduce these formatters. +void test_P1636() { +#ifndef TEST_HAS_NO_THREADS + static_assert(!std::enable_nonlocking_formatter_optimization<std::thread::id>); +#endif +} + +template <class Vector> +void test_P2286_vector_bool() { + static_assert(!std::enable_nonlocking_formatter_optimization<Vector>); + static_assert(!std::enable_nonlocking_formatter_optimization<typename Vector::reference>); + + // The const_reference shall be a bool. + // However libc++ uses a __bit_const_reference<vector> when + // _LIBCPP_ABI_BITSET_VECTOR_BOOL_CONST_SUBSCRIPT_RETURN_BOOL is defined. + static_assert(!std::enable_nonlocking_formatter_optimization<const Vector&>); + static_assert(std::enable_nonlocking_formatter_optimization<typename Vector::const_reference> == + std::same_as<typename Vector::const_reference, bool>); +} + +// Tests for P2286 Formatting ranges +void test_P2286() { + static_assert(!std::enable_nonlocking_formatter_optimization<std::array<int, 42>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::vector<int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::deque<int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::forward_list<int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::list<int>>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::set<int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::map<int, int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::multiset<int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::multimap<int, int>>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::unordered_set<int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::unordered_map<int, int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::unordered_multiset<int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::unordered_multimap<int, int>>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::stack<int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::queue<int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::priority_queue<int>>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::span<int>>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::valarray<int>>); + + static_assert(!std::enable_nonlocking_formatter_optimization<std::pair<int, int>>); + static_assert(!std::enable_nonlocking_formatter_optimization<std::tuple<int>>); + + test_P2286_vector_bool<std::vector<bool>>(); + test_P2286_vector_bool<std::vector<bool, std::allocator<bool>>>(); + test_P2286_vector_bool<std::vector<bool, min_allocator<bool>>>(); +} + +// The trait does not care about whether the type is formattable, obviously the +// trait for non formattable types are not used. +struct not_formattable_nonlocking_disabled {}; +static_assert(!std::enable_nonlocking_formatter_optimization<not_formattable_nonlocking_disabled>); + +struct not_formattable_nonlocking_enabled {}; +template <> +inline constexpr bool std::enable_nonlocking_formatter_optimization<not_formattable_nonlocking_enabled> = true; +static_assert(std::enable_nonlocking_formatter_optimization<not_formattable_nonlocking_enabled>); + +void test() { + test_P0645<char>(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_P0645<wchar_t>(); +#endif + test_P1361(); + test_P1636(); + test_P2286(); +} |