[libcxx-commits] [libcxx] 502e5df - [libc++] Implement `ranges::{cbegin, cend}` per the spec.

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jan 4 13:19:15 PST 2022


Author: Arthur O'Dwyer
Date: 2022-01-04T16:18:41-05:00
New Revision: 502e5df0e08e0ea625b528e45fc92257273b6d89

URL: https://github.com/llvm/llvm-project/commit/502e5df0e08e0ea625b528e45fc92257273b6d89
DIFF: https://github.com/llvm/llvm-project/commit/502e5df0e08e0ea625b528e45fc92257273b6d89.diff

LOG: [libc++] Implement `ranges::{cbegin,cend}` per the spec.

The big change here is that they now work as intended for rvalues,
e.g. `ranges::cbegin(std::string_view("hello"))`.
Also, add tests verifying their return types.

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

Added: 
    

Modified: 
    libcxx/include/__ranges/access.h
    libcxx/test/std/ranges/range.access/begin.pass.cpp
    libcxx/test/std/ranges/range.access/end.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index 91dc3055c86da..4a1242130ac05 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -160,20 +160,19 @@ namespace ranges {
 namespace __cbegin {
   struct __fn {
     template <class _Tp>
-      requires invocable<decltype(ranges::begin), _Tp const&>
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const
-      noexcept(noexcept(ranges::begin(_VSTD::as_const(__t))))
-    {
-      return ranges::begin(_VSTD::as_const(__t));
-    }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+    constexpr auto operator()(_Tp& __t) const
+      noexcept(noexcept(ranges::begin(static_cast<const _Tp&>(__t))))
+      -> decltype(      ranges::begin(static_cast<const _Tp&>(__t)))
+      { return          ranges::begin(static_cast<const _Tp&>(__t)); }
 
     template <class _Tp>
-      requires is_rvalue_reference_v<_Tp> && invocable<decltype(ranges::begin), _Tp const&&>
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::begin(static_cast<_Tp const&&>(__t))))
-    {
-      return ranges::begin(static_cast<_Tp const&&>(__t));
-    }
+      requires is_rvalue_reference_v<_Tp&&>
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+    constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::begin(static_cast<const _Tp&&>(__t))))
+      -> decltype(      ranges::begin(static_cast<const _Tp&&>(__t)))
+      { return          ranges::begin(static_cast<const _Tp&&>(__t)); }
   };
 }
 
@@ -188,20 +187,19 @@ namespace ranges {
 namespace __cend {
   struct __fn {
     template <class _Tp>
-      requires invocable<decltype(ranges::end), _Tp const&>
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const
-      noexcept(noexcept(ranges::end(_VSTD::as_const(__t))))
-    {
-      return ranges::end(_VSTD::as_const(__t));
-    }
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+    constexpr auto operator()(_Tp& __t) const
+      noexcept(noexcept(ranges::end(static_cast<const _Tp&>(__t))))
+      -> decltype(      ranges::end(static_cast<const _Tp&>(__t)))
+      { return          ranges::end(static_cast<const _Tp&>(__t)); }
 
     template <class _Tp>
-      requires is_rvalue_reference_v<_Tp> && invocable<decltype(ranges::end), _Tp const&&>
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::end(static_cast<_Tp const&&>(__t))))
-    {
-      return ranges::end(static_cast<_Tp const&&>(__t));
-    }
+      requires is_rvalue_reference_v<_Tp&&>
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+    constexpr auto operator()(_Tp&& __t) const
+      noexcept(noexcept(ranges::end(static_cast<const _Tp&&>(__t))))
+      -> decltype(      ranges::end(static_cast<const _Tp&&>(__t)))
+      { return          ranges::end(static_cast<const _Tp&&>(__t)); }
   };
 }
 

diff  --git a/libcxx/test/std/ranges/range.access/begin.pass.cpp b/libcxx/test/std/ranges/range.access/begin.pass.cpp
index 95d2196803b28..1a6951967f88a 100644
--- a/libcxx/test/std/ranges/range.access/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/begin.pass.cpp
@@ -11,6 +11,7 @@
 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
 
 // std::ranges::begin
+// std::ranges::cbegin
 
 #include <ranges>
 
@@ -18,8 +19,8 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
-using RangeBeginT = decltype(std::ranges::begin)&;
-using RangeCBeginT = decltype(std::ranges::cbegin)&;
+using RangeBeginT = decltype(std::ranges::begin);
+using RangeCBeginT = decltype(std::ranges::cbegin);
 
 static int globalBuff[8];
 
@@ -49,6 +50,28 @@ static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember &&>);
 static_assert( std::is_invocable_v<RangeCBeginT, BeginMember const&>);
 static_assert( std::is_invocable_v<RangeCBeginT, BeginMember const&&>);
 
