Skip to content

Commit f94a73e

Browse files
author
Tomasz Kamiński
committed
libstdc++: Ensure that _Utf_view is always a view.
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 requirements of 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 constructing _Utf_view. Reviewed-by: Jonathan Wakely <jwakely@redhat.com> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
1 parent 14e5e4e commit f94a73e

File tree

3 files changed

+19
-14
lines changed

3 files changed

+19
-14
lines changed

libstdc++-v3/include/bits/unicode.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -695,13 +695,14 @@ namespace __unicode
695695
friend class _Utf_iterator;
696696
};
697697

698-
template<typename _ToFormat, ranges::input_range _Range>
698+
template<typename _ToFormat, ranges::input_range _View>
699+
requires ranges::view<_View>
699700
class _Utf_view
700-
: public ranges::view_interface<_Utf_view<_ToFormat, _Range>>
701+
: public ranges::view_interface<_Utf_view<_ToFormat, _View>>
701702
{
702-
using _Iterator = _Utf_iterator<ranges::range_value_t<_Range>,
703-
_ToFormat, ranges::iterator_t<_Range>,
704-
ranges::sentinel_t<_Range>>;
703+
using _Iterator = _Utf_iterator<ranges::range_value_t<_View>,
704+
_ToFormat, ranges::iterator_t<_View>,
705+
ranges::sentinel_t<_View>>;
705706

706707
template<typename _Iter, typename _Sent>
707708
constexpr auto
@@ -725,11 +726,11 @@ namespace __unicode
725726
return _Iterator(__last, __last);
726727
}
727728

728-
_Range _M_base;
729+
_View _M_base;
729730

730731
public:
731732
constexpr explicit
732-
_Utf_view(_Range&& __r) : _M_base(std::forward<_Range>(__r)) { }
733+
_Utf_view(_View __r) : _M_base(std::move(__r)) { }
733734

734735
constexpr auto begin()
735736
{ return _M_begin(ranges::begin(_M_base), ranges::end(_M_base)); }

libstdc++-v3/include/std/format

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,7 @@ namespace __format
845845
{
846846
// Encode fill char as multiple code units of type _CharT.
847847
const char32_t __arr[1]{ __fill_char };
848-
_Utf_view<_CharT, const char32_t(&)[1]> __v(__arr);
848+
_Utf_view<_CharT, span<const char32_t, 1>> __v(__arr);
849849
basic_string<_CharT> __padstr(__v.begin(), __v.end());
850850
__padding = __padstr;
851851
while (__l-- > 0)

libstdc++-v3/testsuite/ext/unicode/view.cc

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
namespace uc = std::__unicode;
88
using namespace std::string_view_literals;
99

10+
static_assert( std::ranges::view<uc::_Utf8_view<std::string_view>> );
11+
static_assert( std::ranges::view<uc::_Utf16_view<std::string_view>> );
12+
static_assert( std::ranges::view<uc::_Utf32_view<std::string_view>> );
13+
1014
template<std::ranges::range View>
1115
constexpr void
1216
compare(View v, std::basic_string_view<std::ranges::range_value_t<View>> s)
@@ -87,18 +91,18 @@ test_illformed_utf16()
8791
compare(uc::_Utf16_view(s.substr(0, 1)), r);
8892
compare(uc::_Utf16_view(s.substr(1, 1)), r);
8993
std::array s2{ s[0], s[0] };
90-
compare(uc::_Utf16_view(s2), u"\uFFFD\uFFFD"sv);
94+
compare(uc::_Utf16_view(std::span(s2)), u"\uFFFD\uFFFD"sv);
9195
std::array s3{ s[0], s[0], s[1] };
92-
compare(uc::_Utf16_view(s3), u"\uFFFD\N{CLOWN FACE}"sv);
96+
compare(uc::_Utf16_view(std::span(s3)), u"\uFFFD\N{CLOWN FACE}"sv);
9397
std::array s4{ s[1], s[0] };
94-
compare(uc::_Utf16_view(s4), u"\uFFFD\uFFFD"sv);
98+
compare(uc::_Utf16_view(std::span(s4)), u"\uFFFD\uFFFD"sv);
9599
std::array s5{ s[1], s[0], s[1] };
96-
compare(uc::_Utf16_view(s5), u"\uFFFD\N{CLOWN FACE}"sv);
100+
compare(uc::_Utf16_view(std::span(s5)), u"\uFFFD\N{CLOWN FACE}"sv);
97101

98102
std::array<char16_t, 2> s6{ 0xDC00, 0xDC01 };
99-
compare(uc::_Utf16_view(s6), u"\uFFFD\uFFFD"sv);
103+
compare(uc::_Utf16_view(std::span(s6)), u"\uFFFD\uFFFD"sv);
100104
std::array<char16_t, 2> s7{ 0xD7FF, 0xDC00 };
101-
compare(uc::_Utf16_view(s7), u"\uD7FF\uFFFD"sv);
105+
compare(uc::_Utf16_view(std::span(s7)), u"\uD7FF\uFFFD"sv);
102106
}
103107

104108
constexpr void

0 commit comments

Comments
 (0)