libstdc++: P1970R2 Consistency for size() functions: Add ranges::ssize
authorJonathan Wakely <jwakely@redhat.com>
Mon, 17 Feb 2020 17:58:09 +0000 (17:58 +0000)
committerJonathan Wakely <jwakely@redhat.com>
Mon, 17 Feb 2020 18:15:41 +0000 (18:15 +0000)
This defines ranges::ssize as approved in Prague. It's unclear what is
supposed to happen for types for which range_difference_t is not a valid
type. I've assumed they are not meant to be usable with ranges::ssize,
despite being usable with ranges::size.

* include/bits/range_access.h (_SSize, ssize): Define for C++20.
* testsuite/std/ranges/access/ssize.cc: New test.

libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/range_access.h
libstdc++-v3/testsuite/std/ranges/access/ssize.cc [new file with mode: 0644]

index 999c35f..547337d 100644 (file)
@@ -1,5 +1,9 @@
 2020-02-17  Jonathan Wakely  <jwakely@redhat.com>
 
+       P1970R2 Consistency for size() functions: Add ranges::ssize
+       * include/bits/range_access.h (_SSize, ssize): Define for C++20.
+       * testsuite/std/ranges/access/ssize.cc: New test.
+
        P1956R1 On the names of low-level bit manipulation functions
        * include/bits/hashtable_policy.h: Update comment.
        * include/std/bit (__ispow2, __ceil2, __floor2, __log2p1): Rename.
index 8b546a5..8bac0ef 100644 (file)
@@ -35,6 +35,7 @@
 #if __cplusplus >= 201103L
 #include <initializer_list>
 #include <bits/iterator_concepts.h>
+#include <bits/int_limits.h>
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -723,6 +724,32 @@ namespace ranges
        }
     };
 
+    struct _SSize
+    {
+      template<typename _Tp>
+       requires requires (_Tp&& __e)
+         {
+           _Begin{}(std::forward<_Tp>(__e));
+           _Size{}(std::forward<_Tp>(__e));
+         }
+       constexpr auto
+       operator()(_Tp&& __e) const
+       noexcept(noexcept(_Size{}(std::forward<_Tp>(__e))))
+       {
+         using __iter_type = decltype(_Begin{}(std::forward<_Tp>(__e)));
+         using __diff_type = iter_difference_t<__iter_type>;
+         using std::__detail::__int_limits;
+         auto __size = _Size{}(std::forward<_Tp>(__e));
+         if constexpr (integral<__diff_type>)
+           {
+             if constexpr (__int_limits<__diff_type>::digits
+                           < __int_limits<ptrdiff_t>::digits)
+               return static_cast<ptrdiff_t>(__size);
+           }
+         return static_cast<__diff_type>(__size);
+       }
+    };
+
     template<typename _Tp>
       concept __member_empty = requires(_Tp&& __t)
        { bool(std::forward<_Tp>(__t).empty()); };
@@ -834,6 +861,7 @@ namespace ranges
     inline constexpr __cust_access::_CRBegin crbegin{};
     inline constexpr __cust_access::_CREnd crend{};
     inline constexpr __cust_access::_Size size{};
+    inline constexpr __cust_access::_SSize ssize{};
     inline constexpr __cust_access::_Empty empty{};
     inline constexpr __cust_access::_Data data{};
     inline constexpr __cust_access::_CData cdata{};
diff --git a/libstdc++-v3/testsuite/std/ranges/access/ssize.cc b/libstdc++-v3/testsuite/std/ranges/access/ssize.cc
new file mode 100644 (file)
index 0000000..5aa05be
--- /dev/null
@@ -0,0 +1,98 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using std::ptrdiff_t;
+
+void
+test01()
+{
+  constexpr int a[10] = { };
+  static_assert( std::same_as<decltype(std::ranges::ssize(a)), ptrdiff_t> );
+  static_assert( std::ranges::ssize(a) == 10 );
+  static_assert( noexcept(std::ranges::ssize(a)) );
+
+  int a2[2];
+  static_assert( std::same_as<decltype(std::ranges::ssize(a2)), ptrdiff_t> );
+  VERIFY( std::ranges::ssize(a2) == 2);
+  static_assert( noexcept(std::ranges::ssize(a2)) );
+
+  struct Incomplete;
+  using A = Incomplete[2]; // bounded array of incomplete type
+  extern A& f();
+  static_assert( std::same_as<decltype(std::ranges::ssize(f())), ptrdiff_t> );
+}
+
+void
+test02()
+{
+  int a[3] = { };
+  __gnu_test::test_sized_range<int, __gnu_test::input_iterator_wrapper> ri(a);
+  VERIFY( std::ranges::ssize(ri) == 3 );
+  static_assert( noexcept(std::ranges::ssize(ri)) );
+}
+
+void
+test04()
+{
+  int a[] = { 0, 1 };
+  __gnu_test::test_range<int, __gnu_test::random_access_iterator_wrapper> r(a);
+  VERIFY( std::ranges::ssize(r) == std::ranges::end(r) - std::ranges::begin(r) );
+}
+
+struct R5
+{
+  int size() const noexcept { return 0; }
+  R5* begin() { return this; }
+  R5* end() { return this + 1; }
+};
+
+template<>
+constexpr bool std::ranges::disable_sized_range<R5> = true;
+
+void
+test05()
+{
+  R5 r;
+  VERIFY( std::ranges::ssize(r) == 1 );
+}
+
+void
+test06()
+{
+  auto i = std::views::iota(1ull, 5);
+  auto s = std::ranges::ssize(i);
+  using R = std::ranges::range_difference_t<decltype(i)>;
+  static_assert( std::same_as<decltype(s), R> );
+  VERIFY( s == 4 );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test04();
+  test05();
+  test06();
+}