+constexpr bool testReturnTypes() {
+  {
+    int *x[2];
+    ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int**);
+    ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), int* const*);
+  }
+  {
+    int x[2][2];
+    ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int(*)[2]);
+    ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), const int(*)[2]);
+  }
+  {
+    struct Different {
+      char*& begin();
+      short*& begin() const;
+    } x;
+    ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), char*);
+    ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), short*);
+  }
+  return true;
+}
+
 constexpr bool testArray() {
   int a[2];
   assert(std::ranges::begin(a) == a);
@@ -118,12 +141,18 @@ constexpr bool testBeginMember() {
   BeginMember a;
   assert(std::ranges::begin(a) == &a.x);
   assert(std::ranges::cbegin(a) == &a.x);
+  static_assert(!std::is_invocable_v<RangeBeginT, BeginMember&&>);
+  static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember&&>);
 
   NonConstBeginMember b;
   assert(std::ranges::begin(b) == &b.x);
+  static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember&>);
 
   EnabledBorrowingBeginMember c;
+  assert(std::ranges::begin(c) == &globalBuff[0]);
+  assert(std::ranges::cbegin(c) == &globalBuff[0]);
   assert(std::ranges::begin(std::move(c)) == &globalBuff[0]);
+  assert(std::ranges::cbegin(std::move(c)) == &globalBuff[0]);
 
   BeginMemberFunction d;
   assert(std::ranges::begin(d) == &d.x);
