diff --git a/examples/readme_examples.cpp b/examples/readme_examples.cpp index 037ac0f..1809288 100644 --- a/examples/readme_examples.cpp +++ b/examples/readme_examples.cpp @@ -110,7 +110,7 @@ transcode_result transcode_to_utf32(I first, S last, O out) { bool transcode_to_utf32_test() { std::u8string_view char8_string{u8"\xf0\x9f\x95\xb4\xef\xbf\xbd"}; - to_utf16_view utf16_transcoding_view{char8_string}; + auto utf16_transcoding_view{char8_string | to_utf16}; std::u32string char32_string{}; auto transcode_result{transcode_to_utf32(utf16_transcoding_view.begin(), utf16_transcoding_view.end(), diff --git a/include/beman/utf_view/detail/nontype_t_polyfill.hpp b/include/beman/utf_view/detail/nontype_t_polyfill.hpp new file mode 100644 index 0000000..5b57567 --- /dev/null +++ b/include/beman/utf_view/detail/nontype_t_polyfill.hpp @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: BSL-1.0 + +// Copyright Eddie Nolan and Jonathan Wakely 2023 - 2025. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +#ifndef BEMAN_UTF_VIEW_NONTYPE_T_POLYFILL_HPP +#define BEMAN_UTF_VIEW_NONTYPE_T_POLYFILL_HPP + +#include +#include + +namespace beman::utf_view::detail { + +#ifdef __cpp_lib_function_ref + +template +using nontype_t = std::nontype_t; + +template constexpr auto nontype{std::nontype}; + +#else + +template +struct nontype_t { + explicit nontype_t() = default; +}; + +template constexpr nontype_t nontype{}; + +#endif + + +} // namespace beman::utf_view::detail + +#endif // BEMAN_UTF_VIEW_NONTYPE_T_POLYFILL_HPP diff --git a/include/beman/utf_view/to_utf_view.hpp b/include/beman/utf_view/to_utf_view.hpp index d1b90ef..1fd2dc1 100644 --- a/include/beman/utf_view/to_utf_view.hpp +++ b/include/beman/utf_view/to_utf_view.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -97,12 +98,29 @@ enum class to_utf_view_error_kind : bool { expected }; +template +struct to_utf_tag_t { + explicit to_utf_tag_t() = default; +}; + +template +constexpr to_utf_tag_t to_utf_tag{}; + +using to_utf8_tag_t = to_utf_tag_t; + +constexpr to_utf8_tag_t to_utf8_tag{}; + +using to_utf16_tag_t = to_utf_tag_t; + +constexpr to_utf16_tag_t to_utf16_tag{}; + +using to_utf32_tag_t = to_utf_tag_t; + +constexpr to_utf32_tag_t to_utf32_tag{}; + template requires std::ranges::view && exposition_only_code_unit> -/* !PAPER */ -class exposition_only_to_utf_view_impl { -/* PAPER */ -/* PAPER: class @*to-utf-view-impl*@ */ +class to_utf_view : public std::ranges::view_interface> { private: template struct exposition_only_iterator; // @*exposition only*@ template struct exposition_only_sentinel; // @*exposition only*@ @@ -112,7 +130,7 @@ class exposition_only_to_utf_view_impl { /* !PAPER */ template struct maybe_cache_begin_impl { - constexpr auto begin_impl(exposition_only_to_utf_view_impl& self, auto base) noexcept { + constexpr auto begin_impl(to_utf_view& self, auto base) noexcept { return exposition_only_iterator{self, std::move(base)}; } }; @@ -120,7 +138,7 @@ class exposition_only_to_utf_view_impl { template requires (!std::same_as, char32_t> && std::copy_constructible) struct maybe_cache_begin_impl { - constexpr auto begin_impl(exposition_only_to_utf_view_impl& self, auto base) noexcept { + constexpr auto begin_impl(to_utf_view& self, auto base) noexcept { if (!cache_) { cache_.emplace(self, std::move(base)); } @@ -133,12 +151,12 @@ class exposition_only_to_utf_view_impl { /* PAPER */ public: - constexpr exposition_only_to_utf_view_impl() + constexpr to_utf_view() requires std::default_initializable = default; - /* PAPER: constexpr explicit exposition_only_to_utf_view_impl(V base); */ + /* PAPER: constexpr explicit to_utf_view(V base, nontype_t, to_utf_tag_t); */ /* !PAPER */ - constexpr explicit exposition_only_to_utf_view_impl(V base) + constexpr explicit to_utf_view(V base, detail::nontype_t, to_utf_tag_t) : base_(std::move(base)) { } /* PAPER */ @@ -211,15 +229,18 @@ class exposition_only_to_utf_view_impl { /* PAPER */ }; +template +to_utf_view(R&&, detail::nontype_t, to_utf_tag_t) -> to_utf_view, E, ToType>; + template requires std::ranges::view && exposition_only_code_unit> template -/* PAPER: class @*to-utf-view-impl*@::@*iterator*@ { */ +/* PAPER: class to_utf_view::@*iterator*@ { */ /* !PAPER */ -struct exposition_only_to_utf_view_impl::exposition_only_iterator : detail::iter_category_impl { +struct to_utf_view::exposition_only_iterator : detail::iter_category_impl { /* PAPER */ private: - using exposition_only_Parent = exposition_only_maybe_const; // @*exposition only*@ + using exposition_only_Parent = exposition_only_maybe_const; // @*exposition only*@ using exposition_only_Base = exposition_only_maybe_const; // @*exposition only*@ /* !PAPER */ @@ -268,7 +289,7 @@ struct exposition_only_to_utf_view_impl::exposition_only_iterator template requires std::ranges::view && exposition_only_code_unit> - friend class exposition_only_to_utf_view_impl; // @*exposition only*@ + friend class to_utf_view; // @*exposition only*@ public: @@ -826,12 +847,9 @@ struct exposition_only_to_utf_view_impl::exposition_only_iterator template requires std::ranges::view && exposition_only_code_unit> template -/* PAPER: class @*to-utf-view-impl*@::@*sentinel*@ { */ -/* !PAPER */ -struct exposition_only_to_utf_view_impl::exposition_only_sentinel { - /* PAPER */ +struct to_utf_view::exposition_only_sentinel { private: - using exposition_only_Parent = exposition_only_maybe_const; // @*exposition only*@ + using exposition_only_Parent = exposition_only_maybe_const; // @*exposition only*@ using exposition_only_Base = exposition_only_maybe_const; // @*exposition only*@ std::ranges::sentinel_t end_ = std::ranges::sentinel_t(); @@ -860,416 +878,21 @@ struct exposition_only_to_utf_view_impl::exposition_only_sentinel } }; -template - requires std::ranges::view && exposition_only_code_unit> -class to_utf8_view : public std::ranges::view_interface> { -public: - constexpr to_utf8_view() requires std::default_initializable = default; - constexpr explicit to_utf8_view(V base) - : impl_(std::move(base)) { } - - constexpr V base() const& - requires std::copy_constructible - { - return impl_.base(); - } - constexpr V base() && { - return std::move(impl_).base(); - } - - constexpr auto begin() - { - return impl_.begin(); - } - constexpr auto begin() const - requires std::ranges::range && - ((std::same_as, char32_t>) || - (!std::ranges::forward_range)) { - return impl_.begin(); - } - constexpr auto end() - { - return impl_.end(); - } - constexpr auto end() const - requires std::ranges::range { - return impl_.end(); - } - - constexpr bool empty() const { - return impl_.empty(); - } - - /* !PAPER */ -#if defined(__cpp_lib_ranges_reserve_hint) - /* PAPER */ - constexpr auto reserve_hint() requires std::ranges::approximately_sized_range { - return std::ranges::reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires std::ranges::approximately_sized_range; { - return std::ranges::reserve_hint(impl_); - } - /* !PAPER */ -#endif - /* PAPER */ - -private: - exposition_only_to_utf_view_impl impl_; // @*exposition only*@ -}; - -template -to_utf8_view(R&&) -> to_utf8_view>; - -template - requires std::ranges::view && exposition_only_code_unit> -class to_utf8_or_error_view - : public std::ranges::view_interface> { -public: - constexpr to_utf8_or_error_view() - requires std::default_initializable - = default; - constexpr explicit to_utf8_or_error_view(V base) - : impl_(std::move(base)) { } - - constexpr V base() const& - requires std::copy_constructible - { - return impl_.base(); - } - constexpr V base() && { - return std::move(impl_).base(); - } - - constexpr auto begin() - { - return impl_.begin(); - } - constexpr auto begin() const - requires std::ranges::range && - ((std::same_as, char32_t>) || - (!std::ranges::forward_range)) { - return impl_.begin(); - } - constexpr auto end() - { - return impl_.end(); - } - constexpr auto end() const - requires std::ranges::range { - return impl_.end(); - } - - constexpr bool empty() const { - return impl_.empty(); - } - - /* !PAPER */ -#if defined(__cpp_lib_ranges_reserve_hint) - /* PAPER */ - constexpr auto reserve_hint() requires std::ranges::approximately_sized_range { - return std::ranges::reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires std::ranges::approximately_sized_range; { - return std::ranges::reserve_hint(impl_); - } - /* !PAPER */ -#endif - /* PAPER */ - -private: - exposition_only_to_utf_view_impl impl_; // @*exposition only*@ -}; - -template -to_utf8_or_error_view(R&&) -> to_utf8_or_error_view>; - -template - requires std::ranges::view && exposition_only_code_unit> -class to_utf16_view : public std::ranges::view_interface> { -public: - constexpr to_utf16_view() - requires std::default_initializable - = default; - constexpr explicit to_utf16_view(V base) - : impl_(std::move(base)) { } - - constexpr V base() const& - requires std::copy_constructible - { - return impl_.base(); - } - constexpr V base() && { - return std::move(impl_).base(); - } - - constexpr auto begin() - { - return impl_.begin(); - } - constexpr auto begin() const - requires std::ranges::range && - ((std::same_as, char32_t>) || - (!std::ranges::forward_range)) { - return impl_.begin(); - } - constexpr auto end() - { - return impl_.end(); - } - constexpr auto end() const - requires std::ranges::range { - return impl_.end(); - } - - constexpr bool empty() const { - return impl_.empty(); - } - - /* !PAPER */ -#if defined(__cpp_lib_ranges_reserve_hint) - /* PAPER */ - constexpr auto reserve_hint() requires std::ranges::approximately_sized_range { - return std::ranges::reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires std::ranges::approximately_sized_range; { - return std::ranges::reserve_hint(impl_); - } - /* !PAPER */ -#endif - /* PAPER */ - -private: - exposition_only_to_utf_view_impl impl_; // @*exposition only*@ -}; - -template -to_utf16_view(R&&) -> to_utf16_view>; - -template - requires std::ranges::view && exposition_only_code_unit> -class to_utf16_or_error_view - : public std::ranges::view_interface> { -public: - constexpr to_utf16_or_error_view() - requires std::default_initializable - = default; - constexpr explicit to_utf16_or_error_view(V base) - : impl_(std::move(base)) { } - - constexpr V base() const& - requires std::copy_constructible - { - return impl_.base(); - } - constexpr V base() && { - return std::move(impl_).base(); - } - - constexpr auto begin() - { - return impl_.begin(); - } - constexpr auto begin() const - requires std::ranges::range && - ((std::same_as, char32_t>) || - (!std::ranges::forward_range)) { - return impl_.begin(); - } - constexpr auto end() - { - return impl_.end(); - } - constexpr auto end() const - requires std::ranges::range { - return impl_.end(); - } - - constexpr bool empty() const { - return impl_.empty(); - } - - /* !PAPER */ -#if defined(__cpp_lib_ranges_reserve_hint) - /* PAPER */ - constexpr auto reserve_hint() requires std::ranges::approximately_sized_range { - return std::ranges::reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires std::ranges::approximately_sized_range; { - return std::ranges::reserve_hint(impl_); - } - /* !PAPER */ -#endif - /* PAPER */ - -private: - exposition_only_to_utf_view_impl impl_; // @*exposition only*@ -}; - -template -to_utf16_or_error_view(R&&) -> to_utf16_or_error_view>; - -template - requires std::ranges::view && exposition_only_code_unit> -class to_utf32_view : public std::ranges::view_interface> { -public: - constexpr to_utf32_view() - requires std::default_initializable - = default; - constexpr explicit to_utf32_view(V base) - : impl_(std::move(base)) { } - - constexpr V base() const& - requires std::copy_constructible - { - return impl_.base(); - } - constexpr V base() && { - return std::move(impl_).base(); - } - - constexpr auto begin() - { - return impl_.begin(); - } - constexpr auto begin() const - requires std::ranges::range && - ((std::same_as, char32_t>) || - (!std::ranges::forward_range)) { - return impl_.begin(); - } - constexpr auto end() - { - return impl_.end(); - } - constexpr auto end() const - requires std::ranges::range { - return impl_.end(); - } - - constexpr bool empty() const { - return impl_.empty(); - } - - constexpr std::size_t size() - requires std::ranges::sized_range && - std::same_as> { - return impl_.size(); - } - - /* !PAPER */ -#if defined(__cpp_lib_ranges_reserve_hint) - /* PAPER */ - constexpr auto reserve_hint() requires std::ranges::approximately_sized_range { - return std::ranges::reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires std::ranges::approximately_sized_range; { - return std::ranges::reserve_hint(impl_); - } - /* !PAPER */ -#endif - /* PAPER */ - -private: - exposition_only_to_utf_view_impl impl_; // @*exposition only*@ -}; - -template -to_utf32_view(R&&) -> to_utf32_view>; - -template - requires std::ranges::view && exposition_only_code_unit> -class to_utf32_or_error_view - : public std::ranges::view_interface> { -public: - constexpr to_utf32_or_error_view() - requires std::default_initializable - = default; - constexpr explicit to_utf32_or_error_view(V base) - : impl_(std::move(base)) { } - - constexpr V base() const& - requires std::copy_constructible - { - return impl_.base(); - } - constexpr V base() && { - return std::move(impl_).base(); - } - - constexpr auto begin() - { - return impl_.begin(); - } - constexpr auto begin() const - requires std::ranges::range && - ((std::same_as, char32_t>) || - (!std::ranges::forward_range)) { - return impl_.begin(); - } - constexpr auto end() - { - return impl_.end(); - } - constexpr auto end() const - requires std::ranges::range { - return impl_.end(); - } - - constexpr bool empty() const { - return impl_.empty(); - } - - constexpr std::size_t size() - requires std::ranges::sized_range && - std::same_as> { - return impl_.size(); - } - - /* !PAPER */ -#if defined(__cpp_lib_ranges_reserve_hint) - /* PAPER */ - constexpr auto reserve_hint() requires std::ranges::approximately_sized_range { - return std::ranges::reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires std::ranges::approximately_sized_range; { - return std::ranges::reserve_hint(impl_); - } - /* !PAPER */ -#endif - /* PAPER */ - -private: - exposition_only_to_utf_view_impl impl_; // @*exposition only*@ -}; - -template -to_utf32_or_error_view(R&&) -> to_utf32_or_error_view>; - /* !PAPER */ namespace detail { - template - using to_utf_view = std::conditional_t< - E == to_utf_view_error_kind::expected, - std::conditional_t< - std::is_same_v, to_utf8_or_error_view, - std::conditional_t, to_utf16_or_error_view, - std::conditional_t, - to_utf32_or_error_view, void>>>, - std::conditional_t< - std::is_same_v, to_utf8_view, - std::conditional_t, to_utf16_view, - std::conditional_t, - to_utf32_view, void>>>>; - template struct to_utf_impl : std::ranges::range_adaptor_closure> { template constexpr auto operator()(R&& r) const { using T = std::remove_cvref_t; if constexpr (detail::is_empty_view) { - return std::ranges::empty_view{}; + if constexpr (E == to_utf_view_error_kind::replacement) { + return std::ranges::empty_view{}; + } else { + return std::ranges::empty_view>{}; + } } else if constexpr (std::is_bounded_array_v) { constexpr auto n = std::extent_v; auto first{std::ranges::begin(r)}; @@ -1278,10 +901,9 @@ namespace detail { --last; } std::ranges::subrange subrange(first, last); - return to_utf_view(std::move(subrange)); + return to_utf_view(std::move(subrange), detail::nontype, to_utf_tag); } else { - auto view = std::views::all(std::forward(r)); - return to_utf_view(std::move(view)); + return to_utf_view(std::forward(r), detail::nontype, to_utf_tag); } } }; diff --git a/paper/P2728.md b/paper/P2728.md index 4753aab..fc92996 100644 --- a/paper/P2728.md +++ b/paper/P2728.md @@ -197,14 +197,14 @@ Each of the proposed `to_utfN_view` views adheres to this specification. The ## Transcoding Views Invoking `begin()` or `end()` on a transcoding view constructs an instance of -an exposition-only `@*to-utf-view-impl*@::@*iterator*@` type. +an exposition-only `to_utf_view::@*iterator*@` type. -The `@*to-utf-view-impl*@::@*iterator*@` stores an iterator pointing to the +The `to_utf_view::@*iterator*@` stores an iterator pointing to the start of the character it's transcoding, and a back-pointer to the underlying range in order to bounds check its beginning and end (which is required for correctness, not just safety). -The `@*to-utf-view-impl*@::@*iterator*@` maintains a small buffer (`buf_`) +The `to_utf_view::@*iterator*@` maintains a small buffer (`buf_`) containing between one and four code units, which comprise the current character in the target encoding. @@ -218,13 +218,13 @@ actual data members of the iterator; dashed lines are just function calls. ![](../../paper/images/iterator.png){ width=500px } -The `@*to-utf-view-impl*@::@*iterator*@` is converting the string `Qϕ学𡪇` +The `to_utf_view::@*iterator*@` is converting the string `Qϕ学𡪇` from UTF-8 to UTF-16. The user has iterated the view to the first UTF-16 code unit of the fourth character. `base_` points to the start of the fourth character in the input. `buf_` contains both UTF-16 code units of the fourth character; `buf_index_` keeps track of the fact that we're currently pointing to the first one. If we invoke `operator++` on the -`@*to-utf-view-impl*@::@*iterator*@`, it will increment `buf_index_` to point +`to_utf_view::@*iterator*@`, it will increment `buf_index_` to point to the second code unit. On the other hand, if we invoke `operator--`, it will notice that `buf_index_` is already at the beginning and move backward from the fourth character to the third character by invoking @@ -436,54 +436,42 @@ Add the following subclause to [range.adaptors]{.sref}: #### 24.7.?.1 Overview [range.transcoding.overview] {-} -`to_utf8_view` produces a view of the UTF-8 code units transcoded from the -elements of a `@*utf-range*@`. `to_utf16_view` produces a view of the UTF-16 -code units transcoded from the elements of a `@*utf-range*@`. `to_utf32_view` -produces a view of the UTF-32 code units transcoded from the elements of a -`@*utf-range*@`. Their `or_error` equivalents produce a view of +`to_utf_view` produces a view of the UTF code units transcoded from the +elements of a `@*utf-range*@`. It transcodes from UTF-N to UTF-M, where N and +M are each one of 8, 16, or 32. N may equal M. `to_utf_view`'s +`ToType` template parameter is based on a mapping between character types and +UTF encodings, which is that that `char8_t` corresponds to UTF-8, `char16_t` +corresponds to UTF-16, and `char32_t` corresponds to UTF-32. If its +`to_utf_view_error_kind` CTP is `expected`, it produces a view of `expected` where invalid input subsequences result in errors. -`@*to-utf-view-impl*@` is an exposition-only class that provides -implementation details common to the six aforementioned transcoding views. It -transcodes from UTF-N to UTF-M, where N and M are each one of 8, 16, or 32. N -may equal M. `@*to-utf-view-impl*@`'s `ToType` template parameter is based on -a mapping between character types and UTF encodings, which is that that -`char8_t` corresponds to UTF-8, `char16_t` corresponds to UTF-16, and -`char32_t` corresponds to UTF-32. - -The names `views::to_utf8`, `views::to_utf8_or_error`, `views::to_utf16`, +The names `views::to_utf`, `views::to_utf_or_error`, +`views::to_utf8`, `views::to_utf8_or_error`, `views::to_utf16`, `views::to_utf16_or_error`, `views::to_utf32`, and `views::to_utf32_or_error` -denote range adaptor objects ([range.adaptor.object]). `views::to_utf` and -`views::to_utf_or_error` denote range adaptor object -templates. `views::to_utfN` produces `to_utfN_view`s, and -`views::to_utfN_or_error` produces `views::to_utfN_or_error_view`s. +denote range adaptor objects ([range.adaptor.object]). `views::to_utf` is equivalent to `views::to_utf8` if `ToType` is -`char8_t`, `to_utf16` if `ToType` is `char16_t`, and `views::to_utf32` if -`ToType` is `char32_t`, and similarly for `views::to_utf_or_error`. +`char8_t`, `views::to_utf16` if `ToType` is `char16_t`, and `views::to_utf32` +if `ToType` is `char32_t`, and similarly for `views::to_utf_or_error`. Let `views::to_utfN` denote any of the aforementioned range adaptor objects, -let `Char` be its corresponding character type, and let `V` denote the -`to_utfN_view` or `to_utfN_or_error_view` associated with that object. Let -`E` be an expression and let `T` be `remove_cvref_t`. If -`decltype((E))` does not model `@*utf-range*@`, `to_utfN(E)` is -ill-formed. The expression `to_utfN(E)` is expression-equivalent to: - -- If `E` is a specialization of `empty_view` ([range.empty.view]), then - `empty_view{}`. +let `Char` be its corresponding character type, and let `Error` be its +corresponding `to_utf_view_error_kind`. Let `E` be an expression and let `T` +be `remove_cvref_t`. If `decltype((E))` does not model +`@*utf-range*@`, `to_utfN(E)` is ill-formed. The expression `to_utfN(E)` is +expression-equivalent to: -- Otherwise, if `T` is an array type of known bound, then: - - - If the array extent is nonzero and the last element of the array is zero, - then - `V(std::ranges::subrange(std::ranges::begin(E), --std::ranges::end(E)))` - - Otherwise, - `V(std::ranges::subrange(std::ranges::begin(E), std::ranges::end(E)))` +- If `E` is a specialization of `empty_view` ([range.empty.view]): + + - If `Error` is `to_utf_view_error_kind::replacement`, then + `empty_view{}`. + + - Otherwise, `empty_view>{}`. -- Otherwise, `V(std::views::all(E))` +- Otherwise, `to_utf_view(E, nontype, to_utf_tag)`. -#### 24.7.?.2 Enumeration `utf_transcoding_error` [range.transcoding.error] {-} +#### 24.7.?.2 Enumeration `utf_transcoding_error` [range.transcoding.error.transcoding] {-} ```c++ enum class utf_transcoding_error { @@ -501,18 +489,42 @@ enum class utf_transcoding_error { #### 24.7.?.3 Enumeration `to_utf_view_error_kind` [range.transcoding.error.kind] {-} ```c++ - enum class to_utf_view_error_kind : bool { - replacement, - expected - }; +enum class to_utf_view_error_kind : bool { + replacement, + expected +}; +``` + +#### 24.7.?.4 UTF Tags [range.transcoding.tags] {-} + +```c++ +template<@*code-unit*@ ToType> +struct to_utf_tag_t { + explicit to_utf_tag_t() = default; +}; + +template<@*code-unit*@ ToType> +constexpr to_utf_tag_t to_utf_tag{}; + +using to_utf8_tag_t = to_utf_tag_t; + +constexpr to_utf8_tag_t to_utf8_tag{}; + +using to_utf16_tag_t = to_utf_tag_t; + +constexpr to_utf16_tag_t to_utf16_tag{}; + +using to_utf32_tag_t = to_utf_tag_t; + +constexpr to_utf32_tag_t to_utf32_tag{}; ``` -#### 24.7.?.4 Exposition-only class template `@*to-utf-view-impl*@` [range.transcoding.view.impl] {-} +#### 24.7.?.5 Class template `to_utf_view` [range.transcoding.view] {-} ```c++ template requires view && @*code-unit*@> -class @*to-utf-view-impl*@ +class to_utf_view : public view_interface> { private: template struct @*iterator*@; // @*exposition only*@ @@ -522,8 +534,8 @@ private: V @*base_*@ = V(); // @*exposition only*@ public: - constexpr @*to-utf-view-impl*@() requires default_initializable = default; - constexpr explicit @*to-utf-view-impl*@(V base); + constexpr to_utf_view() requires default_initializable = default; + constexpr explicit to_utf_view(V base, nontype_t, to_utf_tag_t); constexpr V base() const& requires copy_constructible { return @*base_*@; } constexpr V base() && { return std::move(@*base_*@); } @@ -560,10 +572,13 @@ public: constexpr auto reserve_hint() requires approximately_sized_range; constexpr auto reserve_hint() const requires approximately_sized_range; }; + +template + to_utf_view(R&&, nontype_t, to_utf_tag_t) -> to_utf_view, E, ToType>; ``` ```cpp -constexpr explicit @*to-utf-view-impl*@(V base); +constexpr explicit to_utf_view(V base, nontype_t, to_utf_tag_t); ``` _Effects_: Initializes `@*base_*@` with `std::move(base)`. @@ -574,7 +589,7 @@ constexpr @*iterator*@ begin(); _Returns:_ `{*this, std::ranges::begin(base_)}` -_Remarks:_ In order to provide the amortized constant time complexity required by the `range` concept when `@*to-utf-view-impl*@` transcodes from UTF-8 or UTF-16, this function caches the result within the `@*to-utf-view-impl*@` for use on subsequent calls. +_Remarks:_ In order to provide the amortized constant time complexity required by the `range` concept when `to_utf_view` transcodes from UTF-8 or UTF-16, this function caches the result within the `to_utf_view` for use on subsequent calls. ```cpp constexpr auto reserve_hint() requires approximately_sized_range; @@ -592,22 +607,22 @@ _Returns:_ The result is implementation-defined. The implementation of the `empty()` member function provided by the transcoding views is more efficient than the one provided by `view_interface`, -since `view_interface`'s implementation will construct -`@*to-utf-view-impl*@::begin()` and `@*to-utf-view-impl*@::end()` and compare -them, whereas we can simply use the underlying range's `empty()`, since a -transcoding view is empty if and only if its underlying range is empty. +since `view_interface`'s implementation will construct `to_utf_view::begin()` +and `to_utf_view::end()` and compare them, whereas we can simply use the +underlying range's `empty()`, since a transcoding view is empty if and only if +its underlying range is empty. ::: -#### 24.7.?.5 Class `@*to-utf-view-impl*@::@*iterator*@` [range.transcoding.view.impl.iterator] {-} +#### 24.7.?.6 Class `to_utf_view::@*iterator*@` [range.transcoding.iterator] {-} ```c++ template requires view && @*code-unit*@> template -class @*to-utf-view-impl*@::@*iterator*@ { +class to_utf_view::@*iterator*@ { private: - using @*Parent*@ = @*maybe-const*@; // @*exposition only*@ + using @*Parent*@ = @*maybe-const*@; // @*exposition only*@ using @*Base*@ = @*maybe-const*@; // @*exposition only*@ public: @@ -628,7 +643,7 @@ private: template requires view && @*code-unit*@> - friend class @*to-utf-view-impl*@; // @*exposition only*@ + friend class to_utf_view; // @*exposition only*@ public: constexpr @*iterator*@() requires default_initializable> = default; @@ -726,7 +741,7 @@ private: ::: note -`@*to-utf-view-impl*@::@*iterator*@` does its work by adapting an underlying +`to_utf_view::@*iterator*@` does its work by adapting an underlying range of code units. We use the term "input subsequence" to refer to a potentially ill-formed code unit subsequence which is to be transcoded into a code point `c`. Each input subsequence is decoded from the UTF encoding @@ -740,7 +755,7 @@ encoding, into an internal code unit buffer `buf_`. ::: note -`@*to-utf-view-impl*@::@*iterator*@` maintains invariants on `base()` which +`to_utf_view::@*iterator*@` maintains invariants on `base()` which differ depending on whether it's an input iterator. In both cases, if `*this` is at the end of the range being adapted, then `base()` == `@*end*@()`. But if it's not at the end of the adapted range, and it's an input iterator, then the @@ -751,7 +766,7 @@ of the input subsequence corresponding to the current code point. ::: -`@*to-utf-view-impl*@::@*iterator*@::iterator_concept` is defined as follows: +`to_utf_view::@*iterator*@::iterator_concept` is defined as follows: - If `V` models `bidirectional_range`, then `iterator_concept` is `bidirectional_iterator_tag`. - Otherwise, if `V` models `forward_range`, then `iterator_concept` is `forward_iterator_tag`. @@ -759,7 +774,7 @@ of the input subsequence corresponding to the current code point. The member *typedef-name* `iterator_category` is defined if and only if `V` models `forward_range`. -In that case, `@*to-utf-view-impl*@::@*iterator*@::iterator_category` is defined as follows: +In that case, `to_utf_view::@*iterator*@::iterator_category` is defined as follows: - Let `C` denote the type `iterator_traits>::iterator_category`. - If `C` models `derived_from`, then `iterator_category` denotes `bidirectional_iterator_tag`. @@ -844,15 +859,15 @@ the UTF encoding corresponding to `ToType`; and sets `buf_index_` to `buf_.size() - 1`, or to `0` if this is an `or_error` view and we read an invalid subsequence. -#### 24.7.?.6 Class `@*to-utf-view-impl*@::@*sentinel*@` [range.transcoding.view.impl.sentinel] {-} +#### 24.7.?.7 Class `to_utf_view::@*sentinel*@` [range.transcoding.sentinel] {-} ```c++ template requires view && @*code-unit*@> template -class @*to-utf-view-impl*@::@*sentinel*@ { +struct to_utf_view::@*sentinel*@ { private: - using @*Parent*@ = @*maybe-const*@; // @*exposition only*@ + using @*Parent*@ = @*maybe-const*@; // @*exposition only*@ using @*Base*@ = @*maybe-const*@; // @*exposition only*@ sentinel_t<@*Base*@> end_ = sentinel_t<@*Base*@>(); // @*exposition only*@ @@ -878,268 +893,6 @@ public: }; ``` -#### 24.7.?.7 Class template `to_utf8_view` [range.transcoding.view.to_utf8] - -```c++ -template - requires view && @*code-unit*@> -class to_utf8_view : public view_interface> { -public: - constexpr to_utf8_view() requires default_initializable = default; - constexpr explicit to_utf8_view(V base) : impl_(std::move(base)) {} - - constexpr V base() const& requires copy_constructible { return impl_.base(); } - constexpr V base() && { return std::move(impl_).base(); } - - constexpr auto begin() { return impl_.begin(); } - constexpr auto begin() const - requires range && ((same_as, char32_t>) || (!forward_range)) - { - return impl_.begin(); - } - constexpr auto end() { return impl_.end(); } - constexpr auto end() const requires range - { - return impl_.end(); - } - - constexpr bool empty() const { return impl_.empty(); } - - constexpr auto reserve_hint() requires approximately_sized_range - { - return reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires approximately_sized_range; - { return reserve_hint(impl_); } - -private: - @*to-utf-view-impl*@ impl_; // @*exposition only*@ -}; - -template - to_utf8_view(R&&) -> to_utf8_view>; -``` - -#### 24.7.?.8 Class template `to_utf8_or_error_view` [range.transcoding.view.to_utf8_or_error] - -```c++ -template - requires view && @*code-unit*@> -class to_utf8_or_error_view : public view_interface> { -public: - constexpr to_utf8_or_error_view() requires default_initializable = default; - constexpr explicit to_utf8_or_error_view(V base) : impl_(std::move(base)) {} - - constexpr V base() const& requires copy_constructible { return impl_.base(); } - constexpr V base() && { return std::move(impl_).base(); } - - constexpr auto begin() { return impl_.begin(); } - constexpr auto begin() const - requires range && ((same_as, char32_t>) || (!forward_range)) - { - return impl_.begin(); - } - constexpr auto end() { return impl_.end(); } - constexpr auto end() const requires range - { - return impl_.end(); - } - - constexpr bool empty() const { return impl_.empty(); } - - constexpr auto reserve_hint() requires approximately_sized_range - { - return reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires approximately_sized_range; - { return reserve_hint(impl_); } - -private: - @*to-utf-view-impl*@ impl_; // @*exposition only*@ -}; - -template - to_utf8_or_error_view(R&&) -> to_utf8_or_error_view>; -``` - -#### 24.7.?.9 Class template `to_utf16_view` [range.transcoding.view.to_utf16] - -```c++ -template - requires view && @*code-unit*@> -class to_utf16_view : public view_interface> { -public: - constexpr to_utf16_view() requires default_initializable = default; - constexpr explicit to_utf16_view(V base) : impl_(std::move(base)) {} - - constexpr V base() const& requires copy_constructible { return impl_.base(); } - constexpr V base() && { return std::move(impl_).base(); } - - constexpr auto begin() { return impl_.begin(); } - constexpr auto begin() const - requires range && ((same_as, char32_t>) || (!forward_range)) - { - return impl_.begin(); - } - constexpr auto end() { return impl_.end(); } - constexpr auto end() const requires range - { - return impl_.end(); - } - - constexpr bool empty() const { return impl_.empty(); } - - constexpr auto reserve_hint() requires approximately_sized_range - { - return reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires approximately_sized_range; - { return reserve_hint(impl_); } - -private: - @*to-utf-view-impl*@ impl_; // @*exposition only*@ -}; - -template - to_utf16_view(R&&) -> to_utf16_view>; -``` - -#### 24.7.?.10 Class template `to_utf16_or_error_view` [range.transcoding.view.to_utf16_or_error] - -```c++ -template - requires view && @*code-unit*@> -class to_utf16_or_error_view : public view_interface> { -public: - constexpr to_utf16_or_error_view() requires default_initializable = default; - constexpr explicit to_utf16_or_error_view(V base) : impl_(std::move(base)) {} - - constexpr V base() const& requires copy_constructible { return impl_.base(); } - constexpr V base() && { return std::move(impl_).base(); } - - constexpr auto begin() { return impl_.begin(); } - constexpr auto begin() const - requires range && ((same_as, char32_t>) || (!forward_range)) - { - return impl_.begin(); - } - constexpr auto end() { return impl_.end(); } - constexpr auto end() const requires range - { - return impl_.end(); - } - - constexpr bool empty() const { return impl_.empty(); } - - constexpr size_t size() requires sized_range && same_as> - { - return impl_.size(); - } - - constexpr auto reserve_hint() requires approximately_sized_range - { - return reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires approximately_sized_range; - { return reserve_hint(impl_); } - -private: - @*to-utf-view-impl*@ impl_; // @*exposition only*@ -}; - -template - to_utf16_or_error_view(R&&) -> to_utf16_or_error_view>; -``` - -#### 24.7.?.11 Class template `to_utf32_view` [range.transcoding.view.to_utf32] - -```c++ -template - requires view && @*code-unit*@> -class to_utf32_view : public view_interface> { -public: - constexpr to_utf32_view() requires default_initializable = default; - constexpr explicit to_utf32_view(V base) : impl_(std::move(base)) {} - - constexpr V base() const& requires copy_constructible { return impl_.base(); } - constexpr V base() && { return std::move(impl_).base(); } - - constexpr auto begin() { return impl_.begin(); } - constexpr auto begin() const - requires range && ((same_as, char32_t>) || (!forward_range)) - { - return impl_.begin(); - } - constexpr auto end() { return impl_.end(); } - constexpr auto end() const requires range - { - return impl_.end(); - } - - constexpr bool empty() const { return impl_.empty(); } - - constexpr size_t size() requires sized_range && same_as> - { - return impl_.size(); - } - - constexpr auto reserve_hint() requires approximately_sized_range - { - return reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires approximately_sized_range; - { return reserve_hint(impl_); } - -private: - @*to-utf-view-impl*@ impl_; // @*exposition only*@ -}; - -template - to_utf32_view(R&&) -> to_utf32_view>; -``` - -#### 24.7.?.12 Class template `to_utf32_or_error_view` [range.transcoding.view.to_utf32_or_error] - -```c++ -template - requires view && @*code-unit*@> -class to_utf32_or_error_view : public view_interface> { -public: - constexpr to_utf32_or_error_view() requires default_initializable = default; - constexpr explicit to_utf32_or_error_view(V base) : impl_(std::move(base)) {} - - constexpr V base() const& requires copy_constructible { return impl_.base(); } - constexpr V base() && { return std::move(impl_).base(); } - - constexpr auto begin() { return impl_.begin(); } - constexpr auto begin() const - requires range && ((same_as, char32_t>) || (!forward_range)) - { - return impl_.begin(); - } - constexpr auto end() { return impl_.end(); } - constexpr auto end() const requires range - { - return impl_.end(); - } - - constexpr bool empty() const { return impl_.empty(); } - - constexpr auto reserve_hint() requires approximately_sized_range - { - return reserve_hint(impl_); - } - constexpr auto reserve_hint() const requires approximately_sized_range; - { return reserve_hint(impl_); } - -private: - @*to-utf-view-impl*@ impl_; // @*exposition only*@ -}; - -template - to_utf32_or_error_view(R&&) -> to_utf32_or_error_view>; -``` - ## Code unit adaptors Add the following subclause to [range.adaptors]{.sref}: @@ -1263,132 +1016,6 @@ a `const` overload of `begin()` that is non-caching. However, we do not provide [@P3725R1]-style `views::input_to_utf8` CPOs; for that use case, we expect users to simply spell `views::to_input | views::to_utf8`. -## Why There Are Three `to_utfN_view`s and No `to_utf_view` - -This section starts with an simplified, idealized, but unimplementable design, and works -backwards from there to various hypothetical alternatives, including the design that's -proposed in the current revision. - -### `to_utf_view` with Unary Constructor - -Imagine we had a single `to_utf_view`, and users specified which encoding to transcode -to via template parameter: - -```c++ -std::u32string transcode_to_utf8(const std::u8string& str) { - return std::ranges::to_utf_view(str) | std::ranges::to(); -} -``` - -Why doesn't this work? - -Well, in this scenario, `to_utf_view` would have two template parameters, one for the -`ToType` and one for the underlying view: - -```c++ -template<@*code-unit*@ ToType, input_range V> - requires view && @*code-unit*@> -class to_utf_view { -// ... -``` - -Spelling the constructor invocation as `std::ranges::to_utf_view(str)` doesn't -work, because CTAD is all-or-nothing; you can't specify the `ToType` explicitly and still -deduce the `input_range V`. - -### `to_utf_view` with Tag Type Constructor - -One alternative would be to have CTAD deduce the `charN_t` template parameter from the -parameters of the constructor using some kind of tag: - -```c++ -std::u32string transcode_to_utf8(const std::u8string& str) { - return std::ranges::to_utf_view(str, std::ranges::utf_tag{}) - | std::ranges::to(); -} -``` - -This is a viable alternative to the status quo. - -But let's revisit the unary constructor approach. - -### `to_utf_view` with Unary Constructor and `to_utfN_view` Views as Type Aliases - -Let's try keeping `std::ranges::to_utf_view`'s unary constructor from before, and then -we'll add `to_utf8_view`, `to_utf16_view`, and `to_utf32_view` as type aliases of -`to_utf_view`: - -```c++ -template -using to_utf8_view = to_utf_view; - -template -using to_utf16_view = to_utf_view; - -template -using to_utf32_view = to_utf_view; -``` - -Now let me fill in some additional background on how CTAD works for views. All views in -the standard have a user-defined deduction guide that ensures that when a range is passed -to the contructor of a view, it gets wrapped in `views::all_t`, e.g.: - -```c++ -template - explicit join_view(R&&) -> join_view>; -``` - -Miraculously, thanks to [@P1814R0], we could write a deduction guide like the following, -and all of the `to_utfN_view` aliases specified above would just work: - -```c++ -template<@*code-unit*@ ToType, class R> -to_utf_view(R&&) -> to_utf_view>; -``` - -But the problem is that, without going through an alias, it's still not possible to invoke -the constructor of `to_utf_view` in a way that activates that deduction guide. Users would -need to explicitly write down the type of the underlying view and the destination -encoding: - -```c++ -std::u32string transcode_to_utf8(const std::u8string& str) { - return std::ranges::to_utf_view>(str) - | std::ranges::to(); -} -``` - -Which isn't really viable. - -### `to_utfN_view` Views As Thin Wrappers Around an Implementation-Defined `@*to-utf-view-impl*@` - -This is the status quo in the current revision: rename `to_utf_view` to -`@*to-utf-view-impl*@` and add separate `to_utf8_view`, `to_utf16_view`, and -`to_utf32_view` classes that each contain a `@*to-utf-view-impl*@` data member. - -Although this wording strategy is somewhat novel, it allows us to write down conventional -user-defined deduction guides for each of these views: - -```c++ -template - to_utf8_view(R&&) -> to_utf8_view>; -``` - -## CPO Template - -The section above describes limitations imposed on us by the requirements of the deduction -guides of the view constructors. On the other hand, the CPOs have no such limitations. - -This paper introduces a novelty: a CPO template. This is the mechanism by which users can -decide the encoding that they're converting to via template parameter: - -``` -template -std::basic_string transcode_to(std::basic_string const& input) { - return input | to_utf | std::ranges::to>(); -} -``` - ## Optimizing for Double-Transcoding In generic code, it's possible to introduce transcoding views that wrap other transcoding @@ -1459,17 +1086,17 @@ The wording change that would enable this optimization is as follows: These concepts are true when the type in question is the iterator/sentinel of a transcoding view. -#### Exposition-only class template `@*to-utf-view-impl*@` +#### Class template `to_utf_view` ```diff -- using @*Parent*@ = @*maybe-const*@; // @*exposition only*@ +- using @*Parent*@ = @*maybe-const*@; // @*exposition only*@ - using @*Base*@ = @*maybe-const*@; // @*exposition only*@ + using @*innermost-parent*@ = @*unspecified*@ // @*exposition only*@ + using @*innermost-base*@ = @*unspecified*@ // @*exposition only*@ + static constexpr bool @*optimizing*@{@*to-utf-view-iterator-optimizable*@> ``` -#### Class `@*to-utf-view-impl*@::@*iterator*@` +#### Class `to_utf_view::@*iterator*@` ```diff - iterator_t<@*Base*@> current_ = iterator_t<@*Base*@>(); // @*exposition only*@ @@ -1539,10 +1166,10 @@ These concepts are true when the type in question is the iterator/sentinel of a } ``` -#### Class `@*to-utf-view-impl*@::@*sentinel*@` +#### Class `to_utf_view::@*sentinel*@` ```diff -- using @*Parent*@ = @*maybe-const*@; // @*exposition only*@ +- using @*Parent*@ = @*maybe-const*@; // @*exposition only*@ - using @*Base*@ = @*maybe-const*@; // @*exposition only*@ - sentinel_t<@*Base*@> end_ = sentinel_t<@*Base*@>(); + @@ -1570,6 +1197,14 @@ These concepts are true when the type in question is the iterator/sentinel of a - Replace bool OrError CTP with `to_utf_view_error_kind` enum class like ranges::subrange_kind +- Replace `to_utfX_view` classes with a single `to_utf_view` class with annoying + constructor tags to make CTAD work, per SG9 feedback from Kona 2025, and remove related + obsolete design discussion +- Fix a bug where the value type of `empty_view` was not set properly in the + CPO when using an `_or_error` CPO +- Remove section stating that the concept of a CPO template is a "novelty" + after it was pointed out in Kona 2025 that it has precedent in + `views::adjacent_transform` and elsewhere ## Changes since R8 diff --git a/tests/beman/utf_view/to_utf_view.t.cpp b/tests/beman/utf_view/to_utf_view.t.cpp index 40603eb..4cb6a1c 100644 --- a/tests/beman/utf_view/to_utf_view.t.cpp +++ b/tests/beman/utf_view/to_utf_view.t.cpp @@ -26,47 +26,54 @@ namespace beman::utf_view::tests { static_assert( std::input_iterator< std::ranges::iterator_t< - to_utf8_view< - std::ranges::subrange, std::default_sentinel_t>>>>); + to_utf_view< + std::ranges::subrange, std::default_sentinel_t>, + to_utf_view_error_kind::replacement, char8_t>>>); static_assert( std::input_iterator< - std::ranges::iterator_t< - to_utf8_view< - std::ranges::subrange, - std::default_sentinel_t>>>>); + std::ranges::iterator_t< + to_utf_view< + std::ranges::subrange< + test_comparable_input_iterator, std::default_sentinel_t>, + to_utf_view_error_kind::replacement, char8_t>>>); static_assert( std::input_iterator< - std::ranges::iterator_t< - to_utf8_view< - std::ranges::subrange, - std::default_sentinel_t>>>>); + std::ranges::iterator_t< + to_utf_view< + std::ranges::subrange< + test_copyable_input_iterator, std::default_sentinel_t>, + to_utf_view_error_kind::replacement, char8_t>>>); static_assert( !std::forward_iterator< - std::ranges::iterator_t< - to_utf8_view< - std::ranges::subrange, - std::default_sentinel_t>>>>); + std::ranges::iterator_t< + to_utf_view< + std::ranges::subrange< + test_copyable_input_iterator, std::default_sentinel_t>, + to_utf_view_error_kind::replacement, char8_t>>>); static_assert( std::forward_iterator< - std::ranges::sentinel_t< - to_utf8_view< - std::ranges::subrange, - test_forward_iterator>>>>); + std::ranges::sentinel_t< + to_utf_view< + std::ranges::subrange< + test_forward_iterator, test_forward_iterator>, + to_utf_view_error_kind::replacement, char8_t>>>); static_assert( std::bidirectional_iterator< - std::ranges::iterator_t< - to_utf8_view< - std::ranges::subrange, - std::default_sentinel_t>>>>); + std::ranges::iterator_t< + to_utf_view< + std::ranges::subrange< + test_bidi_iterator, std::default_sentinel_t>, + to_utf_view_error_kind::replacement, char8_t>>>); static_assert( std::bidirectional_iterator< - std::ranges::sentinel_t< - to_utf8_view< - std::ranges::subrange, - test_bidi_iterator>>>>); + std::ranges::sentinel_t< + to_utf_view< + std::ranges::subrange< + test_bidi_iterator, test_bidi_iterator>, + to_utf_view_error_kind::replacement, char8_t>>>); template using test_case_code_unit_result = std::expected; @@ -350,7 +357,7 @@ template constexpr bool input_iterator_test(std::initializer_list const single_arr) { test_input_iterator single_begin(single_arr); std::ranges::subrange subrange{std::move(single_begin), std::default_sentinel}; - auto single_view{to_utf32_view(std::move(subrange))}; + auto single_view{std::move(subrange) | to_utf32}; std::u32string single_u32{single_view | std::ranges::to()}; if (single_u32.size() != 1 || single_u32.at(0) != U'x') { return false; @@ -362,7 +369,7 @@ template constexpr bool forward_iterator_test(std::initializer_list const single_arr) { test_forward_iterator single_begin(single_arr); std::ranges::subrange subrange{std::move(single_begin), std::default_sentinel}; - auto single_view{to_utf32_view(std::move(subrange))}; + auto single_view{std::move(subrange) | to_utf32}; std::u32string single_u32{single_view | std::ranges::to()}; if (single_u32.size() != 1 || single_u32.at(0) != U'x') { return false; @@ -374,7 +381,7 @@ template constexpr bool bidi_iterator_test(std::initializer_list const single_arr) { test_bidi_iterator single_begin(single_arr); std::ranges::subrange subrange{std::move(single_begin), std::default_sentinel}; - auto single_view{to_utf32_view(std::move(subrange))}; + auto single_view{std::move(subrange) | to_utf32}; std::u32string single_u32{single_view | std::ranges::to()}; if (single_u32.size() != 1 || single_u32.at(0) != U'x') { return false; @@ -386,8 +393,8 @@ template constexpr bool double_encode_test(std::initializer_list const single_arr) { test_forward_iterator single_begin(single_arr); std::ranges::subrange subrange{std::move(single_begin), std::default_sentinel}; - auto single_view{to_utf32_view(std::move(subrange))}; - auto double_encoded_view{to_utf8_view(std::move(single_view))}; + auto single_view{std::move(subrange) | to_utf32}; + auto double_encoded_view{std::move(single_view) | to_utf8}; std::u8string single_u8{double_encoded_view | std::ranges::to()}; if (single_u8.size() != 1 || single_u8.at(0) != U'x') { return false; @@ -860,12 +867,12 @@ CONSTEXPR_UNLESS_MSVC bool wrapped_view_custom_sentinel_test() { test_bidi_iterator end_it{nums}; std::ranges::advance(end_it, std::ranges::size(nums)); std::ranges::subrange u32_subrange{start_it, end_it}; - to_utf16_view u16v{u32_subrange}; + auto u16v{u32_subrange | to_utf16}; std::ranges::subrange custom_u16_subrange{u16v.begin(), end_at_exclamation_mark_sentinel}; if (std::ranges::distance(custom_u16_subrange) != 2) { return false; } - to_utf32_view custom_u32v{custom_u16_subrange}; + auto custom_u32v{custom_u16_subrange | to_utf32}; if (std::ranges::distance(custom_u32v) != 2) { return false; } @@ -875,7 +882,11 @@ CONSTEXPR_UNLESS_MSVC bool wrapped_view_custom_sentinel_test() { constexpr bool empty_test() { static_assert(std::is_same_v | to_utf8), std::ranges::empty_view>); - auto empty_utf{to_utf8_view{std::views::empty}}; + static_assert( + std::is_same_v< + decltype(std::views::empty | to_utf8_or_error), + std::ranges::empty_view>>); + auto empty_utf{to_utf_view{std::views::empty, detail::nontype, to_utf8_tag}}; if (!empty_utf.empty()) { return false; } @@ -1451,7 +1462,7 @@ bool decode_test() { constexpr bool input_range_equality_test() { std::initializer_list arr{U'\u03D5'}; test_copyable_input_iterator input_it(arr); - to_utf8_view u8v{std::ranges::subrange{input_it, std::default_sentinel}}; + auto u8v{std::ranges::subrange{input_it, std::default_sentinel} | to_utf8}; auto it1{u8v.begin()}; auto it2{it1}; ++it2;