diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h --- a/libcxx/include/__ranges/common_view.h +++ b/libcxx/include/__ranges/common_view.h @@ -16,8 +16,10 @@ #include <__ranges/all.h> #include <__ranges/concepts.h> #include <__ranges/enable_borrowed_range.h> +#include <__ranges/range_adaptor.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> +#include <__utility/forward.h> #include <__utility/move.h> #include #include @@ -100,6 +102,30 @@ template inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_View>; +namespace views { +namespace __common { + struct __fn : __range_adaptor_closure<__fn> { + template + requires common_range<_Range> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range) const + noexcept(noexcept(views::all(_VSTD::forward<_Range>(__range)))) + -> decltype( views::all(_VSTD::forward<_Range>(__range))) + { return views::all(_VSTD::forward<_Range>(__range)); } + + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range) const + noexcept(noexcept(common_view{_VSTD::forward<_Range>(__range)})) + -> decltype( common_view{_VSTD::forward<_Range>(__range)}) + { return common_view{_VSTD::forward<_Range>(__range)}; } + }; +} + +inline namespace __cpo { + inline constexpr auto common = __common::__fn{}; +} +} // namespace views } // namespace ranges #endif // !defined(_LIBCPP_HAS_NO_RANGES) diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common.view/adaptor.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common.view/adaptor.nodiscard.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common.view/adaptor.nodiscard.verify.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Test the libc++ extension that std::views::common is marked as [[nodiscard]] to avoid +// the potential for user mistakenly thinking they're calling an algorithm. + +#include + +void test() { + int range[] = {1, 2, 3}; + + std::views::common(range); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + range | std::views::common; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/adaptor.pass.cpp @@ -0,0 +1,116 @@ +//===----------------------------------------------------------------------===// +// +// 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::common + +#include + +#include +#include +#include +#include + +#include "test_iterators.h" +#include "types.h" + +template +concept CanBePiped = requires (View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; +}; + +constexpr bool test() { + int buf[] = {1, 2, 3}; + + // views::common(r) is equivalent to views::all(r) if r is a common_range + { + { + CommonView view(buf, buf + 3); + std::same_as auto result = std::views::common(view); + assert(result.begin_ == buf); + assert(result.end_ == buf + 3); + } + { + using NotAView = std::array; + NotAView arr = {1, 2, 3}; + std::same_as> auto result = std::views::common(arr); + assert(result.begin() == arr.begin()); + assert(result.end() == arr.end()); + } + } + + // Otherwise, views::common(r) is equivalent to ranges::common_view{r} + { + NonCommonView view(buf, buf + 3); + std::same_as> auto result = std::views::common(view); + assert(result.base().begin_ == buf); + assert(result.base().end_ == buf + 3); + } + + // Test that std::views::common is a range adaptor + { + using SomeView = NonCommonView; + + // Test `v | views::common` + { + SomeView view(buf, buf + 3); + std::same_as> auto result = view | std::views::common; + assert(result.base().begin_ == buf); + assert(result.base().end_ == buf + 3); + } + + // Test `adaptor | views::common` + { + SomeView view(buf, buf + 3); + auto f = [](int i) { return i; }; + auto const partial = std::views::transform(f) | std::views::common; + using Result = std::ranges::common_view>; + std::same_as auto result = partial(view); + assert(result.base().base().begin_ == buf); + assert(result.base().base().end_ == buf + 3); + } + + // Test `views::common | adaptor` + { + SomeView view(buf, buf + 3); + auto f = [](int i) { return i; }; + auto const partial = std::views::common | std::views::transform(f); + using Result = std::ranges::transform_view, decltype(f)>; + std::same_as auto result = partial(view); + assert(result.base().base().begin_ == buf); + assert(result.base().base().end_ == buf + 3); + } + + // Check SFINAE friendliness + { + struct NotAView { }; + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert( CanBePiped); + static_assert( CanBePiped); + static_assert(!CanBePiped); + static_assert(!CanBePiped); + } + } + + { + static_assert(std::same_as); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h --- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h +++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h @@ -9,6 +9,8 @@ #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H +#include + #include "test_iterators.h" struct DefaultConstructibleView : std::ranges::view_base { @@ -104,4 +106,28 @@ return iter.base() - sent.base().base(); } +struct CommonView : std::ranges::view_base { + int* begin_; + int* end_; + constexpr explicit CommonView(int* b, int* e) : begin_(b), end_(e) { } + friend constexpr int* begin(CommonView& view) { return view.begin_; } + friend constexpr int* begin(CommonView const& view) { return view.begin_; } + friend constexpr int* end(CommonView& view) { return view.end_; } + friend constexpr int* end(CommonView const& view) { return view.end_; } +}; +static_assert(std::ranges::range); +static_assert(std::ranges::common_range); + +struct NonCommonView : std::ranges::view_base { + int* begin_; + int* end_; + constexpr explicit NonCommonView(int* b, int* e) : begin_(b), end_(e) { } + friend constexpr int* begin(NonCommonView& view) { return view.begin_; } + friend constexpr int* begin(NonCommonView const& view) { return view.begin_; } + friend constexpr sentinel_wrapper end(NonCommonView& view) { return sentinel_wrapper(view.end_); } + friend constexpr sentinel_wrapper end(NonCommonView const& view) { return sentinel_wrapper(view.end_); } +}; +static_assert( std::ranges::range); +static_assert(!std::ranges::common_range); + #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H