[PATCH] libstdc++: Support range adaptors with defaultable arguments
Patrick Palka
ppalka@redhat.com
Thu May 20 16:07:41 GMT 2021
This adds support for defining range adaptors with defaultable arguments.
No such range adaptors have yet been standardized, but range-v3 has a
couple, e.g. 'unique' and 'sample' (which are approximately implemented
in the added testcase), and it would be good to preemptively support
such adaptors.
In order to make 'unique | unique' (where 'unique' is an adaptor that
takes a single defaultable extra argument) unambiguously mean
composition instead of the partial application 'unique(unique)', we need
to additionally constrain the first operand in the first overload of
_RangeAdaptorClosure::operator| as per [range.adaptor.object]/1, which
says R | C is equivalent to C(R) only if R models viewable_range.
However, for our purposes checking range instead of viewable_range
suffices and is cheaper to check.
Tested on x86_64-pc-linux-gnu, does this look OK for trunk/11? Existing
adaptors aren't affected by this change.
libstdc++-v3/ChangeLog:
* include/std/ranges (__adaptor_partial_app_arity_ok): Define.
(__adaptor_partial_app_viable): Use it.
(_RangeAdaptorClosure::operator|): In the first overload, swap
order of template parameters _Self and _Range. Add a
range<_Range> constraint to this overload.
(_RangeAdaptor): Document that _S_arity can also be defined
as a pair consisting of the minimum and maximum arity.
* testsuite/std/ranges/adaptors/detail/user_defined.cc: New test.
---
libstdc++-v3/include/std/ranges | 27 +++++--
.../ranges/adaptors/detail/user_defined.cc | 81 +++++++++++++++++++
2 files changed, 102 insertions(+), 6 deletions(-)
create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/detail/user_defined.cc
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 48100e9d7f2..8f691ee41f6 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -742,12 +742,25 @@ namespace views::__adaptor
concept __adaptor_invocable
= requires { std::declval<_Adaptor>()(declval<_Args>()...); };
+ template<typename _Adaptor>
+ constexpr bool
+ __adaptor_partial_app_arity_ok(int __nargs)
+ {
+ if constexpr (integral<decltype(_Adaptor::_S_arity)>)
+ return 1 + __nargs == _Adaptor::_S_arity;
+ else
+ {
+ auto [__min, __max] = _Adaptor::_S_arity;
+ return 1 + __nargs >= __min && 1 + __nargs <= __max;
+ }
+ }
+
// True if the range adaptor non-closure _Adaptor can be partially applied
// with _Args.
template<typename _Adaptor, typename... _Args>
- concept __adaptor_partial_app_viable = (_Adaptor::_S_arity > 1)
- && (sizeof...(_Args) == _Adaptor::_S_arity - 1)
- && (constructible_from<decay_t<_Args>, _Args> && ...);
+ concept __adaptor_partial_app_viable
+ = __adaptor_partial_app_arity_ok<_Adaptor>(sizeof...(_Args))
+ && (constructible_from<decay_t<_Args>, _Args> && ...);
template<typename _Adaptor, typename... _Args>
struct _Partial;
@@ -759,7 +772,7 @@ namespace views::__adaptor
struct _RangeAdaptorClosure
{
// range | adaptor is equivalent to adaptor(range).
- template<typename _Self, typename _Range>
+ template<range _Range, typename _Self>
requires derived_from<remove_cvref_t<_Self>, _RangeAdaptorClosure>
&& __adaptor_invocable<_Self, _Range>
friend constexpr auto
@@ -778,8 +791,10 @@ namespace views::__adaptor
// The base class of every range adaptor non-closure.
//
- // The static data member _Derived::_S_arity must contain the total number of
- // arguments that the adaptor takes, and the class _Derived must introduce
+ // The static data member _Derived::_S_arity must either be an integer
+ // denoting the total arity of the adaptor, or a tuple consisting of the
+ // minimum and maximum arity of the adaptor (if e.g. the adaptor has
+ // defaultable arguments). The class _Derived must also introduce
// _RangeAdaptor::operator() into the class scope via a using-declaration.
template<typename _Derived>
struct _RangeAdaptor
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/detail/user_defined.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/detail/user_defined.cc
new file mode 100644
index 00000000000..94dabb50db8
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/detail/user_defined.cc
@@ -0,0 +1,81 @@
+// Copyright (C) 2021 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 compile { target c++2a } }
+
+#include <ranges>
+
+using std::views::__adaptor::_RangeAdaptor;
+using std::views::__adaptor::_RangeAdaptorClosure;
+
+// An example of a range adaptor that accepts one defaultable extra argument.
+struct _Unique : _RangeAdaptor<_Unique>, _RangeAdaptorClosure
+{
+ template<std::ranges::viewable_range _Range, typename _Pred = std::ranges::equal_to>
+ std::ranges::empty_view<int>
+ operator()(_Range&&, _Pred = {}) const;
+
+ using _RangeAdaptor<_Unique>::operator();
+ static constexpr std::pair<int, int> _S_arity = {1,2};
+};
+
+inline constexpr _Unique unique;
+
+void
+test01()
+{
+ extern int r[42];
+ auto p = std::ranges::equal_to{};
+ r | unique;
+ r | (unique | unique);
+ r | (unique(p) | unique(p));
+ r | (unique(p) | unique);
+ r | (unique | unique(p));
+ unique(r, p);
+ unique(p)(r);
+}
+
+struct default_rng { };
+
+// An example of a range adaptor that accepts two extra arguments, one of which
+// is defaultable.
+struct _Sample : _RangeAdaptor<_Sample>
+{
+ template<std::ranges::viewable_range _Range, typename _Rng = default_rng>
+ std::ranges::empty_view<int>
+ operator()(_Range&&, int, _Rng = {}) const;
+
+ using _RangeAdaptor<_Sample>::operator();
+ static constexpr std::pair<int, int> _S_arity = {2,3};
+};
+
+inline constexpr _Sample sample;
+
+void
+test02()
+{
+ extern int r[42];
+ sample(r, 5);
+ sample(r, 5, default_rng{});
+ sample(5)(r);
+ sample(5, default_rng{})(r);
+ r | sample(5);
+ r | sample(5, default_rng{});
+ r | (sample(5) | sample(5));
+ r | (sample(5, default_rng{}) | sample(5, default_rng{}));
+}
--
2.32.0.rc0
More information about the Libstdc++
mailing list