[libcxx-commits] [libcxx] 9924d8d - [libc++][ranges] Implement `views::take`.

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Fri May 6 14:16:27 PDT 2022


Author: Konstantin Varlamov
Date: 2022-05-06T14:16:13-07:00
New Revision: 9924d8d66ae103bee09387de3bef226d745807a8

URL: https://github.com/llvm/llvm-project/commit/9924d8d66ae103bee09387de3bef226d745807a8
DIFF: https://github.com/llvm/llvm-project/commit/9924d8d66ae103bee09387de3bef226d745807a8.diff

LOG: [libc++][ranges] Implement `views::take`.

The view itself has been implemented previously -- this patch only adds
the ability to pipe it.

Also implements [P1739](https://wg21.link/p1739) (partially) and [LWG3407](https://wg21.link/lwg3407).

Differential Revision: https://reviews.llvm.org/D123600

Added: 
    libcxx/include/__fwd/span.h
    libcxx/include/__fwd/string_view.h
    libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/__ranges/take_view.h
    libcxx/include/module.modulemap
    libcxx/include/span
    libcxx/include/string_view
    libcxx/test/libcxx/lint/lint_modulemap.sh.py
    libcxx/test/libcxx/private_headers.verify.cpp
    libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index f24440b3bd424..5b04f013c052e 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -254,6 +254,8 @@ set(files
   __functional/unary_negate.h
   __functional/unwrap_ref.h
   __functional/weak_result_type.h
+  __fwd/span.h
+  __fwd/string_view.h
   __hash_table
   __ios/fpos.h
   __iterator/access.h

diff  --git a/libcxx/include/__fwd/span.h b/libcxx/include/__fwd/span.h
new file mode 100644
index 0000000000000..f38b8cfa05260
--- /dev/null
+++ b/libcxx/include/__fwd/span.h
@@ -0,0 +1,37 @@
+// -*- C++ -*-
+//===---------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_FWD_SPAN_H
+#define _LIBCPP_FWD_SPAN_H
+
+#include <__config>
+#include <cstddef>
+#include <limits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17
+
+inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();
+template <typename _Tp, size_t _Extent = dynamic_extent> class span;
+
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_FWD_SPAN_H

diff  --git a/libcxx/include/__fwd/string_view.h b/libcxx/include/__fwd/string_view.h
new file mode 100644
index 0000000000000..e5e77b292a6ef
--- /dev/null
+++ b/libcxx/include/__fwd/string_view.h
@@ -0,0 +1,37 @@
+// -*- C++ -*-
+//===---------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_FWD_STRING_VIEW_H
+#define _LIBCPP_FWD_STRING_VIEW_H
+
+#include <__config>
+#include <iosfwd> // char_traits
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template<class _CharT, class _Traits = char_traits<_CharT> >
+class _LIBCPP_TEMPLATE_VIS basic_string_view;
+
+typedef basic_string_view<char>     string_view;
+#ifndef _LIBCPP_HAS_NO_CHAR8_T
+typedef basic_string_view<char8_t>  u8string_view;
+#endif
+typedef basic_string_view<char16_t> u16string_view;
+typedef basic_string_view<char32_t> u32string_view;
+#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+typedef basic_string_view<wchar_t>  wstring_view;
+#endif
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_FWD_STRING_VIEW_H

diff  --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h
index 3ad7810a9c2b4..11d5c9fc36bc4 100644
--- a/libcxx/include/__ranges/take_view.h
+++ b/libcxx/include/__ranges/take_view.h
@@ -9,18 +9,29 @@
 #ifndef _LIBCPP___RANGES_TAKE_VIEW_H
 #define _LIBCPP___RANGES_TAKE_VIEW_H
 
+#include <__algorithm/min.h>
 #include <__algorithm/ranges_min.h>
 #include <__config>
+#include <__functional/bind_back.h>
+#include <__fwd/span.h>
+#include <__fwd/string_view.h>
 #include <__iterator/concepts.h>
 #include <__iterator/counted_iterator.h>
 #include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
 #include <__ranges/access.h>
 #include <__ranges/all.h>
 #include <__ranges/concepts.h>
+#include <__ranges/empty_view.h>
 #include <__ranges/enable_borrowed_range.h>
+#include <__ranges/iota_view.h>
+#include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
+#include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
+#include <__utility/auto_cast.h>
+#include <__utility/forward.h>
 #include <__utility/move.h>
 #include <concepts>
 #include <type_traits>
@@ -115,7 +126,6 @@ class take_view : public view_interface<take_view<_View>> {
     }
   }
 
-
   _LIBCPP_HIDE_FROM_ABI
   constexpr auto size() requires sized_range<_View> {
     auto __n = ranges::size(__base_);
@@ -174,6 +184,148 @@ take_view(_Range&&, range_
diff erence_t<_Range>) -> take_view<views::all_t<_Range
 template<class _Tp>
 inline constexpr bool enable_borrowed_range<take_view<_Tp>> = enable_borrowed_range<_Tp>;
 
+namespace views {
+namespace __take {
+
+template <class _Tp>
+inline constexpr bool __is_empty_view = false;
+
+template <class _Tp>
+inline constexpr bool __is_empty_view<empty_view<_Tp>> = true;
+
+template <class _Tp>
+inline constexpr bool __is_passthrough_specialization = false;
+
+template <class _Tp, size_t _Extent>
+inline constexpr bool __is_passthrough_specialization<span<_Tp, _Extent>> = true;
+
+template <class _CharT, class _Traits>
+inline constexpr bool __is_passthrough_specialization<basic_string_view<_CharT, _Traits>> = true;
+
+template <class _Iter, class _Sent, subrange_kind _Kind>
+inline constexpr bool __is_passthrough_specialization<subrange<_Iter, _Sent, _Kind>> = true;
+
+template <class _Tp>
+inline constexpr bool __is_iota_specialization = false;
+
+template <class _Np, class _Bound>
+inline constexpr bool __is_iota_specialization<iota_view<_Np, _Bound>> = true;
+
+template <class _Tp>
+struct __passthrough_type;
+
+template <class _Tp, size_t _Extent>
+struct __passthrough_type<span<_Tp, _Extent>> {
+  using type = span<_Tp>;
+};
+
+template <class _CharT, class _Traits>
+struct __passthrough_type<basic_string_view<_CharT, _Traits>> {
+  using type = basic_string_view<_CharT, _Traits>;
+};
+
+template <class _Iter, class _Sent, subrange_kind _Kind>
+struct __passthrough_type<subrange<_Iter, _Sent, _Kind>> {
+  using type = subrange<_Iter>;
+};
+
+template <class _Tp>
+using __passthrough_type_t = typename __passthrough_type<_Tp>::type;
+
+struct __fn {
+  // [range.take.overview]: the `empty_view` case.
+  template <class _Range, convertible_to<range_
diff erence_t<_Range>> _Np>
+    requires __is_empty_view<remove_cvref_t<_Range>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator()(_Range&& __range, _Np&&) const
+    noexcept(noexcept(_LIBCPP_AUTO_CAST(std::forward<_Range>(__range))))
+    -> decltype(      _LIBCPP_AUTO_CAST(std::forward<_Range>(__range)))
+    { return          _LIBCPP_AUTO_CAST(std::forward<_Range>(__range)); }
+
+  // [range.take.overview]: the `span | basic_string_view | subrange` case.
+  template <class _Range,
+            convertible_to<range_
diff erence_t<_Range>> _Np,
+            class _RawRange = remove_cvref_t<_Range>,
+            class _Dist = range_
diff erence_t<_Range>>
+    requires (!__is_empty_view<_RawRange> &&
+              random_access_range<_RawRange> &&
+              sized_range<_RawRange> &&
+              __is_passthrough_specialization<_RawRange>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator()(_Range&& __rng, _Np&& __n) const
+    noexcept(noexcept(__passthrough_type_t<_RawRange>(
+                              ranges::begin(__rng),
+                              ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n))
+                              )))
+    -> decltype(      __passthrough_type_t<_RawRange>(
+                              // Note: deliberately not forwarding `__rng` to guard against double moves.
+                              ranges::begin(__rng),
+                              ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n))
+                              ))
+    { return          __passthrough_type_t<_RawRange>(
+                              ranges::begin(__rng),
+                              ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n))
+                              ); }
+
+  // [range.take.overview]: the `iota_view` case.
+  template <class _Range,
+            convertible_to<range_
diff erence_t<_Range>> _Np,
+            class _RawRange = remove_cvref_t<_Range>,
+            class _Dist = range_
diff erence_t<_Range>>
+    requires (!__is_empty_view<_RawRange> &&
+              random_access_range<_RawRange> &&
+              sized_range<_RawRange> &&
+              __is_iota_specialization<_RawRange>)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator()(_Range&& __rng, _Np&& __n) const
+    noexcept(noexcept(ranges::iota_view(
+                              *ranges::begin(__rng),
+                              *ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n))
+                              )))
+    -> decltype(      ranges::iota_view(
+                              // Note: deliberately not forwarding `__rng` to guard against double moves.
+                              *ranges::begin(__rng),
+                              *ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n))
+                              ))
+    { return          ranges::iota_view(
+                              *ranges::begin(__rng),
+                              *ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n))
+                              ); }
+
+  // [range.take.overview]: the "otherwise" case.
+  template <class _Range, convertible_to<range_
diff erence_t<_Range>> _Np,
+            class _RawRange = remove_cvref_t<_Range>>
+    // Note: without specifically excluding the other cases, GCC sees this overload as ambiguous with the other
+    // overloads.
+    requires (!(__is_empty_view<_RawRange> ||
+               (__is_iota_specialization<_RawRange> &&
+                sized_range<_RawRange> &&
+                random_access_range<_RawRange>) ||
+               (__is_passthrough_specialization<_RawRange> &&
+                sized_range<_RawRange> &&
+                random_access_range<_RawRange>)
+             ))
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator()(_Range&& __range, _Np&& __n) const
+    noexcept(noexcept(take_view(std::forward<_Range>(__range), std::forward<_Np>(__n))))
+    -> decltype(      take_view(std::forward<_Range>(__range), std::forward<_Np>(__n)))
+    { return          take_view(std::forward<_Range>(__range), std::forward<_Np>(__n)); }
+
+  template <class _Np>
+    requires constructible_from<decay_t<_Np>, _Np>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator()(_Np&& __n) const
+    noexcept(is_nothrow_constructible_v<decay_t<_Np>, _Np>)
+  { return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Np>(__n))); }
+};
+
+} // namespace __take
+
+inline namespace __cpo {
+  inline constexpr auto take = __take::__fn{};
+} // namespace __cpo
+} // namespace views
+
 } // namespace ranges
 
 #endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 65e628d44da84..6cf0467d32264 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -919,6 +919,7 @@ module std [system] {
     header "span"
     export ranges.__ranges.enable_borrowed_range
     export version
