diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -201,6 +201,8 @@ __functional/unary_negate.h __functional/unwrap_ref.h __functional/weak_result_type.h + __fwd/span.h + __fwd/string_view.h __hash_table __iterator/access.h __iterator/advance.h diff --git a/libcxx/include/__fwd/span.h b/libcxx/include/__fwd/span.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__fwd/span.h @@ -0,0 +1,37 @@ +// -*- 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_FWD_SPAN_H +#define _LIBCPP_FWD_SPAN_H + +#include <__config> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +inline constexpr size_t dynamic_extent = numeric_limits::max(); +template class span; + +#endif + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP_FWD_SPAN_H diff --git a/libcxx/include/__fwd/string_view.h b/libcxx/include/__fwd/string_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__fwd/string_view.h @@ -0,0 +1,37 @@ +// -*- 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_FWD_STRING_VIEW_H +#define _LIBCPP_FWD_STRING_VIEW_H + +#include <__config> +#include // char_traits + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +template > +class _LIBCPP_TEMPLATE_VIS basic_string_view; + +typedef basic_string_view string_view; +#ifndef _LIBCPP_HAS_NO_CHAR8_T +typedef basic_string_view u8string_view; +#endif +typedef basic_string_view u16string_view; +typedef basic_string_view u32string_view; +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +typedef basic_string_view wstring_view; +#endif + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_FWD_STRING_VIEW_H diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h --- a/libcxx/include/__ranges/drop_view.h +++ b/libcxx/include/__ranges/drop_view.h @@ -11,16 +11,25 @@ #include <__config> #include <__debug> +#include <__functional/bind_back.h> +#include <__fwd/span.h> +#include <__fwd/string_view.h> #include <__iterator/concepts.h> #include <__iterator/iterator_traits.h> #include <__iterator/next.h> #include <__ranges/access.h> #include <__ranges/all.h> #include <__ranges/concepts.h> +#include <__ranges/empty_view.h> #include <__ranges/enable_borrowed_range.h> +#include <__ranges/iota_view.h> #include <__ranges/non_propagating_cache.h> +#include <__ranges/range_adaptor.h> #include <__ranges/size.h> +#include <__ranges/subrange.h> #include <__ranges/view_interface.h> +#include <__utility/auto_cast.h> +#include <__utility/forward.h> #include <__utility/move.h> #include #include @@ -118,6 +127,75 @@ template inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Tp>; + + namespace views { + namespace __drop { + template + constexpr bool __is_empty_view = false; + + template + constexpr bool __is_empty_view> = true; + + template + constexpr bool __is_passthrough_specialization = false; + + template + constexpr bool __is_passthrough_specialization> = true; + + template + constexpr bool __is_passthrough_specialization> = true; + + template + constexpr bool __is_passthrough_specialization> = true; + + template + constexpr bool __is_passthrough_specialization> = true; + + struct __fn { + template> _Np> + requires __is_empty_view> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range, _Np&&) const + noexcept(noexcept(_LIBCPP_AUTO_CAST(_VSTD::forward<_Range>(__range)))) + -> decltype( _LIBCPP_AUTO_CAST(_VSTD::forward<_Range>(__range))) + { return _LIBCPP_AUTO_CAST(_VSTD::forward<_Range>(__range)); } + + template> _Np, + class _RawRng = remove_cvref_t<_Rng>, + class _Dist = range_difference_t<_Rng>> + requires (!__is_empty_view<_RawRng> && + random_access_range<_RawRng> && + sized_range<_RawRng> && + __is_passthrough_specialization<_RawRng>) + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Rng&& __rng, _Np&& __n) const + noexcept(noexcept(_RawRng(ranges::begin(_VSTD::forward<_Rng>(__rng)) + _VSTD::min<_Dist>(ranges::size(_VSTD::forward<_Rng>(__rng)), _VSTD::forward<_Np>(__n)), + ranges::end(_VSTD::forward<_Rng>(__rng))))) + -> decltype( _RawRng(ranges::begin(_VSTD::forward<_Rng>(__rng)) + _VSTD::min<_Dist>(ranges::size(_VSTD::forward<_Rng>(__rng)), _VSTD::forward<_Np>(__n)), + ranges::end(_VSTD::forward<_Rng>(__rng)))) + { return _RawRng(ranges::begin(_VSTD::forward<_Rng>(__rng)) + _VSTD::min<_Dist>(ranges::size(_VSTD::forward<_Rng>(__rng)), _VSTD::forward<_Np>(__n)), + ranges::end(_VSTD::forward<_Rng>(__rng))); } + + template> _Np> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range, _Np&& __n) const + noexcept(noexcept(drop_view(_VSTD::forward<_Range>(__range), _VSTD::forward<_Np>(__n)))) + -> decltype( drop_view(_VSTD::forward<_Range>(__range), _VSTD::forward<_Np>(__n))) + { return drop_view(_VSTD::forward<_Range>(__range), _VSTD::forward<_Np>(__n)); } + + template + requires constructible_from, _Np> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Np&& __n) const + noexcept(is_nothrow_constructible_v, _Np>) + { return __range_adaptor_closure_t(_VSTD::__bind_back(*this, _VSTD::forward<_Np>(__n))); } + }; + } + + inline namespace __cpo { + inline constexpr auto drop = __drop::__fn{}; + } + } // namespace views } // namespace ranges #endif // !defined(_LIBCPP_HAS_NO_RANGES) diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -840,6 +840,7 @@ header "span" export ranges.__ranges.enable_borrowed_range export version + module span_fwd { private header "__fwd/span.h" } } module sstream { header "sstream" @@ -871,6 +872,7 @@ export initializer_list export __string export * + module string_view_fwd { private header "__fwd/string_view.h" } } module strstream { header "strstream" diff --git a/libcxx/include/span b/libcxx/include/span --- a/libcxx/include/span +++ b/libcxx/include/span @@ -129,6 +129,7 @@ #include <__config> #include <__debug> +#include <__fwd/span.h> #include <__iterator/concepts.h> #include <__iterator/wrap_iter.h> #include <__ranges/concepts.h> @@ -147,17 +148,10 @@ #pragma GCC system_header #endif -_LIBCPP_PUSH_MACROS -#include <__undef_macros> - _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER > 17 -inline constexpr size_t dynamic_extent = numeric_limits::max(); -template class span; - - template struct __is_std_array : false_type {}; @@ -588,6 +582,4 @@ _LIBCPP_END_NAMESPACE_STD -_LIBCPP_POP_MACROS - #endif // _LIBCPP_SPAN diff --git a/libcxx/include/string_view b/libcxx/include/string_view --- a/libcxx/include/string_view +++ b/libcxx/include/string_view @@ -197,6 +197,7 @@ #include <__config> #include <__debug> +#include <__fwd/string_view.h> #include <__ranges/concepts.h> #include <__ranges/data.h> #include <__ranges/enable_borrowed_range.h> @@ -219,22 +220,8 @@ _LIBCPP_PUSH_MACROS #include <__undef_macros> - _LIBCPP_BEGIN_NAMESPACE_STD -template > - class _LIBCPP_TEMPLATE_VIS basic_string_view; - -typedef basic_string_view string_view; -#ifndef _LIBCPP_HAS_NO_CHAR8_T -typedef basic_string_view u8string_view; -#endif -typedef basic_string_view u16string_view; -typedef basic_string_view u32string_view; -#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS -typedef basic_string_view wstring_view; -#endif - template class _LIBCPP_PREFERRED_NAME(string_view) diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp @@ -0,0 +1,172 @@ +//===----------------------------------------------------------------------===// +// +// 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::drop + +#include + +#include +#include +#include +#include +#include + +struct View : std::ranges::view_base { + int* begin_; + int* end_; + constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { } + constexpr int* begin() const { return begin_; } + constexpr int* end() const { return end_; } +}; + +template +concept CanBePiped = requires (View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; +}; + +constexpr bool test() { + // views::drop(r, n) is equivalent to r if r is a ranges::empty_view + { + std::ranges::empty_view view; + std::same_as> auto result = std::views::drop(view, 3); + (void)result; + } + + // views::drop(r, n) is equivalent to T(begin(r) + min(size(r), n), end(r)) if: + { + // r is a std::span with dynamic_extent + { + { + int buff[] = {1, 2, 3, 4}; + std::span span(buff); + std::same_as> auto result = std::views::drop(span, 2); + assert(result.begin() == span.begin() + 2); + } + { + int buff[] = {1, 2, 3, 4}; + std::span span(buff); + std::same_as> auto result = std::views::drop(span, 10); + assert(result.begin() == span.begin() + 4); + } + } + + // r is a std::basic_string_view + { + { + char buff[5] = "1234"; + std::string_view str(buff); + std::same_as auto result = std::views::drop(str, 2); + assert(result.begin() == buff + 2); + } + { + char buff[5] = "1234"; + std::string_view str(buff); + std::same_as auto result = std::views::drop(str, 10); + assert(result.begin() == buff + 4); + } + } + + // r is a ranges::iota_view + { + { + std::ranges::iota_view view(0, 4); + std::same_as> auto result = std::views::drop(view, 2); + assert(*result.begin() == 2); + } + { + std::ranges::iota_view view(0, 4); + std::same_as> auto result = std::views::drop(view, 10); + assert(std::ranges::empty(result)); + } + } + + // r is a ranges::subrange + { + { + int buff[] = {1, 2, 3, 4}; + std::ranges::subrange subrange(buff, buff + 4); + std::same_as> auto result = std::views::drop(subrange, 2); + assert(result.begin() == buff + 2); + } + { + int buff[] = {1, 2, 3, 4}; + std::ranges::subrange subrange(buff, buff + 4); + std::same_as> auto result = std::views::drop(subrange, 10); + assert(result.begin() == buff + 4); + } + } + } + + // Otherwise, views::drop(r, n) is ranges::drop_view{r, n} + { + int buff[] = {1, 2, 3, 4}; + View view(buff, buff + 4); + std::same_as> auto result = std::views::drop(view, 2); + assert(result.begin() == buff + 2); + } + + // Test that std::views::drop is a range adaptor + { + int buff[] = {1, 2, 3, 4}; + View view(buff, buff + 4); + + // Test `v | views::drop(n)` + { + std::same_as> auto result = view | std::views::drop(1); + assert(result.begin() == buff + 1); + } + + // Test `adaptor | views::drop(n)` + { + auto f = [](int i) { return i; }; + auto const partial = std::views::transform(f) | std::views::drop(1); + using Result = std::ranges::drop_view>; + std::same_as auto result = partial(view); + assert(result.begin().base() == buff + 1); + } + + // Test `views::drop(n) | adaptor` + { + auto f = [](int i) { return i; }; + auto const partial = std::views::drop(1) | std::views::transform(f); + using Result = std::ranges::transform_view, decltype(f)>; + std::same_as auto result = partial(view); + assert(result.begin().base() == buff + 1); + } + + // Check SFINAE friendliness + { + struct NotAView { }; + struct NotADifferenceType { }; + static_assert( std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + 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; +}