-
Notifications
You must be signed in to change notification settings - Fork 12.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[libc++][ranges] P2387R3: Pipe support for user-defined range adaptors (
#89148) This patch finalizes the std::ranges::range_adaptor_closure class template from https://wg21.link/P2387R3. // [range.adaptor.object], range adaptor objects template<class D> requires is_class_v<D> && same_as<D, remove_cv_t<D>> class range_adaptor_closure { }; The current implementation of __range_adaptor_closure was introduced in ee44dd8 and has served as the foundation for the range adaptors in libc++ for a while. This patch keeps its implementation, with the exception of the following changes: - __range_adaptor_closure now includes the missing constraints `is_class_v<D> && same_as<D, remove_cv_t<D>>` to restrict the type of class that can inherit from it. (https://eel.is/c++draft/ranges.syn) - The operator| of __range_adaptor_closure no longer requires its first argument to model viewable_range. (https://eel.is/c++draft/range.adaptor.object#1) - The _RangeAdaptorClosure concept is refined to exclude cases where T models range or where T has base classes of type range_adaptor_closure<U> for another type U. (https://eel.is/c++draft/range.adaptor.object#2)
- Loading branch information
Xiaoyang Liu
authored
Apr 23, 2024
1 parent
adb0126
commit c108653
Showing
7 changed files
with
186 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
Standard,Name,Assignee,CL,Status | ||
C++23,`ranges::to <https://wg21.link/P1206R7>`_,Konstantin Varlamov,`D142335 <https://reviews.llvm.org/D142335>`_,Complete | ||
C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,Unassigned,No patch yet,Not started | ||
C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,"Louis Dionne, Jakub Mazurkiewicz, and Xiaoyang Liu",Various,Complete | ||
C++23,`Formatting Ranges <https://wg21.link/P2286R8>`_,Mark de Wever,Various,Complete | ||
C++20,`Stashing stashing iterators for proper flattening <https://wg21.link/P2770R0>`_,Jakub Mazurkiewicz,Various,In progress |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
142 changes: 142 additions & 0 deletions
142
libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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, c++20 | ||
|
||
// std::ranges::range_adaptor_closure; | ||
|
||
#include <ranges> | ||
|
||
#include <algorithm> | ||
#include <vector> | ||
|
||
#include "test_range.h" | ||
|
||
template <class T> | ||
concept CanDeriveFromRangeAdaptorClosure = requires { typename std::ranges::range_adaptor_closure<T>; }; | ||
static_assert(!CanDeriveFromRangeAdaptorClosure<int>); | ||
|
||
struct Foo {}; | ||
static_assert(CanDeriveFromRangeAdaptorClosure<Foo>); | ||
static_assert(!CanDeriveFromRangeAdaptorClosure<Foo&>); | ||
static_assert(!CanDeriveFromRangeAdaptorClosure<const Foo>); | ||
static_assert(!CanDeriveFromRangeAdaptorClosure<volatile Foo>); | ||
static_assert(!CanDeriveFromRangeAdaptorClosure<const volatile Foo&&>); | ||
|
||
struct incomplete_t; | ||
static_assert(CanDeriveFromRangeAdaptorClosure<incomplete_t>); | ||
|
||
using range_t = std::vector<int>; | ||
|
||
template <class T> | ||
concept RangeAdaptorClosure = | ||
CanBePiped<range_t, T&> && CanBePiped<range_t, const T&> && CanBePiped<range_t, T&&> && | ||
CanBePiped<range_t, const T&&>; | ||
|
||
struct callable : std::ranges::range_adaptor_closure<callable> { | ||
static void operator()(const range_t&) {} | ||
}; | ||
static_assert(RangeAdaptorClosure<callable>); | ||
|
||
// `not_callable_1` doesn't have an `operator()` | ||
struct not_callable_1 : std::ranges::range_adaptor_closure<not_callable_1> {}; | ||
static_assert(!RangeAdaptorClosure<not_callable_1>); | ||
|
||
// `not_callable_2` doesn't have an `operator()` that accepts a `range` argument | ||
struct not_callable_2 : std::ranges::range_adaptor_closure<not_callable_2> { | ||
static void operator()() {} | ||
}; | ||
static_assert(!RangeAdaptorClosure<not_callable_2>); | ||
|
||
// `not_derived_from_1` doesn't derive from `std::ranges::range_adaptor_closure` | ||
struct not_derived_from_1 { | ||
static void operator()(const range_t&) {} | ||
}; | ||
static_assert(!RangeAdaptorClosure<not_derived_from_1>); | ||
|
||
// `not_derived_from_2` doesn't publicly derive from `std::ranges::range_adaptor_closure` | ||
struct not_derived_from_2 : private std::ranges::range_adaptor_closure<not_derived_from_2> { | ||
static void operator()(const range_t&) {} | ||
}; | ||
static_assert(!RangeAdaptorClosure<not_derived_from_2>); | ||
|
||
// `not_derived_from_3` doesn't derive from the correct specialization of `std::ranges::range_adaptor_closure` | ||
struct not_derived_from_3 : std::ranges::range_adaptor_closure<callable> { | ||
static void operator()(const range_t&) {} | ||
}; | ||
static_assert(!RangeAdaptorClosure<not_derived_from_3>); | ||
|
||
// `not_derived_from_4` doesn't derive from exactly one specialization of `std::ranges::range_adaptor_closure` | ||
struct not_derived_from_4 | ||
: std::ranges::range_adaptor_closure<not_derived_from_4>, | ||
std::ranges::range_adaptor_closure<callable> { | ||
static void operator()(const range_t&) {} | ||
}; | ||
static_assert(!RangeAdaptorClosure<not_derived_from_4>); | ||
|
||
// `is_range` models `range` | ||
struct is_range : std::ranges::range_adaptor_closure<is_range> { | ||
static void operator()(const range_t&) {} | ||
int* begin() const { return nullptr; } | ||
int* end() const { return nullptr; } | ||
}; | ||
static_assert(std::ranges::range<is_range> && std::ranges::range<const is_range>); | ||
static_assert(!RangeAdaptorClosure<is_range>); | ||
|
||
// user-defined range adaptor closure object | ||
struct negate_fn : std::ranges::range_adaptor_closure<negate_fn> { | ||
template <std::ranges::range Range> | ||
static constexpr decltype(auto) operator()(Range&& range) { | ||
return std::forward<Range>(range) | std::views::transform([](auto element) { return -element; }); | ||
} | ||
}; | ||
static_assert(RangeAdaptorClosure<negate_fn>); | ||
constexpr auto negate = negate_fn{}; | ||
|
||
// user-defined range adaptor closure object | ||
struct plus_1_fn : std::ranges::range_adaptor_closure<plus_1_fn> { | ||
template <std::ranges::range Range> | ||
static constexpr decltype(auto) operator()(Range&& range) { | ||
return std::forward<Range>(range) | std::views::transform([](auto element) { return element + 1; }); | ||
} | ||
}; | ||
static_assert(RangeAdaptorClosure<plus_1_fn>); | ||
constexpr auto plus_1 = plus_1_fn{}; | ||
|
||
constexpr bool test() { | ||
const std::vector<int> n{1, 2, 3, 4, 5}; | ||
const std::vector<int> n_negate{-1, -2, -3, -4, -5}; | ||
|
||
assert(std::ranges::equal(n | negate, n_negate)); | ||
assert(std::ranges::equal(negate(n), n_negate)); | ||
|
||
assert(std::ranges::equal(n | negate | negate, n)); | ||
assert(std::ranges::equal(n | (negate | negate), n)); | ||
assert(std::ranges::equal((n | negate) | negate, n)); | ||
assert(std::ranges::equal(negate(n) | negate, n)); | ||
assert(std::ranges::equal(negate(n | negate), n)); | ||
assert(std::ranges::equal((negate | negate)(n), n)); | ||
assert(std::ranges::equal(negate(negate(n)), n)); | ||
|
||
const std::vector<int> n_plus_1_negate{-2, -3, -4, -5, -6}; | ||
assert(std::ranges::equal(n | plus_1 | negate, n_plus_1_negate)); | ||
assert(std::ranges::equal( | ||
n | plus_1 | std::views::transform([](auto element) { return element; }) | negate, n_plus_1_negate)); | ||
|
||
const std::vector<int> n_negate_plus_1{0, -1, -2, -3, -4}; | ||
assert(std::ranges::equal(n | negate | plus_1, n_negate_plus_1)); | ||
assert(std::ranges::equal(n | std::views::reverse | negate | plus_1 | std::views::reverse, n_negate_plus_1)); | ||
return true; | ||
} | ||
|
||
int main(int, char**) { | ||
test(); | ||
static_assert(test()); | ||
|
||
return 0; | ||
} |