From 860b13e4b55689a7acb2b50f83eb32ef2d59b384 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Wed, 6 Dec 2023 11:35:54 -0500 Subject: [PATCH 1/2] [libc++] Remove assumptions that std::array::iterator is a raw pointer This patch removes assumptions that std::array's iterators are raw pointers in the source code and in our test suite. While this is true right now, this doesn't have to be true and ion the future we might want to enable bounded iterators in std::array, which would require this change. This is a pre-requisite for landing #74482 --- libcxx/include/__format/buffer.h | 12 +- libcxx/include/__format/formatter_integral.h | 32 +- libcxx/include/__format/formatter_output.h | 34 +- .../print.fun/transcoding.pass.cpp | 5 +- ...nst_proxy_iterators_lifetime_bugs.pass.cpp | 18 +- .../containers/sequences/array/types.pass.cpp | 4 - .../span.cons/iterator_sentinel.pass.cpp | 5 +- .../formatter.floating_point.pass.cpp | 594 ++++++++++-------- .../formatter.pointer.pass.cpp | 4 +- 9 files changed, 402 insertions(+), 306 deletions(-) diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h index 8aa58d6464bbf..24608a0b1d200 100644 --- a/libcxx/include/__format/buffer.h +++ b/libcxx/include/__format/buffer.h @@ -130,8 +130,10 @@ class _LIBCPP_TEMPLATE_VIS __output_buffer { /// A std::transform wrapper. /// /// Like @ref __copy it may need to do type conversion. - template <__fmt_char_type _InCharT, class _UnaryOperation> - _LIBCPP_HIDE_FROM_ABI void __transform(const _InCharT* __first, const _InCharT* __last, _UnaryOperation __operation) { + template ::value_type> + _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) { _LIBCPP_ASSERT_UNCATEGORIZED(__first <= __last, "not a valid range"); size_t __n = static_cast(__last - __first); @@ -590,8 +592,10 @@ class _LIBCPP_TEMPLATE_VIS __retarget_buffer { __size_ += __n; } - template <__fmt_char_type _InCharT, class _UnaryOperation> - _LIBCPP_HIDE_FROM_ABI void __transform(const _InCharT* __first, const _InCharT* __last, _UnaryOperation __operation) { + template ::value_type> + _LIBCPP_HIDE_FROM_ABI void __transform(_Iterator __first, _Iterator __last, _UnaryOperation __operation) { _LIBCPP_ASSERT_UNCATEGORIZED(__first <= __last, "not a valid range"); size_t __n = static_cast(__last - __first); diff --git a/libcxx/include/__format/formatter_integral.h b/libcxx/include/__format/formatter_integral.h index 598decb0a95ea..93a41f9417588 100644 --- a/libcxx/include/__format/formatter_integral.h +++ b/libcxx/include/__format/formatter_integral.h @@ -20,6 +20,8 @@ #include <__format/format_error.h> #include <__format/formatter_output.h> #include <__format/parser_std_format_spec.h> +#include <__iterator/concepts.h> +#include <__memory/pointer_traits.h> #include <__system_error/errc.h> #include <__type_traits/make_unsigned.h> #include <__utility/unreachable.h> @@ -49,7 +51,8 @@ namespace __formatter { // Generic // -_LIBCPP_HIDE_FROM_ABI inline char* __insert_sign(char* __buf, bool __negative, __format_spec::__sign __sign) { +template +_LIBCPP_HIDE_FROM_ABI inline _Iterator __insert_sign(_Iterator __buf, bool __negative, __format_spec::__sign __sign) { if (__negative) *__buf++ = '-'; else @@ -148,14 +151,15 @@ _LIBCPP_HIDE_FROM_ABI auto __format_char( // Integer // -/** Wrapper around @ref to_chars, returning the output pointer. */ -template -_LIBCPP_HIDE_FROM_ABI char* __to_buffer(char* __first, char* __last, _Tp __value, int __base) { +/** Wrapper around @ref to_chars, returning the output iterator. */ +template +_LIBCPP_HIDE_FROM_ABI _Iterator __to_buffer(_Iterator __first, _Iterator __last, _Tp __value, int __base) { // TODO FMT Evaluate code overhead due to not calling the internal function // directly. (Should be zero overhead.) - to_chars_result __r = std::to_chars(__first, __last, __value, __base); + to_chars_result __r = std::to_chars(std::to_address(__first), std::to_address(__last), __value, __base); _LIBCPP_ASSERT_UNCATEGORIZED(__r.ec == errc(0), "Internal buffer too small"); - return __r.ptr; + auto __diff = __r.ptr - std::to_address(__first); + return __first + __diff; } /** @@ -203,9 +207,9 @@ consteval size_t __buffer_size() noexcept + 1; // Reserve space for the sign. } -template -_LIBCPP_HIDE_FROM_ABI _OutIt __write_using_decimal_separators(_OutIt __out_it, const char* __begin, const char* __first, - const char* __last, string&& __grouping, _CharT __sep, +template +_LIBCPP_HIDE_FROM_ABI _OutIt __write_using_decimal_separators(_OutIt __out_it, _Iterator __begin, _Iterator __first, + _Iterator __last, string&& __grouping, _CharT __sep, __format_spec::__parsed_specifications<_CharT> __specs) { int __size = (__first - __begin) + // [sign][prefix] (__last - __first) + // data @@ -269,22 +273,22 @@ _LIBCPP_HIDE_FROM_ABI _OutIt __write_using_decimal_separators(_OutIt __out_it, c -template +template _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator __format_integer( _Tp __value, _FormatContext& __ctx, __format_spec::__parsed_specifications<_CharT> __specs, bool __negative, - char* __begin, - char* __end, + _Iterator __begin, + _Iterator __end, const char* __prefix, int __base) { - char* __first = __formatter::__insert_sign(__begin, __negative, __specs.__std_.__sign_); + _Iterator __first = __formatter::__insert_sign(__begin, __negative, __specs.__std_.__sign_); if (__specs.__std_.__alternate_form_ && __prefix) while (*__prefix) *__first++ = *__prefix++; - char* __last = __formatter::__to_buffer(__first, __end, __value, __base); + _Iterator __last = __formatter::__to_buffer(__first, __end, __value, __base); # ifndef _LIBCPP_HAS_NO_LOCALIZATION if (__specs.__std_.__locale_specific_form_) { diff --git a/libcxx/include/__format/formatter_output.h b/libcxx/include/__format/formatter_output.h index 2909fcd9baf1e..6c7892d86900d 100644 --- a/libcxx/include/__format/formatter_output.h +++ b/libcxx/include/__format/formatter_output.h @@ -23,8 +23,9 @@ #include <__format/unicode.h> #include <__iterator/back_insert_iterator.h> #include <__iterator/concepts.h> -#include <__iterator/iterator_traits.h> // iter_value_t +#include <__iterator/iterator_traits.h> #include <__memory/addressof.h> +#include <__memory/pointer_traits.h> #include <__utility/move.h> #include <__utility/unreachable.h> #include @@ -110,26 +111,32 @@ _LIBCPP_HIDE_FROM_ABI auto __copy(basic_string_view<_CharT> __str, output_iterat } } -template <__fmt_char_type _CharT, __fmt_char_type _OutCharT = _CharT> -_LIBCPP_HIDE_FROM_ABI auto -__copy(const _CharT* __first, const _CharT* __last, output_iterator auto __out_it) +template ::value_type, + __fmt_char_type _OutCharT = _CharT> +_LIBCPP_HIDE_FROM_ABI auto __copy(_Iterator __first, _Iterator __last, output_iterator auto __out_it) -> decltype(__out_it) { return __formatter::__copy(basic_string_view{__first, __last}, std::move(__out_it)); } -template <__fmt_char_type _CharT, __fmt_char_type _OutCharT = _CharT> -_LIBCPP_HIDE_FROM_ABI auto __copy(const _CharT* __first, size_t __n, output_iterator auto __out_it) +template ::value_type, + __fmt_char_type _OutCharT = _CharT> +_LIBCPP_HIDE_FROM_ABI auto __copy(_Iterator __first, size_t __n, output_iterator auto __out_it) -> decltype(__out_it) { - return __formatter::__copy(basic_string_view{__first, __n}, std::move(__out_it)); + return __formatter::__copy(basic_string_view{std::to_address(__first), __n}, std::move(__out_it)); } /// Transform wrapper. /// /// This uses a "mass output function" of __format::__output_buffer when possible. -template <__fmt_char_type _CharT, __fmt_char_type _OutCharT = _CharT, class _UnaryOperation> +template ::value_type, + __fmt_char_type _OutCharT = _CharT, + class _UnaryOperation> _LIBCPP_HIDE_FROM_ABI auto -__transform(const _CharT* __first, - const _CharT* __last, +__transform(_Iterator __first, + _Iterator __last, output_iterator auto __out_it, _UnaryOperation __operation) -> decltype(__out_it) { if constexpr (std::same_as>>) { @@ -260,8 +267,11 @@ __write(_Iterator __first, return __formatter::__write(__first, __last, std::move(__out_it), __specs, __last - __first); } -template -_LIBCPP_HIDE_FROM_ABI auto __write_transformed(const _CharT* __first, const _CharT* __last, +template ::value_type, + class _ParserCharT, + class _UnaryOperation> +_LIBCPP_HIDE_FROM_ABI auto __write_transformed(_Iterator __first, _Iterator __last, output_iterator auto __out_it, __format_spec::__parsed_specifications<_ParserCharT> __specs, _UnaryOperation __op) -> decltype(__out_it) { diff --git a/libcxx/test/libcxx/input.output/iostream.format/print.fun/transcoding.pass.cpp b/libcxx/test/libcxx/input.output/iostream.format/print.fun/transcoding.pass.cpp index 3936edb8bd083..168fa40a243c5 100644 --- a/libcxx/test/libcxx/input.output/iostream.format/print.fun/transcoding.pass.cpp +++ b/libcxx/test/libcxx/input.output/iostream.format/print.fun/transcoding.pass.cpp @@ -8,6 +8,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 // UNSUPPORTED: no-filesystem // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME +// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000 // @@ -32,9 +33,9 @@ constexpr void test(std::basic_string_view expected, std::string_view inp std::array buffer; std::ranges::fill(buffer, CharT('*')); - CharT* out = std::__unicode::__transcode(input.begin(), input.end(), buffer.data()); + auto out = std::__unicode::__transcode(input.begin(), input.end(), buffer.begin()); - assert(std::basic_string_view(buffer.data(), out) == expected); + assert(std::basic_string_view(buffer.begin(), out) == expected); out = std::find_if(out, buffer.end(), [](CharT c) { return c != CharT('*'); }); assert(out == buffer.end()); diff --git a/libcxx/test/std/algorithms/robust_against_proxy_iterators_lifetime_bugs.pass.cpp b/libcxx/test/std/algorithms/robust_against_proxy_iterators_lifetime_bugs.pass.cpp index 3a335c44ed1e1..0de22022526a6 100644 --- a/libcxx/test/std/algorithms/robust_against_proxy_iterators_lifetime_bugs.pass.cpp +++ b/libcxx/test/std/algorithms/robust_against_proxy_iterators_lifetime_bugs.pass.cpp @@ -547,24 +547,22 @@ class ConstexprIterator { #endif // TEST_STD_VER > 17 -template +template class Input { - using Array = std::array; - std::size_t size_ = 0; - Array values_ = {}; + T values_[StorageSize] = {}; public: - template - TEST_CONSTEXPR_CXX20 Input(std::array from) { - static_assert(N2 <= N, ""); + template + TEST_CONSTEXPR_CXX20 Input(std::array from) { + static_assert(N <= StorageSize, ""); std::copy(from.begin(), from.end(), begin()); - size_ = N2; + size_ = N; } - TEST_CONSTEXPR_CXX20 typename Array::iterator begin() { return values_.begin(); } - TEST_CONSTEXPR_CXX20 typename Array::iterator end() { return values_.begin() + size_; } + TEST_CONSTEXPR_CXX20 T* begin() { return values_; } + TEST_CONSTEXPR_CXX20 T* end() { return values_ + size_; } TEST_CONSTEXPR_CXX20 std::size_t size() const { return size_; } }; diff --git a/libcxx/test/std/containers/sequences/array/types.pass.cpp b/libcxx/test/std/containers/sequences/array/types.pass.cpp index f86e008d2e8de..c509810507962 100644 --- a/libcxx/test/std/containers/sequences/array/types.pass.cpp +++ b/libcxx/test/std/containers/sequences/array/types.pass.cpp @@ -54,8 +54,6 @@ int main(int, char**) typedef std::array C; static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); - LIBCPP_STATIC_ASSERT((std::is_same::value), ""); - LIBCPP_STATIC_ASSERT((std::is_same::value), ""); test_iterators(); static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); @@ -76,8 +74,6 @@ int main(int, char**) typedef std::array C; static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); - LIBCPP_STATIC_ASSERT((std::is_same::value), ""); - LIBCPP_STATIC_ASSERT((std::is_same::value), ""); test_iterators(); static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); diff --git a/libcxx/test/std/containers/views/views.span/span.cons/iterator_sentinel.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/iterator_sentinel.pass.cpp index 73b13ccc34cf8..e893b5ae62874 100644 --- a/libcxx/test/std/containers/views/views.span/span.cons/iterator_sentinel.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/span.cons/iterator_sentinel.pass.cpp @@ -114,7 +114,10 @@ class throw_operator_minus { friend difference_type operator-(throw_operator_minus, throw_operator_minus) { throw 42; }; friend bool operator==(const throw_operator_minus& x, const throw_operator_minus& y) { return x.it_ == y.it_; } - friend auto operator<=>(const throw_operator_minus& x, const throw_operator_minus& y) { return x.it_ <=> y.it_; } + friend bool operator<(const throw_operator_minus& x, const throw_operator_minus& y) { return x.it_ < y.it_; } + friend bool operator>(const throw_operator_minus& x, const throw_operator_minus& y) { return x.it_ > y.it_; } + friend bool operator<=(const throw_operator_minus& x, const throw_operator_minus& y) { return x.it_ <= y.it_; } + friend bool operator>=(const throw_operator_minus& x, const throw_operator_minus& y) { return x.it_ >= y.it_; } }; template diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp index 87e2f9628757b..2df7834477291 100644 --- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp @@ -61,7 +61,7 @@ void test(std::basic_string_view fmt, ArithmeticT arg, std::basic_string< if (expected.empty()) { std::array buffer; - expected.append(buffer.begin(), std::to_chars(buffer.begin(), buffer.end(), arg).ptr); + expected.append(buffer.data(), std::to_chars(buffer.data(), buffer.data() + buffer.size(), arg).ptr); } assert(result == expected); @@ -84,321 +84,401 @@ void test_termination_condition(StringT f, ArithmeticT arg, StringT expected = { template void test_hex_lower_case_precision(ArithmeticT value) { - std::array buffer; - char* end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::hex, 20'000).ptr; - test_termination_condition(STR(".20000a}"), value, std::basic_string{buffer.begin(), end}); - - std::size_t size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000a}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000a}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000a}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::hex, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + test_termination_condition(STR(".20000a}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000a}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000a}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000a}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000a}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000a}"), value, std::basic_string{buffer.begin(), buffer.end()}); + #ifndef TEST_HAS_NO_LOCALIZATION - end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::hex, 20'000).ptr; - test_termination_condition(STR(".20000La}"), value, std::basic_string{buffer.begin(), end}); - - size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000La}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000La}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000La}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::hex, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + test_termination_condition(STR(".20000La}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000La}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000La}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000La}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000La}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000La}"), value, std::basic_string{buffer.begin(), buffer.end()}); #endif } template void test_hex_upper_case_precision(ArithmeticT value) { - std::array buffer; - char* end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::hex, 20'000).ptr; - std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); - test_termination_condition(STR(".20000A}"), value, std::basic_string{buffer.begin(), end}); - - std::size_t size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000A}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000A}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000A}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::hex, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); + test_termination_condition(STR(".20000A}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000A}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000A}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000A}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000A}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000A}"), value, std::basic_string{buffer.begin(), buffer.end()}); + #ifndef TEST_HAS_NO_LOCALIZATION - end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::hex, 20'000).ptr; - std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); - test_termination_condition(STR(".20000LA}"), value, std::basic_string{buffer.begin(), end}); - - size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000LA}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000LA}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000LA}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::hex, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); + test_termination_condition(STR(".20000LA}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000LA}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000LA}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000LA}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000LA}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000LA}"), value, std::basic_string{buffer.begin(), buffer.end()}); #endif } template void test_scientific_lower_case_precision(ArithmeticT value) { - std::array buffer; - char* end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::scientific, 20'000).ptr; - test_termination_condition(STR(".20000e}"), value, std::basic_string{buffer.begin(), end}); - - std::size_t size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000e}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000e}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000e}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::scientific, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + test_termination_condition(STR(".20000e}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000e}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000e}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000e}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000e}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000e}"), value, std::basic_string{buffer.begin(), buffer.end()}); + #ifndef TEST_HAS_NO_LOCALIZATION - end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::scientific, 20'000).ptr; - test_termination_condition(STR(".20000Le}"), value, std::basic_string{buffer.begin(), end}); - - size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000Le}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000Le}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000Le}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::scientific, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + test_termination_condition(STR(".20000Le}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000Le}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000Le}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000Le}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000Le}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000Le}"), value, std::basic_string{buffer.begin(), buffer.end()}); #endif } template void test_scientific_upper_case_precision(ArithmeticT value) { - std::array buffer; - char* end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::scientific, 20'000).ptr; - std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); - test_termination_condition(STR(".20000E}"), value, std::basic_string{buffer.begin(), end}); - - std::size_t size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000E}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000E}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000E}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::scientific, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); + test_termination_condition(STR(".20000E}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000E}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000E}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000E}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000E}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000E}"), value, std::basic_string{buffer.begin(), buffer.end()}); + #ifndef TEST_HAS_NO_LOCALIZATION - end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::scientific, 20'000).ptr; - std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); - test_termination_condition(STR(".20000LE}"), value, std::basic_string{buffer.begin(), end}); - - size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000LE}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000LE}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000LE}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::scientific, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); + test_termination_condition(STR(".20000LE}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000LE}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000LE}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000LE}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000LE}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000LE}"), value, std::basic_string{buffer.begin(), buffer.end()}); #endif } template void test_fixed_lower_case_precision(ArithmeticT value) { - std::array buffer; - char* end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::fixed, 20'000).ptr; - test_termination_condition(STR(".20000f}"), value, std::basic_string{buffer.begin(), end}); - - std::size_t size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000f}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000f}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000f}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::fixed, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + test_termination_condition(STR(".20000f}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000f}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000f}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000f}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000f}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000f}"), value, std::basic_string{buffer.begin(), buffer.end()}); + #ifndef TEST_HAS_NO_LOCALIZATION - end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::fixed, 20'000).ptr; - test_termination_condition(STR(".20000Lf}"), value, std::basic_string{buffer.begin(), end}); - - size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000Lf}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000Lf}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000Lf}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::fixed, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + test_termination_condition(STR(".20000Lf}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000Lf}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000Lf}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000Lf}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000Lf}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000Lf}"), value, std::basic_string{buffer.begin(), buffer.end()}); #endif } template void test_fixed_upper_case_precision(ArithmeticT value) { - std::array buffer; - char* end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::fixed, 20'000).ptr; - std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); - test_termination_condition(STR(".20000F}"), value, std::basic_string{buffer.begin(), end}); - - std::size_t size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000F}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000F}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000F}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::fixed, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); + test_termination_condition(STR(".20000F}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000F}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000F}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000F}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000F}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000F}"), value, std::basic_string{buffer.begin(), buffer.end()}); + #ifndef TEST_HAS_NO_LOCALIZATION - end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::fixed, 20'000).ptr; - std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); - test_termination_condition(STR(".20000LF}"), value, std::basic_string{buffer.begin(), end}); - - size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000LF}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000LF}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000LF}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::fixed, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); + test_termination_condition(STR(".20000LF}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000LF}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000LF}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000LF}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000LF}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000LF}"), value, std::basic_string{buffer.begin(), buffer.end()}); #endif } template void test_general_lower_case_precision(ArithmeticT value) { - std::array buffer; - char* end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::general, 20'000).ptr; - test_termination_condition(STR(".20000g}"), value, std::basic_string{buffer.begin(), end}); - - std::size_t size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000g}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000g}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000g}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::general, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + test_termination_condition(STR(".20000g}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000g}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000g}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000g}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000g}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000g}"), value, std::basic_string{buffer.begin(), buffer.end()}); + #ifndef TEST_HAS_NO_LOCALIZATION - end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::general, 20'000).ptr; - test_termination_condition(STR(".20000Lg}"), value, std::basic_string{buffer.begin(), end}); - - size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000Lg}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000Lg}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000Lg}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::general, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + test_termination_condition(STR(".20000Lg}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000Lg}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000Lg}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000Lg}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000Lg}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000Lg}"), value, std::basic_string{buffer.begin(), buffer.end()}); #endif } template void test_general_upper_case_precision(ArithmeticT value) { - std::array buffer; - char* end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::general, 20'000).ptr; - std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); - test_termination_condition(STR(".20000G}"), value, std::basic_string{buffer.begin(), end}); - - std::size_t size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000G}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000G}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000G}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::general, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); + test_termination_condition(STR(".20000G}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000G}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000G}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000G}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000G}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000G}"), value, std::basic_string{buffer.begin(), buffer.end()}); + #ifndef TEST_HAS_NO_LOCALIZATION - end = std::to_chars(buffer.begin(), buffer.end(), value, std::chars_format::general, 20'000).ptr; - std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); - test_termination_condition(STR(".20000LG}"), value, std::basic_string{buffer.begin(), end}); - - size = buffer.end() - end; - std::fill_n(end, size, '#'); - test_termination_condition(STR("#<25000.20000LG}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - (size / 2), buffer.end()); - test_termination_condition(STR("#^25000.20000LG}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::rotate(buffer.begin(), buffer.end() - ((size + 1) / 2), buffer.end()); - test_termination_condition(STR("#>25000.20000LG}"), value, std::basic_string{buffer.begin(), buffer.end()}); - std::fill_n(buffer.begin(), size, '0'); - if (std::signbit(value)) { - buffer[0] = '-'; - buffer[size] = '0'; + { + std::array buffer; + char* end_ptr = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value, std::chars_format::general, 20'000).ptr; + std::size_t size = end_ptr - buffer.data(); + auto end = buffer.begin() + size; + std::transform(buffer.begin(), end, buffer.begin(), [](char c) { return std::toupper(c); }); + test_termination_condition(STR(".20000LG}"), value, std::basic_string{buffer.begin(), end}); + + std::size_t unused = buffer.end() - end; + std::fill_n(end, unused, '#'); + test_termination_condition(STR("#<25000.20000LG}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - (unused / 2), buffer.end()); + test_termination_condition(STR("#^25000.20000LG}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::rotate(buffer.begin(), buffer.end() - ((unused + 1) / 2), buffer.end()); + test_termination_condition(STR("#>25000.20000LG}"), value, std::basic_string{buffer.begin(), buffer.end()}); + std::fill_n(buffer.begin(), unused, '0'); + if (std::signbit(value)) { + buffer[0] = '-'; + buffer[unused] = '0'; + } + test_termination_condition(STR("025000.20000LG}"), value, std::basic_string{buffer.begin(), buffer.end()}); } - test_termination_condition(STR("025000.20000LG}"), value, std::basic_string{buffer.begin(), buffer.end()}); #endif } diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.pointer.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.pointer.pass.cpp index 347458d7d7562..ff5bfe0fb472a 100644 --- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.pointer.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.pointer.pass.cpp @@ -58,8 +58,8 @@ void test(StringT expected, StringViewT fmt, PointerT arg, std::size_t offset) { std::array buffer; buffer[0] = CharT('0'); buffer[1] = CharT('x'); - expected.append(buffer.begin(), - std::to_chars(buffer.begin() + 2, buffer.end(), reinterpret_cast(arg), 16).ptr); + expected.append(buffer.data(), + std::to_chars(buffer.data() + 2, buffer.data() + buffer.size(), reinterpret_cast(arg), 16).ptr); } assert(result == expected); } From 489fc76e28a2ca39fb902a1dc3f8c211d1485d5e Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 12 Dec 2023 10:27:55 -0500 Subject: [PATCH 2/2] Add missing constraints for iter_value_t --- libcxx/include/__format/formatter_integral.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libcxx/include/__format/formatter_integral.h b/libcxx/include/__format/formatter_integral.h index 93a41f9417588..cbb3505bca2f2 100644 --- a/libcxx/include/__format/formatter_integral.h +++ b/libcxx/include/__format/formatter_integral.h @@ -21,6 +21,7 @@ #include <__format/formatter_output.h> #include <__format/parser_std_format_spec.h> #include <__iterator/concepts.h> +#include <__iterator/iterator_traits.h> #include <__memory/pointer_traits.h> #include <__system_error/errc.h> #include <__type_traits/make_unsigned.h> @@ -52,6 +53,7 @@ namespace __formatter { // template + requires same_as> _LIBCPP_HIDE_FROM_ABI inline _Iterator __insert_sign(_Iterator __buf, bool __negative, __format_spec::__sign __sign) { if (__negative) *__buf++ = '-'; @@ -153,6 +155,7 @@ _LIBCPP_HIDE_FROM_ABI auto __format_char( /** Wrapper around @ref to_chars, returning the output iterator. */ template + requires same_as> _LIBCPP_HIDE_FROM_ABI _Iterator __to_buffer(_Iterator __first, _Iterator __last, _Tp __value, int __base) { // TODO FMT Evaluate code overhead due to not calling the internal function // directly. (Should be zero overhead.) @@ -208,6 +211,7 @@ consteval size_t __buffer_size() noexcept } template + requires same_as> _LIBCPP_HIDE_FROM_ABI _OutIt __write_using_decimal_separators(_OutIt __out_it, _Iterator __begin, _Iterator __first, _Iterator __last, string&& __grouping, _CharT __sep, __format_spec::__parsed_specifications<_CharT> __specs) { @@ -274,6 +278,7 @@ _LIBCPP_HIDE_FROM_ABI _OutIt __write_using_decimal_separators(_OutIt __out_it, _ template + requires same_as> _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator __format_integer( _Tp __value, _FormatContext& __ctx,