[libcxx-commits] [libcxx] ee44dd8 - [libc++] Implement the underlying mechanism for range adaptors
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Aug 26 11:07:37 PDT 2021
Author: Louis Dionne
Date: 2021-08-26T14:07:21-04:00
New Revision: ee44dd8062a26541808fc0d3fd5c6703e19f6016
URL: https://github.com/llvm/llvm-project/commit/ee44dd8062a26541808fc0d3fd5c6703e19f6016
DIFF: https://github.com/llvm/llvm-project/commit/ee44dd8062a26541808fc0d3fd5c6703e19f6016.diff
LOG: [libc++] Implement the underlying mechanism for range adaptors
This patch implements the underlying mechanism for range adaptors. It
does so based on http://wg21.link/p2387, even though that paper hasn't
been adopted yet. In the future, if p2387 is adopted, it would suffice
to rename `__bind_back` to `std::bind_back` and `__range_adaptor_closure`
to `std::range_adaptor_closure` to implement that paper by the spec.
Differential Revision: https://reviews.llvm.org/D107098
Added:
libcxx/include/__ranges/range_adaptor.h
libcxx/test/libcxx/diagnostics/detail.headers/ranges/range_adaptor.module.verify.cpp
libcxx/test/std/ranges/range.adaptors/range.all/all.nodiscard.verify.cpp
libcxx/test/std/ranges/range.adaptors/range.transform/adaptor.nodiscard.verify.cpp
libcxx/test/std/ranges/range.adaptors/range.transform/adaptor.pass.cpp
Modified:
libcxx/include/CMakeLists.txt
libcxx/include/__ranges/all.h
libcxx/include/__ranges/transform_view.h
libcxx/include/module.modulemap
libcxx/test/std/ranges/range.adaptors/range.all/all.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.transform/types.h
Removed:
################################################################################
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index a757449f79734..3189651995c0a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -227,6 +227,7 @@ set(files
__ranges/iota_view.h
__ranges/join_view.h
__ranges/non_propagating_cache.h
+ __ranges/range_adaptor.h
__ranges/ref_view.h
__ranges/reverse_view.h
__ranges/take_view.h
diff --git a/libcxx/include/__ranges/all.h b/libcxx/include/__ranges/all.h
index f44beaa272722..144ecdfb5cf59 100644
--- a/libcxx/include/__ranges/all.h
+++ b/libcxx/include/__ranges/all.h
@@ -14,6 +14,7 @@
#include <__iterator/iterator_traits.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
+#include <__ranges/range_adaptor.h>
#include <__ranges/ref_view.h>
#include <__ranges/subrange.h>
#include <__utility/__decay_copy.h>
@@ -35,10 +36,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace ranges::views {
namespace __all {
- struct __fn {
+ struct __fn : __range_adaptor_closure<__fn> {
template<class _Tp>
requires ranges::view<decay_t<_Tp>>
- _LIBCPP_HIDE_FROM_ABI
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
constexpr auto operator()(_Tp&& __t) const
noexcept(noexcept(_VSTD::__decay_copy(_VSTD::forward<_Tp>(__t))))
{
@@ -48,7 +49,7 @@ namespace __all {
template<class _Tp>
requires (!ranges::view<decay_t<_Tp>>) &&
requires (_Tp&& __t) { ranges::ref_view{_VSTD::forward<_Tp>(__t)}; }
- _LIBCPP_HIDE_FROM_ABI
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
constexpr auto operator()(_Tp&& __t) const
noexcept(noexcept(ranges::ref_view{_VSTD::forward<_Tp>(__t)}))
{
@@ -59,7 +60,7 @@ namespace __all {
requires (!ranges::view<decay_t<_Tp>> &&
!requires (_Tp&& __t) { ranges::ref_view{_VSTD::forward<_Tp>(__t)}; } &&
requires (_Tp&& __t) { ranges::subrange{_VSTD::forward<_Tp>(__t)}; })
- _LIBCPP_HIDE_FROM_ABI
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
constexpr auto operator()(_Tp&& __t) const
noexcept(noexcept(ranges::subrange{_VSTD::forward<_Tp>(__t)}))
{
diff --git a/libcxx/include/__ranges/range_adaptor.h b/libcxx/include/__ranges/range_adaptor.h
new file mode 100644
index 0000000000000..74aea3187e892
--- /dev/null
+++ b/libcxx/include/__ranges/range_adaptor.h
@@ -0,0 +1,73 @@
+// -*- 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___RANGES_RANGE_ADAPTOR_H
+#define _LIBCPP___RANGES_RANGE_ADAPTOR_H
+
+#include <__config>
+#include <__functional/compose.h>
+#include <__functional/invoke.h>
+#include <__ranges/concepts.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+#include <concepts>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if !defined(_LIBCPP_HAS_NO_RANGES)
+
+// CRTP base that one can derive from in order to be considered a range adaptor closure
+// by the library. When deriving from this class, a pipe operator will be provided to
+// make the following hold:
+// - `x | f` is equivalent to `f(x)`
+// - `f1 | f2` is an adaptor closure `g` such that `g(x)` is equivalent to `f2(f1(x))`
+template <class _Tp>
+struct __range_adaptor_closure;
+
+// Type that wraps an arbitrary function object and makes it into a range adaptor closure,
+// i.e. something that can be called via the `x | f` notation.
+template <class _Fn>
+struct __range_adaptor_closure_t : _Fn, __range_adaptor_closure<__range_adaptor_closure_t<_Fn>> {
+ constexpr explicit __range_adaptor_closure_t(_Fn&& __f) : _Fn(_VSTD::move(__f)) { }
+};
+
+template <class _Tp>
+concept _RangeAdaptorClosure = derived_from<remove_cvref_t<_Tp>, __range_adaptor_closure<remove_cvref_t<_Tp>>>;
+
+template <class _Tp>
+struct __range_adaptor_closure {
+ template <ranges::viewable_range _View, _RangeAdaptorClosure _Closure>
+ requires same_as<_Tp, remove_cvref_t<_Closure>> &&
+ invocable<_Closure, _View>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ friend constexpr decltype(auto) operator|(_View&& __view, _Closure&& __closure)
+ noexcept(is_nothrow_invocable_v<_Closure, _View>)
+ { return _VSTD::invoke(_VSTD::forward<_Closure>(__closure), _VSTD::forward<_View>(__view)); }
+
+ template <_RangeAdaptorClosure _Closure, _RangeAdaptorClosure _OtherClosure>
+ requires same_as<_Tp, remove_cvref_t<_Closure>> &&
+ constructible_from<decay_t<_Closure>, _Closure> &&
+ constructible_from<decay_t<_OtherClosure>, _OtherClosure>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ friend constexpr auto operator|(_Closure&& __c1, _OtherClosure&& __c2)
+ noexcept(is_nothrow_constructible_v<decay_t<_Closure>, _Closure> &&
+ is_nothrow_constructible_v<decay_t<_OtherClosure>, _OtherClosure>)
+ { return __range_adaptor_closure_t(_VSTD::__compose(_VSTD::forward<_OtherClosure>(__c2), _VSTD::forward<_Closure>(__c1))); }
+};
+
+#endif // !defined(_LIBCPP_HAS_NO_RANGES)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_RANGE_ADAPTOR_H
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 9aee8fce49584..b892e5a045cac 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -10,6 +10,7 @@
#define _LIBCPP___RANGES_TRANSFORM_VIEW_H
#include <__config>
+#include <__functional/bind_back.h>
#include <__functional/invoke.h>
#include <__iterator/concepts.h>
#include <__iterator/iter_swap.h>
@@ -20,8 +21,10 @@
#include <__ranges/concepts.h>
#include <__ranges/copyable_box.h>
#include <__ranges/empty.h>
+#include <__ranges/range_adaptor.h>
#include <__ranges/size.h>
#include <__ranges/view_interface.h>
+#include <__utility/forward.h>
#include <__utility/in_place.h>
#include <__utility/move.h>
#include <concepts>
@@ -401,6 +404,30 @@ class transform_view<_View, _Fn>::__sentinel {
}
};
+namespace views {
+namespace __transform {
+ struct __fn {
+ template<class _Range, class _Fn>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Range&& __range, _Fn&& __f) const
+ noexcept(noexcept(transform_view(_VSTD::forward<_Range>(__range), _VSTD::forward<_Fn>(__f))))
+ -> decltype( transform_view(_VSTD::forward<_Range>(__range), _VSTD::forward<_Fn>(__f)))
+ { return transform_view(_VSTD::forward<_Range>(__range), _VSTD::forward<_Fn>(__f)); }
+
+ template<class _Fn>
+ requires constructible_from<decay_t<_Fn>, _Fn>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Fn&& __f) const
+ noexcept(is_nothrow_constructible_v<decay_t<_Fn>, _Fn>)
+ { return __range_adaptor_closure_t(_VSTD::__bind_back(*this, _VSTD::forward<_Fn>(__f))); }
+ };
+}
+
+inline namespace __cpo {
+ inline constexpr auto transform = __transform::__fn{};
+}
+} // namespace views
+
} // namespace ranges
#endif // !defined(_LIBCPP_HAS_NO_RANGES)
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 98ba8d38c71a3..b6a39a0702d52 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -647,7 +647,11 @@ module std [system] {
module __ranges {
module access { private header "__ranges/access.h" }
- module all { private header "__ranges/all.h" }
+ module all {
+ private header "__ranges/all.h"
+ export functional.__functional.compose
+ export functional.__functional.perfect_forward
+ }
module common_view { private header "__ranges/common_view.h" }
module concepts { private header "__ranges/concepts.h" }
module copyable_box { private header "__ranges/copyable_box.h" }
@@ -662,13 +666,18 @@ module std [system] {
module iota_view { private header "__ranges/iota_view.h" }
module join_view { private header "__ranges/join_view.h" }
module non_propagating_cache { private header "__ranges/non_propagating_cache.h" }
+ module range_adaptor { private header "__ranges/range_adaptor.h" }
module ref_view { private header "__ranges/ref_view.h" }
module reverse_view { private header "__ranges/reverse_view.h" }
module size { private header "__ranges/size.h" }
module single_view { private header "__ranges/single_view.h" }
module subrange { private header "__ranges/subrange.h" }
module take_view { private header "__ranges/take_view.h" }
- module transform_view { private header "__ranges/transform_view.h" }
+ module transform_view {
+ private header "__ranges/transform_view.h"
+ export functional.__functional.bind_back
+ export functional.__functional.perfect_forward
+ }
module view_interface { private header "__ranges/view_interface.h" }
}
}
diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/ranges/range_adaptor.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/range_adaptor.module.verify.cpp
new file mode 100644
index 0000000000000..b1bdc0d1e6874
--- /dev/null
+++ b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/range_adaptor.module.verify.cpp
@@ -0,0 +1,16 @@
+// -*- 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: modules-build
+
+// WARNING: This test was generated by 'generate_private_header_tests.py'
+// and should not be edited manually.
+
+// expected-error@*:* {{use of private header from outside its module: '__ranges/range_adaptor.h'}}
+#include <__ranges/range_adaptor.h>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/all.nodiscard.verify.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/all.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..f3981f95e685d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/all.nodiscard.verify.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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-no-concepts
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+// REQUIRES: libc++
+
+// Test the libc++ extension that std::views::all is marked as [[nodiscard]].
+
+#include <ranges>
+
+void test() {
+ int range[] = {1, 2, 3};
+ auto f = [](int i) { return i; };
+
+ std::views::all(range); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ range | std::views::all; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::views::transform(f) | std::views::all; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::views::all | std::views::transform(f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/all.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/all.pass.cpp
index cef873465eb65..508326dbc225d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.all/all.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.all/all.pass.cpp
@@ -15,6 +15,10 @@
#include <ranges>
#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
#include "test_macros.h"
#include "test_iterators.h"
@@ -83,6 +87,11 @@ struct RandomAccessRange {
template<>
inline constexpr bool std::ranges::enable_borrowed_range<RandomAccessRange> = true;
+template <class View, class T>
+concept CanBePiped = requires (View&& view, T&& t) {
+ { std::forward<View>(view) | std::forward<T>(t) };
+};
+
constexpr bool test() {
{
ASSERT_SAME_TYPE(decltype(std::views::all(View<true>())), View<true>);
@@ -142,6 +151,49 @@ constexpr bool test() {
assert(std::ranges::end(subrange) == std::ranges::begin(subrange) + 8);
}
+ // Check SFINAE friendliness of the call operator
+ {
+ static_assert(!std::is_invocable_v<decltype(std::views::all)>);
+ static_assert(!std::is_invocable_v<decltype(std::views::all), RandomAccessRange, RandomAccessRange>);
+ }
+
+ // Test that std::views::all is a range adaptor
+ {
+ // Test `v | views::all`
+ {
+ Range range(0);
+ auto result = range | std::views::all;
+ ASSERT_SAME_TYPE(decltype(result), std::ranges::ref_view<Range>);
+ assert(&result.base() == &range);
+ }
+
+ // Test `adaptor | views::all`
+ {
+ Range range(0);
+ auto f = [](int i) { return i; };
+ auto const partial = std::views::transform(f) | std::views::all;
+ using Result = std::ranges::transform_view<std::ranges::ref_view<Range>, decltype(f)>;
+ std::same_as<Result> auto result = partial(range);
+ assert(&result.base().base() == &range);
+ }
+
+ // Test `views::all | adaptor`
+ {
+ Range range(0);
+ auto f = [](int i) { return i; };
+ auto const partial = std::views::all | std::views::transform(f);
+ using Result = std::ranges::transform_view<std::ranges::ref_view<Range>, decltype(f)>;
+ std::same_as<Result> auto result = partial(range);
+ assert(&result.base().base() == &range);
+ }
+
+ {
+ struct NotAView { };
+ static_assert( CanBePiped<Range&, decltype(std::views::all)>);
+ static_assert(!CanBePiped<NotAView, decltype(std::views::all)>);
+ }
+ }
+
{
static_assert(std::same_as<decltype(std::views::all), decltype(std::ranges::views::all)>);
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/adaptor.nodiscard.verify.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/adaptor.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..05eb468559b07
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/adaptor.nodiscard.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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-no-concepts
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+// REQUIRES: libc++
+
+// Test the libc++ extension that std::views::transform is marked as [[nodiscard]] to avoid
+// the potential for user mistakenly thinking they're calling an algorithm.
+
+#include <ranges>
+
+void test() {
+ int range[] = {1, 2, 3};
+ auto f = [](int i) { return i; };
+
+ std::views::transform(f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::views::transform(range, f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ range | std::views::transform(f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::views::transform(f) | std::views::transform(f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/adaptor.pass.cpp
new file mode 100644
index 0000000000000..7c3b27bb5739f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/adaptor.pass.cpp
@@ -0,0 +1,151 @@
+//===----------------------------------------------------------------------===//
+//
+// 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-no-concepts
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::views::transform
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.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 NonCopyableFunction {
+ NonCopyableFunction(NonCopyableFunction const&) = delete;
+ template <class T>
+ constexpr T operator()(T x) const { return x; }
+};
+
+constexpr bool test() {
+ int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+ // Test `views::transform(f)(v)`
+ {
+ {
+ using Result = std::ranges::transform_view<ContiguousView, PlusOne>;
+ std::same_as<Result> auto result = std::views::transform(PlusOne{})(ContiguousView{buff});
+ assert(result.begin().base() == buff);
+ assert(result[0] == 1);
+ assert(result[1] == 2);
+ assert(result[2] == 3);
+ }
+ {
+ auto const partial = std::views::transform(PlusOne{});
+ using Result = std::ranges::transform_view<ContiguousView, PlusOne>;
+ std::same_as<Result> auto result = partial(ContiguousView{buff});
+ assert(result.begin().base() == buff);
+ assert(result[0] == 1);
+ assert(result[1] == 2);
+ assert(result[2] == 3);
+ }
+ }
+
+ // Test `v | views::transform(f)`
+ {
+ {
+ using Result = std::ranges::transform_view<ContiguousView, PlusOne>;
+ std::same_as<Result> auto result = ContiguousView{buff} | std::views::transform(PlusOne{});
+ assert(result.begin().base() == buff);
+ assert(result[0] == 1);
+ assert(result[1] == 2);
+ assert(result[2] == 3);
+ }
+ {
+ auto const partial = std::views::transform(PlusOne{});
+ using Result = std::ranges::transform_view<ContiguousView, PlusOne>;
+ std::same_as<Result> auto result = ContiguousView{buff} | partial;
+ assert(result.begin().base() == buff);
+ assert(result[0] == 1);
+ assert(result[1] == 2);
+ assert(result[2] == 3);
+ }
+ }
+
+ // Test `views::transform(v, f)`
+ {
+ using Result = std::ranges::transform_view<ContiguousView, PlusOne>;
+ std::same_as<Result> auto result = std::views::transform(ContiguousView{buff}, PlusOne{});
+ assert(result.begin().base() == buff);
+ assert(result[0] == 1);
+ assert(result[1] == 2);
+ assert(result[2] == 3);
+ }
+
+ // Test that one can call std::views::transform with arbitrary stuff, as long as we
+ // don't try to actually complete the call by passing it a range.
+ //
+ // That makes no sense and we can't do anything with the result, but it's valid.
+ {
+ struct X { };
+ auto partial = std::views::transform(X{});
+ (void)partial;
+ }
+
+ // Test `adaptor | views::transform(f)`
+ {
+ {
+ using Result = std::ranges::transform_view<std::ranges::transform_view<ContiguousView, PlusOne>, TimesTwo>;
+ std::same_as<Result> auto result = ContiguousView{buff} | std::views::transform(PlusOne{}) | std::views::transform(TimesTwo{});
+ assert(result.begin().base().base() == buff);
+ assert(result[0] == 2);
+ assert(result[1] == 4);
+ assert(result[2] == 6);
+ }
+ {
+ auto const partial = std::views::transform(PlusOne{}) | std::views::transform(TimesTwo{});
+ using Result = std::ranges::transform_view<std::ranges::transform_view<ContiguousView, PlusOne>, TimesTwo>;
+ std::same_as<Result> auto result = ContiguousView{buff} | partial;
+ assert(result.begin().base().base() == buff);
+ assert(result[0] == 2);
+ assert(result[1] == 4);
+ assert(result[2] == 6);
+ }
+ }
+
+ // Test SFINAE friendliness
+ {
+ struct NotAView { };
+ struct NotInvocable { };
+
+ static_assert(!CanBePiped<ContiguousView, decltype(std::views::transform)>);
+ static_assert( CanBePiped<ContiguousView, decltype(std::views::transform(PlusOne{}))>);
+ static_assert(!CanBePiped<NotAView, decltype(std::views::transform(PlusOne{}))>);
+ static_assert(!CanBePiped<ContiguousView, decltype(std::views::transform(NotInvocable{}))>);
+
+ static_assert(!std::is_invocable_v<decltype(std::views::transform)>);
+ static_assert(!std::is_invocable_v<decltype(std::views::transform), PlusOne, ContiguousView>);
+ static_assert( std::is_invocable_v<decltype(std::views::transform), ContiguousView, PlusOne>);
+ static_assert(!std::is_invocable_v<decltype(std::views::transform), ContiguousView, PlusOne, PlusOne>);
+ static_assert(!std::is_invocable_v<decltype(std::views::transform), NonCopyableFunction>);
+ }
+
+ {
+ static_assert(std::is_same_v<decltype(std::ranges::views::transform), decltype(std::views::transform)>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index 159db9b4c97bf..1d535eda5db00 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -129,6 +129,10 @@ struct ThreeWayCompView : std::ranges::view_base {
constexpr ThreeWayCompIter end() const { return ThreeWayCompIter(globalBuff + 8); }
};
+struct TimesTwo {
+ constexpr int operator()(int x) const { return x * 2; }
+};
+
struct PlusOneMutable {
constexpr int operator()(int x) { return x + 1; }
};
More information about the libcxx-commits
mailing list