+    module span_fwd { private header "__fwd/span.h" }
   }
   module sstream {
     header "sstream"
@@ -950,6 +951,7 @@ module std [system] {
     export initializer_list
     export __string
     export *
+    module string_view_fwd { private header "__fwd/string_view.h" }
   }
   module strstream {
     header "strstream"

diff  --git a/libcxx/include/span b/libcxx/include/span
index b96466550e8fa..98ecfd4fa61ad 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -130,6 +130,7 @@ template<class R>
 #include <__assert> // all public C++ headers provide the assertion handler
 #include <__config>
 #include <__debug>
+#include <__fwd/span.h>
 #include <__iterator/concepts.h>
 #include <__iterator/wrap_iter.h>
 #include <__ranges/concepts.h>
@@ -155,9 +156,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER > 17
 
-inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();
-template <typename _Tp, size_t _Extent = dynamic_extent> class span;
-
 template <class _Tp>
 struct __is_std_array : false_type {};
 

diff  --git a/libcxx/include/string_view b/libcxx/include/string_view
index 7c5d52b877d1c..bd31b8739d482 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -199,6 +199,7 @@ namespace std {
 #include <__algorithm/min.h>
 #include <__assert> // all public C++ headers provide the assertion handler
 #include <__config>
+#include <__fwd/string_view.h>
 #include <__ranges/concepts.h>
 #include <__ranges/data.h>
 #include <__ranges/enable_borrowed_range.h>
@@ -223,19 +224,6 @@ _LIBCPP_PUSH_MACROS
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template<class _CharT, class _Traits = char_traits<_CharT> >
-    class _LIBCPP_TEMPLATE_VIS basic_string_view;
-
-typedef basic_string_view<char>     string_view;
-#ifndef _LIBCPP_HAS_NO_CHAR8_T
-typedef basic_string_view<char8_t>  u8string_view;
-#endif
-typedef basic_string_view<char16_t> u16string_view;
-typedef basic_string_view<char32_t> u32string_view;
-#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
-typedef basic_string_view<wchar_t>  wstring_view;
-#endif
-
 // TODO: This is a workaround for some vendors to carry a downstream 
diff  to accept `nullptr` in
 //       string_view constructors. This can be refactored when this exact form isn't needed anymore.
 template <class _Traits>

diff  --git a/libcxx/test/libcxx/lint/lint_modulemap.sh.py b/libcxx/test/libcxx/lint/lint_modulemap.sh.py
old mode 100644
new mode 100755
index 65c58c54b812b..e167813cfc099
--- a/libcxx/test/libcxx/lint/lint_modulemap.sh.py
+++ b/libcxx/test/libcxx/lint/lint_modulemap.sh.py
@@ -26,6 +26,9 @@
                 elif re.match(r'^\s*module (\w+)\s+[{] private header "__\w+/\1[.]h" [}]', line):
                     # It's a private submodule, such as <__utility/swap.h>.
                     pass
+                elif re.match(r'^\s*module (\w+)_fwd\s+[{] private header "__fwd/\1[.]h" [}]', line):
+                    # It's a private submodule with forward declarations, such as <__fwd/span.h>.
+                    pass
                 else:
                     okay = False
                     print("LINE DOESN'T MATCH REGEX in libcxx/include/module.modulemap!")

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index c0228a94590f6..58165caff3f26 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -286,6 +286,8 @@ END-SCRIPT
 #include <__functional/unary_negate.h> // expected-error@*:* {{use of private header from outside its module: '__functional/unary_negate.h'}}
 #include <__functional/unwrap_ref.h> // expected-error@*:* {{use of private header from outside its module: '__functional/unwrap_ref.h'}}
 #include <__functional/weak_result_type.h> // expected-error@*:* {{use of private header from outside its module: '__functional/weak_result_type.h'}}
+#include <__fwd/span.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/span.h'}}
+#include <__fwd/string_view.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/string_view.h'}}
 #include <__ios/fpos.h> // expected-error@*:* {{use of private header from outside its module: '__ios/fpos.h'}}
 #include <__iterator/access.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/access.h'}}
 #include <__iterator/advance.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/advance.h'}}

diff  --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index e8af0fe9b5a39..16c54246bcd1b 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -91,8 +91,9 @@ static_assert(test(std::views::counted, a, 10));
 //static_assert(test(std::views::elements<0>, pairs));
 static_assert(test(std::views::filter, a, [](int x){ return x < 10; }));
 //static_assert(test(std::views::join, arrays));
+//static_assert(test(std::views::split, a, 4));
 static_assert(test(std::views::lazy_split, a, 4));
 static_assert(test(std::views::reverse, a));
-//static_assert(test(std::views::take, a, 10));
+static_assert(test(std::views::take, a, 10));
 //static_assert(test(std::views::take_while, a, [](int x){ return x < 10; }));
 static_assert(test(std::views::transform, a, [](int x){ return x + 1; }));

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp
new file mode 100644
index 0000000000000..a326f73f77d02
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp
@@ -0,0 +1,207 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::views::take
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <string_view>
+#include <utility>
+#include "test_iterators.h"
+#include "types.h"
+
+template <class View, class T>
+concept CanBePiped = requires (View&& view, T&& t) {
+  { std::forward<View>(view) | std::forward<T>(t) };
+};
+
+struct SizedView : std::ranges::view_base {
+  int* begin_ = nullptr;
+  int* end_ = nullptr;
+  constexpr SizedView(int* begin, int* end) : begin_(begin), end_(end) {}
+
+  constexpr auto begin() const { return forward_iterator<int*>(begin_); }
+  constexpr auto end() const { return sized_sentinel<forward_iterator<int*>>(forward_iterator<int*>(end_)); }
+};
+static_assert(std::ranges::forward_range<SizedView>);
+static_assert(std::ranges::sized_range<SizedView>);
+static_assert(std::ranges::view<SizedView>);
+
+template <class T>
+constexpr void test_small_range(const T& input) {
+  constexpr int N = 100;
+  auto size = std::ranges::size(input);
+
+  auto result = input | std::views::take(N);
+  assert(size < N);
+  assert(result.size() == size);
+}
+
+constexpr bool test() {
+  constexpr int N = 8;
+  int buf[N] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Test that `std::views::take` is a range adaptor.
+  {
+    using SomeView = SizedView;
+
+    // Test `view | views::take`
+    {
+      SomeView view(buf, buf + N);
+      std::same_as<std::ranges::take_view<SomeView>> decltype(auto) result = view | std::views::take(3);
+      assert(result.base().begin_ == buf);
+      assert(result.base().end_ == buf + N);
+      assert(result.size() == 3);
+    }
+
+    // Test `adaptor | views::take`
+    {
+      SomeView view(buf, buf + N);
+      auto f = [](int i) { return i; };
+      auto const partial = std::views::transform(f) | std::views::take(3);
+
+      using Result = std::ranges::take_view<std::ranges::transform_view<SomeView, decltype(f)>>;
+      std::same_as<Result> decltype(auto) result = partial(view);
+      assert(result.base().base().begin_ == buf);
+      assert(result.base().base().end_ == buf + N);
+      assert(result.size() == 3);
+    }
+
+    // Test `views::take | adaptor`
+    {
+      SomeView view(buf, buf + N);
+      auto f = [](int i) { return i; };
+      auto const partial = std::views::take(3) | std::views::transform(f);
+
+      using Result = std::ranges::transform_view<std::ranges::take_view<SomeView>, decltype(f)>;
+      std::same_as<Result> decltype(auto) result = partial(view);
+      assert(result.base().base().begin_ == buf);
+      assert(result.base().base().end_ == buf + N);
+      assert(result.size() == 3);
+    }
+
+    // Check SFINAE friendliness
+    {
+      struct NotAView { };
+      static_assert(!std::is_invocable_v<decltype(std::views::take)>);
+      static_assert(!std::is_invocable_v<decltype(std::views::take), NotAView, int>);
+      static_assert( CanBePiped<SomeView&,   decltype(std::views::take(3))>);
+      static_assert( CanBePiped<int(&)[10],  decltype(std::views::take(3))>);
+      static_assert(!CanBePiped<int(&&)[10], decltype(std::views::take(3))>);
+      static_assert(!CanBePiped<NotAView,    decltype(std::views::take(3))>);
+
+      static_assert(!CanBePiped<SomeView&,   decltype(std::views::take(/*n=*/NotAView{}))>);
+    }
+  }
+
+  {
+    static_assert(std::same_as<decltype(std::views::take), decltype(std::ranges::views::take)>);
+  }
+
+  // `views::take(empty_view, n)` returns an `empty_view`.
+  {
+    using Result = std::ranges::empty_view<int>;
+    [[maybe_unused]] std::same_as<Result> decltype(auto) result = std::views::empty<int> | std::views::take(3);
+  }
+
+  // `views::take(span, n)` returns a `span`.
+  {
+    std::span<int> s(buf);
+    std::same_as<decltype(s)> decltype(auto) result = s | std::views::take(3);
+    assert(result.size() == 3);
+  }
+
+  // `views::take(span, n)` returns a `span` with a dynamic extent, regardless of the input `span`.
+  {
+    std::span<int, 8> s(buf);
+    std::same_as<std::span<int, std::dynamic_extent>> decltype(auto) result = s | std::views::take(3);
+    assert(result.size() == 3);
+  }
+
+  // `views::take(string_view, n)` returns a `string_view`.
+  {
+    {
+      std::string_view sv = "abcdef";
+      std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::take(3);
+      assert(result.size() == 3);
+    }
+
+    {
+      std::u32string_view sv = U"abcdef";
+      std::same_as<decltype(sv)> decltype(auto) result = sv | std::views::take(3);
+      assert(result.size() == 3);
+    }
+  }
+
+  // `views::take(subrange, n)` returns a `subrange`.
+  {
+    auto subrange = std::ranges::subrange(buf, buf + N);
+    using Result = std::ranges::subrange<int*>;
+    std::same_as<Result> decltype(auto) result = subrange | std::views::take(3);
+    assert(result.size() == 3);
+  }
+
+  // `views::take(subrange, n)` doesn't return a `subrange` if it's not a random access range.
+  {
+    SizedView v(buf, buf + N);
+    auto subrange = std::ranges::subrange(v.begin(), v.end());
+
+    using Result = std::ranges::take_view<std::ranges::subrange<forward_iterator<int*>,
+        sized_sentinel<forward_iterator<int*>>>>;
+    std::same_as<Result> decltype(auto) result = subrange | std::views::take(3);
+    assert(result.size() == 3);
+  }
+
+  // `views::take(subrange, n)` returns a `subrange` with all default template arguments.
+  {
+    std::ranges::subrange<int*, sized_sentinel<int*>, std::ranges::subrange_kind::sized> subrange;
+
+    using Result = std::ranges::subrange<int*, int*, std::ranges::subrange_kind::sized>;
+    [[maybe_unused]] std::same_as<Result> decltype(auto) result = subrange | std::views::take(3);
+  }
+
+  // `views::take(iota_view, n)` returns an `iota_view`.
+  {
+    auto iota = std::views::iota(1, 8);
+    // The second template argument of the resulting `iota_view` is 
diff erent because it has to be able to hold
+    // the `range_
diff erence_t` of the input `iota_view`.
+    using Result = std::ranges::iota_view<int, std::ranges::range_
diff erence_t<decltype(iota)>>;
+    std::same_as<Result> decltype(auto) result = iota | std::views::take(3);
+    assert(result.size() == 3);
+  }
+
+  // When the size of the input range `s` is shorter than `n`, only `s` elements are taken.
+  {
+    test_small_range(std::span(buf));
+    test_small_range(std::string_view("abcdef"));
+    test_small_range(std::ranges::subrange(buf, buf + N));
+    test_small_range(std::views::iota(1, 8));
+  }
+
+  // Test that it's possible to call `std::views::take` with any single argument as long as the resulting closure is
+  // never invoked. There is no good use case for it, but it's valid.
+  {
+    struct X { };
+    [[maybe_unused]] auto partial = std::views::take(X{});
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}


        


More information about the libcxx-commits mailing list