@@ -202,44 +231,44 @@ static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsPtrConvertib
 constexpr bool testBeginFunction() {
   BeginFunction a{};
   const BeginFunction aa{};
-  static_assert(!std::invocable<decltype(std::ranges::begin), decltype((a))>);
-  assert(std::ranges::begin(aa) == &aa.x);
+  static_assert(!std::invocable<RangeBeginT, decltype((a))>);
   assert(std::ranges::cbegin(a) == &a.x);
+  assert(std::ranges::begin(aa) == &aa.x);
   assert(std::ranges::cbegin(aa) == &aa.x);
 
   BeginFunctionByValue b{};
   const BeginFunctionByValue bb{};
   assert(std::ranges::begin(b) == &globalBuff[1]);
-  assert(std::ranges::begin(bb) == &globalBuff[1]);
   assert(std::ranges::cbegin(b) == &globalBuff[1]);
+  assert(std::ranges::begin(bb) == &globalBuff[1]);
   assert(std::ranges::cbegin(bb) == &globalBuff[1]);
 
   BeginFunctionEnabledBorrowing c{};
   const BeginFunctionEnabledBorrowing cc{};
   assert(std::ranges::begin(std::move(c)) == &globalBuff[2]);
-  static_assert(!std::invocable<decltype(std::ranges::cbegin), decltype(std::move(c))>);
+  assert(std::ranges::cbegin(std::move(c)) == &globalBuff[2]);
   assert(std::ranges::begin(std::move(cc)) == &globalBuff[2]);
   assert(std::ranges::cbegin(std::move(cc)) == &globalBuff[2]);
 
   BeginFunctionReturnsEmptyPtr d{};
   const BeginFunctionReturnsEmptyPtr dd{};
-  static_assert(!std::invocable<decltype(std::ranges::begin), decltype((d))>);
-  assert(std::ranges::begin(dd) == &dd.x);
+  static_assert(!std::invocable<RangeBeginT, decltype((d))>);
   assert(std::ranges::cbegin(d) == &d.x);
+  assert(std::ranges::begin(dd) == &dd.x);
   assert(std::ranges::cbegin(dd) == &dd.x);
 
   BeginFunctionWithDataMember e{};
   const BeginFunctionWithDataMember ee{};
-  static_assert(!std::invocable<decltype(std::ranges::begin), decltype((e))>);
+  static_assert(!std::invocable<RangeBeginT, decltype((e))>);
   assert(std::ranges::begin(ee) == &ee.x);
   assert(std::ranges::cbegin(e) == &e.x);
   assert(std::ranges::cbegin(ee) == &ee.x);
 
   BeginFunctionWithPrivateBeginMember f{};
   const BeginFunctionWithPrivateBeginMember ff{};
-  static_assert(!std::invocable<decltype(std::ranges::begin), decltype((f))>);
-  assert(std::ranges::begin(ff) == &ff.y);
+  static_assert(!std::invocable<RangeBeginT, decltype((f))>);
   assert(std::ranges::cbegin(f) == &f.y);
+  assert(std::ranges::begin(ff) == &ff.y);
   assert(std::ranges::cbegin(ff) == &ff.y);
 
   return true;
@@ -274,8 +303,9 @@ struct BeginReturnsArrayRef {
 static_assert(noexcept(std::ranges::begin(brar)));
 static_assert(noexcept(std::ranges::cbegin(brar)));
 
-
 int main(int, char**) {
+  static_assert(testReturnTypes());
+
   testArray();
   static_assert(testArray());
 

diff  --git a/libcxx/test/std/ranges/range.access/end.pass.cpp b/libcxx/test/std/ranges/range.access/end.pass.cpp
index 84b4904d8f963..27eaf741a1131 100644
--- a/libcxx/test/std/ranges/range.access/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/end.pass.cpp
@@ -11,6 +11,7 @@
 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
 
 // std::ranges::end
+// std::ranges::cend
 
 #include <ranges>
 
@@ -18,8 +19,8 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
-using RangeEndT = decltype(std::ranges::end)&;
-using RangeCEndT = decltype(std::ranges::cend)&;
+using RangeEndT = decltype(std::ranges::end);
+using RangeCEndT = decltype(std::ranges::cend);
 
 static int globalBuff[8];
 
@@ -47,6 +48,30 @@ static_assert(!std::is_invocable_v<RangeEndT,  EndMember &&>);
 static_assert( std::is_invocable_v<RangeCEndT, EndMember &>);
 static_assert( std::is_invocable_v<RangeCEndT, EndMember const&>);
 
+constexpr bool testReturnTypes() {
+  {
+    int *x[2];
+    ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), int**);
+    ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), int* const*);
+  }
+  {
+    int x[2][2];
+    ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), int(*)[2]);
+    ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), const int(*)[2]);
+  }
+  {
+    struct Different {
+      char *begin();
+      sentinel_wrapper<char*>& end();
+      short *begin() const;
+      sentinel_wrapper<short*>& end() const;
+    } x;
+    ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), sentinel_wrapper<char*>);
+    ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), sentinel_wrapper<short*>);
+  }
+  return true;
+}
+
 constexpr bool testArray() {
   int a[2];
   assert(std::ranges::end(a) == a + 2);
@@ -139,9 +164,11 @@ constexpr bool testEndMember() {
 
   NonConstEndMember b;
   assert(std::ranges::end(b) == &b.x);
+  static_assert(!std::is_invocable_v<RangeCEndT, decltype((b))>);
 
   EnabledBorrowingEndMember c;
   assert(std::ranges::end(std::move(c)) == &globalBuff[0]);
+  assert(std::ranges::cend(std::move(c)) == &globalBuff[0]);
 
   EndMemberFunction d;
   assert(std::ranges::end(d) == &d.x);
@@ -246,7 +273,9 @@ struct BeginMemberEndFunction {
 constexpr bool testEndFunction() {
   const EndFunction a{};
   assert(std::ranges::end(a) == &a.x);
+  assert(std::ranges::cend(a) == &a.x);
   EndFunction aa{};
+  static_assert(!std::is_invocable_v<RangeEndT, decltype((aa))>);
   assert(std::ranges::cend(aa) == &aa.x);
 
   EndFunctionByValue b;
@@ -255,25 +284,34 @@ constexpr bool testEndFunction() {
 
   EndFunctionEnabledBorrowing c;
   assert(std::ranges::end(std::move(c)) == &globalBuff[2]);
+  assert(std::ranges::cend(std::move(c)) == &globalBuff[2]);
 
   const EndFunctionReturnsEmptyPtr d{};
   assert(std::ranges::end(d) == &d.x);
+  assert(std::ranges::cend(d) == &d.x);
   EndFunctionReturnsEmptyPtr dd{};
+  static_assert(!std::is_invocable_v<RangeEndT, decltype((dd))>);
   assert(std::ranges::cend(dd) == &dd.x);
 
   const EndFunctionWithDataMember e{};
   assert(std::ranges::end(e) == &e.x);
+  assert(std::ranges::cend(e) == &e.x);
   EndFunctionWithDataMember ee{};
+  static_assert(!std::is_invocable_v<RangeEndT, decltype((ee))>);
   assert(std::ranges::cend(ee) == &ee.x);
 
   const EndFunctionWithPrivateEndMember f{};
   assert(std::ranges::end(f) == &f.y);
+  assert(std::ranges::cend(f) == &f.y);
   EndFunctionWithPrivateEndMember ff{};
+  static_assert(!std::is_invocable_v<RangeEndT, decltype((ff))>);
   assert(std::ranges::cend(ff) == &ff.y);
 
   const BeginMemberEndFunction g{};
   assert(std::ranges::end(g) == &g.x);
+  assert(std::ranges::cend(g) == &g.x);
   BeginMemberEndFunction gg{};
+  static_assert(!std::is_invocable_v<RangeEndT, decltype((gg))>);
   assert(std::ranges::cend(gg) == &gg.x);
 
   return true;
@@ -313,6 +351,8 @@ static_assert(noexcept(std::ranges::end(erar)));
 static_assert(noexcept(std::ranges::cend(erar)));
 
 int main(int, char**) {
+  static_assert(testReturnTypes());
+
   testArray();
   static_assert(testArray());
 


        


More information about the libcxx-commits mailing list