diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv --- a/libcxx/docs/Status/RangesPaper.csv +++ b/libcxx/docs/Status/RangesPaper.csv @@ -144,6 +144,6 @@ `[range.empty] `_,`empty_view `_,[view.interface],Zoe Carver,✅ `[range.single] `_,single_view,[view.interface],Zoe Carver,In Progress `[range.split] `_,split_view,[range.all],Unassigned,Not started -`[range.counted] `_,view::counted,[range.subrange],Zoe Carver,Not started +`[range.counted] `_,view::counted,[range.subrange],Zoe Carver,✅ `[range.common] `_,common_view,[range.all],Zoe Carver,✅ `[range.reverse] `_,reverse_view,[range.all],Unassigned,Not started diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -188,6 +188,7 @@ __ranges/common_view.h __ranges/concepts.h __ranges/copyable_box.h + __ranges/counted.h __ranges/dangling.h __ranges/data.h __ranges/drop_view.h diff --git a/libcxx/include/__ranges/counted.h b/libcxx/include/__ranges/counted.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/counted.h @@ -0,0 +1,87 @@ +// -*- 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_COUNTED_H +#define _LIBCPP___RANGES_COUNTED_H + +#include <__config> +#include <__iterator/concepts.h> +#include <__iterator/iterator_traits.h> +#include <__memory/pointer_traits.h> +#include <__ranges/concepts.h> +#include <__ranges/subrange.h> +#include <__utility/__decay_copy.h> +#include <__utility/declval.h> +#include <__utility/forward.h> +#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 !defined(_LIBCPP_HAS_NO_RANGES) + +namespace views { + +namespace __counted { + struct __fn { + template + requires contiguous_iterator> + _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Iter&& __t, iter_difference_t<_Iter> __c) const + noexcept(noexcept( + span(_VSTD::to_address(_VSTD::forward<_Iter>(__t)), static_cast>(__c)) + )) + { + return span(_VSTD::to_address(_VSTD::forward<_Iter>(__t)), static_cast>(__c)); + } + + template + requires random_access_iterator> + _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Iter&& __t, iter_difference_t<_Iter> __c) const + noexcept( + noexcept(__t + static_cast>(__c)) && + noexcept(ranges::subrange(_VSTD::forward<_Iter>(__t), _VSTD::forward<_Iter>(__t))) + ) + { + auto __sent = __t + static_cast>(__c); + return ranges::subrange(_VSTD::forward<_Iter>(__t), _VSTD::forward<_Iter>(__sent)); + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Iter&& __t, iter_difference_t<_Iter> __c) const + noexcept(noexcept( + ranges::subrange(counted_iterator(_VSTD::forward<_Iter>(__t), __c), default_sentinel) + )) + { + return ranges::subrange(counted_iterator(_VSTD::forward<_Iter>(__t), __c), default_sentinel); + } + }; +} + +inline namespace __cpo { + inline constexpr auto counted = __counted::__fn{}; +} // namespace __cpo + +} // namespace views + +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_COUNTED_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -617,6 +617,7 @@ 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" } + module counted { private header "__ranges/counted.h" } module dangling { private header "__ranges/dangling.h" } module data { private header "__ranges/data.h" } module drop_view { private header "__ranges/drop_view.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -149,6 +149,9 @@ can-reference>> class transform_view; + // [range.counted], counted view + namespace views { inline constexpr unspecified counted = unspecified; } + // [range.common], common view template requires (!common_range && copyable>) @@ -164,6 +167,7 @@ #include <__ranges/access.h> #include <__ranges/all.h> #include <__ranges/common_view.h> +#include <__ranges/counted.h> #include <__ranges/concepts.h> #include <__ranges/dangling.h> #include <__ranges/data.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/ranges/counted.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/counted.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/counted.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/counted.h'}} +#include <__ranges/counted.h> diff --git a/libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.counted/counted.pass.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::views::counted; + +#include +#include +#include + +#include +#include "test_macros.h" +#include "test_iterators.h" + +struct Unrelated {}; + +struct ConvertibleToSize { + constexpr operator size_t() const { return 8; } +}; + +template +concept CountedInvocable = requires(Iter& i, T t) { std::views::counted(i, t); }; + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + static_assert( CountedInvocable, ConvertibleToSize>); + static_assert(!CountedInvocable, Unrelated>); + + static_assert(std::semiregular>); + } + + { + { + contiguous_iterator iter(buffer); + std::span s = std::views::counted(iter, 8); + assert(s.size() == 8); + assert(s.data() == buffer); + } + { + const contiguous_iterator iter(buffer); + std::span s = std::views::counted(iter, 8); + assert(s.size() == 8); + assert(s.data() == buffer); + } + } + + { + { + random_access_iterator iter(buffer); + std::ranges::subrange> s = std::views::counted(iter, 8); + assert(s.size() == 8); + assert(s.begin() == iter); + } + { + const random_access_iterator iter(buffer); + std::ranges::subrange> s = std::views::counted(iter, 8); + assert(s.size() == 8); + assert(s.begin() == iter); + } + } + + { + { + bidirectional_iterator iter(buffer); + std::ranges::subrange< + std::counted_iterator>, + std::default_sentinel_t> s = std::views::counted(iter, 8); + assert(s.size() == 8); + assert(s.begin() == std::counted_iterator(iter, 8)); + } + { + const bidirectional_iterator iter(buffer); + std::ranges::subrange< + std::counted_iterator>, + std::default_sentinel_t> s = std::views::counted(iter, 8); + assert(s.size() == 8); + assert(s.begin() == std::counted_iterator(iter, 8)); + } + { + output_iterator iter(buffer); + std::ranges::subrange< + std::counted_iterator>, + std::default_sentinel_t> s = std::views::counted(iter, 8); + assert(s.size() == 8); + assert(s.begin() == std::counted_iterator(iter, 8)); + } + { + const output_iterator iter(buffer); + std::ranges::subrange< + std::counted_iterator>, + std::default_sentinel_t> s = std::views::counted(iter, 8); + assert(s.size() == 8); + assert(s.begin() == std::counted_iterator(iter, 8)); + } + { + cpp20_input_iterator iter(buffer); + std::ranges::subrange< + std::counted_iterator>, + std::default_sentinel_t> s = std::views::counted(std::move(iter), 8); + assert(s.size() == 8); + assert(s.begin().base().base() == buffer); + } + { + std::ranges::subrange< + std::counted_iterator>, + std::default_sentinel_t> s = std::views::counted(cpp20_input_iterator(buffer), 8); + assert(s.size() == 8); + assert(s.begin().base().base() == buffer); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +}