[PATCH] libstdc++: Ensure that _Utf_view is always a view.
Hewill Kang
hewillk@gmail.com
Fri Nov 14 18:04:09 GMT 2025
Thanks for the explanation.
During my review, I unexpectedly discovered that this UTF_view, which
accepts an input_range, doesn't consider the fact that the input_iterator
only guarantees that it can be moved. It uses copying in many places.
Perhaps we should only accept forward_range.
Or perhaps we should do nothing, since only libstdc++ authors are using it
anyway.
Tomasz Kaminski <tkaminsk@redhat.com> 於 2025年11月15日 週六 上午1:58寫道:
>
>
> On Fri, Nov 14, 2025 at 6:55 PM Hewill Kang <hewillk@gmail.com> wrote:
>
>> LGTM.
>>
>> I know that CTAD wasn't provided due to heavy header considerations. But
>> wouldn't providing CTAD be an enhancement?
>> At least in terms of user convenience.
>>
> I care much more about the use of libstdc++ compile time performance, that
> convenience of the
> libstdc++ authors (only people that should use it). If we get
> official Utf_view, it should
> have the deduction guide in place.
>
>>
>> Tomasz Kamiński <tkaminsk@redhat.com> 於 2025年11月15日 週六 上午1:06寫道:
>>
>>> Previously, _Utf_view accepted any input_range, including
>>> reference-to-array
>>> types like char(&)[2], and stored it as the _M_base member. In such
>>> cases,
>>> _Utf_view was not assignable, failing the requirement to be a view
>>> concept.
>>>
>>> This patch addresses the issue by adding the ranges::view constraint to
>>> the
>>> second template parameter of _Utf_view, and for clarity renaming it from
>>> _Range to _View. The constructor is also adjusted to accept its argument
>>> by value (views must be O(1) move-constructible). This prevents
>>> implicitly
>>> generated CTAD from deducing a reference type.
>>>
>>> This makes _Utf_view consistent with both other standard views and the
>>> wording from P2728R8: Unicode in the Library, Part 1: UTF Transcoding
>>> [1].
>>>
>>> The explicit CTAD from viewable_range is not defined for _Utf_view
>>> because
>>> it depends on views::all_t, views::ref_view, and views::owning_view,
>>> which are declared in <ranges>. Consequently, users must explicitly cast
>>> the argument to a view or specify it as a template parameter.
>>>
>>> [1]
>>> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2728r8.html
>>>
>>> libstdc++-v3/ChangeLog:
>>>
>>> * include/bits/unicode.h (_Utf_view): Rename the template
>>> parameter
>>> from _Range to _View and constrain it with ranges::view.
>>> (_Utf_view::_Utf_view): Accept by value instead of rvalue
>>> reference.
>>> * include/std/format (__format::__write_padded): Replace
>>> _Utf_view
>>> over const char32_t(&)[1] with span<const char32_t, 1>.
>>> * testsuite/ext/unicode/view.cc: Add checks if specialization
>>> of _Utf_view satisfy view. Wrap arrays into std::span before
>>> cosntructing _Utf_view.
>>> ---
>>> libstdc++-v3/include/bits/unicode.h | 15 ++++++++-------
>>> libstdc++-v3/include/std/format | 2 +-
>>> libstdc++-v3/testsuite/ext/unicode/view.cc | 16 ++++++++++------
>>> 3 files changed, 19 insertions(+), 14 deletions(-)
>>>
>>> diff --git a/libstdc++-v3/include/bits/unicode.h
>>> b/libstdc++-v3/include/bits/unicode.h
>>> index 44872db4ed6..09f7c2d7bfb 100644
>>> --- a/libstdc++-v3/include/bits/unicode.h
>>> +++ b/libstdc++-v3/include/bits/unicode.h
>>> @@ -695,13 +695,14 @@ namespace __unicode
>>> friend class _Utf_iterator;
>>> };
>>>
>>> - template<typename _ToFormat, ranges::input_range _Range>
>>> + template<typename _ToFormat, ranges::input_range _View>
>>> + requires ranges::view<_View>
>>> class _Utf_view
>>> - : public ranges::view_interface<_Utf_view<_ToFormat, _Range>>
>>> + : public ranges::view_interface<_Utf_view<_ToFormat, _View>>
>>> {
>>> - using _Iterator = _Utf_iterator<ranges::range_value_t<_Range>,
>>> - _ToFormat,
>>> ranges::iterator_t<_Range>,
>>> - ranges::sentinel_t<_Range>>;
>>> + using _Iterator = _Utf_iterator<ranges::range_value_t<_View>,
>>> + _ToFormat,
>>> ranges::iterator_t<_View>,
>>> + ranges::sentinel_t<_View>>;
>>>
>>> template<typename _Iter, typename _Sent>
>>> constexpr auto
>>> @@ -725,11 +726,11 @@ namespace __unicode
>>> return _Iterator(__last, __last);
>>> }
>>>
>>> - _Range _M_base;
>>> + _View _M_base;
>>>
>>> public:
>>> constexpr explicit
>>> - _Utf_view(_Range&& __r) : _M_base(std::forward<_Range>(__r)) { }
>>> + _Utf_view(_View __r) : _M_base(std::move(__r)) { }
>>>
>>> constexpr auto begin()
>>> { return _M_begin(ranges::begin(_M_base), ranges::end(_M_base)); }
>>> diff --git a/libstdc++-v3/include/std/format
>>> b/libstdc++-v3/include/std/format
>>> index 1102ac8f6e8..f64f35a202e 100644
>>> --- a/libstdc++-v3/include/std/format
>>> +++ b/libstdc++-v3/include/std/format
>>> @@ -845,7 +845,7 @@ namespace __format
>>> {
>>> // Encode fill char as multiple code units of type _CharT.
>>> const char32_t __arr[1]{ __fill_char };
>>> - _Utf_view<_CharT, const char32_t(&)[1]> __v(__arr);
>>> + _Utf_view<_CharT, span<const char32_t, 1>> __v(__arr);
>>> basic_string<_CharT> __padstr(__v.begin(), __v.end());
>>> __padding = __padstr;
>>> while (__l-- > 0)
>>> diff --git a/libstdc++-v3/testsuite/ext/unicode/view.cc
>>> b/libstdc++-v3/testsuite/ext/unicode/view.cc
>>> index 40c8fcf34fb..677a21d8c1f 100644
>>> --- a/libstdc++-v3/testsuite/ext/unicode/view.cc
>>> +++ b/libstdc++-v3/testsuite/ext/unicode/view.cc
>>> @@ -7,6 +7,10 @@
>>> namespace uc = std::__unicode;
>>> using namespace std::string_view_literals;
>>>
>>> +static_assert( std::ranges::view<uc::_Utf8_view<std::string_view>> );
>>> +static_assert( std::ranges::view<uc::_Utf16_view<std::string_view>> );
>>> +static_assert( std::ranges::view<uc::_Utf32_view<std::string_view>> );
>>> +
>>> template<std::ranges::range View>
>>> constexpr void
>>> compare(View v,
>>> std::basic_string_view<std::ranges::range_value_t<View>> s)
>>> @@ -87,18 +91,18 @@ test_illformed_utf16()
>>> compare(uc::_Utf16_view(s.substr(0, 1)), r);
>>> compare(uc::_Utf16_view(s.substr(1, 1)), r);
>>> std::array s2{ s[0], s[0] };
>>> - compare(uc::_Utf16_view(s2), u"\uFFFD\uFFFD"sv);
>>> + compare(uc::_Utf16_view(std::span(s2)), u"\uFFFD\uFFFD"sv);
>>> std::array s3{ s[0], s[0], s[1] };
>>> - compare(uc::_Utf16_view(s3), u"\uFFFD\N{CLOWN FACE}"sv);
>>> + compare(uc::_Utf16_view(std::span(s3)), u"\uFFFD\N{CLOWN FACE}"sv);
>>> std::array s4{ s[1], s[0] };
>>> - compare(uc::_Utf16_view(s4), u"\uFFFD\uFFFD"sv);
>>> + compare(uc::_Utf16_view(std::span(s4)), u"\uFFFD\uFFFD"sv);
>>> std::array s5{ s[1], s[0], s[1] };
>>> - compare(uc::_Utf16_view(s5), u"\uFFFD\N{CLOWN FACE}"sv);
>>> + compare(uc::_Utf16_view(std::span(s5)), u"\uFFFD\N{CLOWN FACE}"sv);
>>>
>>> std::array<char16_t, 2> s6{ 0xDC00, 0xDC01 };
>>> - compare(uc::_Utf16_view(s6), u"\uFFFD\uFFFD"sv);
>>> + compare(uc::_Utf16_view(std::span(s6)), u"\uFFFD\uFFFD"sv);
>>> std::array<char16_t, 2> s7{ 0xD7FF, 0xDC00 };
>>> - compare(uc::_Utf16_view(s7), u"\uD7FF\uFFFD"sv);
>>> + compare(uc::_Utf16_view(std::span(s7)), u"\uD7FF\uFFFD"sv);
>>> }
>>>
>>> constexpr void
>>> --
>>> 2.51.0
>>>
>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20251115/3fd5bbdf/attachment-0001.htm>
More information about the Libstdc++
mailing list