diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 2bb6d263340d3..920d5c30572cd 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -663,6 +663,7 @@ set(files __ranges/data.h __ranges/drop_view.h __ranges/drop_while_view.h + __ranges/elements_of.h __ranges/elements_view.h __ranges/empty.h __ranges/empty_view.h diff --git a/libcxx/include/__ranges/elements_of.h b/libcxx/include/__ranges/elements_of.h new file mode 100644 index 0000000000000..668bd23d0ff7f --- /dev/null +++ b/libcxx/include/__ranges/elements_of.h @@ -0,0 +1,49 @@ +// -*- 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_ELEMENTS_OF_H +#define _LIBCPP___RANGES_ELEMENTS_OF_H + +#include <__config> +#include <__memory/allocator.h> +#include <__ranges/concepts.h> +#include <__utility/forward.h> +#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 >= 23 + +namespace ranges { + +template > +struct elements_of { + _LIBCPP_NO_UNIQUE_ADDRESS _Range range; + _LIBCPP_NO_UNIQUE_ADDRESS _Allocator allocator = _Allocator(); +}; + +template > +elements_of(_Range&&, _Allocator = _Allocator()) -> elements_of<_Range&&, _Allocator>; + +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_ELEMENTS_OF_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index ed2b7fb192164..32fe726dadc17 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -1737,6 +1737,7 @@ module std [system] { header "__ranges/drop_while_view.h" export std.functional.bind_back } + module elements_of { header "__ranges/elements_of.h" } module elements_view { header "__ranges/elements_view.h" } module empty { header "__ranges/empty.h" } module empty_view { header "__ranges/empty_view.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges index d8ee6f75e8b23..07ac5ed02ef0d 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -116,6 +116,10 @@ namespace std::ranges { // [range.dangling], dangling iterator handling struct dangling; + // [range.elementsof], class template elements_of + template> + struct elements_of; + template using borrowed_iterator_t = see below; @@ -392,6 +396,7 @@ namespace std { # include <__ranges/data.h> # include <__ranges/drop_view.h> # include <__ranges/drop_while_view.h> +# include <__ranges/elements_of.h> # include <__ranges/elements_view.h> # include <__ranges/empty.h> # include <__ranges/empty_view.h> diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index a5e2a2b4583c1..c3aedd594a0ae 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -83,8 +83,10 @@ export namespace std { // [range.dangling], dangling iterator handling using std::ranges::dangling; +#if _LIBCPP_STD_VER >= 23 // [range.elementsof], class template elements_­of - // using std::ranges::elements_of; + using std::ranges::elements_of; +#endif using std::ranges::borrowed_iterator_t; diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv index 096c321672474..d13717a9d6a4b 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx23.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv @@ -835,6 +835,7 @@ random version ranges cctype ranges compare ranges concepts +ranges cstddef ranges cstdint ranges cstdio ranges cstring diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv index 74d912e5fe3a3..569d722eb40d7 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx26.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv @@ -834,6 +834,7 @@ random version ranges cctype ranges compare ranges concepts +ranges cstddef ranges cstdint ranges cstdio ranges cstring diff --git a/libcxx/test/std/ranges/range.utility/range.elementsof/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.utility/range.elementsof/ctad.compile.pass.cpp new file mode 100644 index 0000000000000..5e7eeb831ba77 --- /dev/null +++ b/libcxx/test/std/ranges/range.utility/range.elementsof/ctad.compile.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// 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::elements_of; + +#include + +#include + +#include "min_allocator.h" +#include "test_allocator.h" +#include "test_iterators.h" + +template +struct Range { + Iterator begin() const; + sentinel_wrapper end() const; +}; + +constexpr bool test() { + types::for_each( + types::type_list, min_allocator, test_allocator>{}, + [] { + types::for_each(types::cpp20_input_iterator_list{}, [] { + Range r; + static_assert(std::same_as&, std::allocator>>); + static_assert(std::same_as())), + std::ranges::elements_of&&, std::allocator>>); + + Allocator a; + static_assert(std::same_as&, Allocator>>); + static_assert(std::same_as(), Allocator())), + std::ranges::elements_of&&, Allocator>>); + + static_assert(std::same_as&, Allocator>>); + static_assert( + std::same_as(), .allocator = Allocator()}), + std::ranges::elements_of&&, Allocator>>); + }); + }); + + return true; +} + +static_assert(test()); diff --git a/libcxx/test/std/ranges/range.utility/range.elementsof/elements_of.pass.cpp b/libcxx/test/std/ranges/range.utility/range.elementsof/elements_of.pass.cpp new file mode 100644 index 0000000000000..e7bf9098881ab --- /dev/null +++ b/libcxx/test/std/ranges/range.utility/range.elementsof/elements_of.pass.cpp @@ -0,0 +1,107 @@ +//===----------------------------------------------------------------------===// +// +// 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::elements_of; + +#include + +#include +#include +#include +#include + +#include "min_allocator.h" +#include "test_allocator.h" +#include "test_iterators.h" + +template +struct Range { + using Sentinel = sentinel_wrapper; + + Iterator begin() { return Iterator(data_.data()); } + + Sentinel end() { return Sentinel(Iterator(data_.data() + data_.size())); } + +private: + std::vector data_ = {0, 1, 2, 3}; +}; + +template +constexpr bool test_range() { + Range r; + + using elements_of_t = std::ranges::elements_of; + { + // constructor + std::same_as decltype(auto) elements_of = std::ranges::elements_of(r, Allocator()); + [[maybe_unused]] std::same_as decltype(auto) elements_of_range = elements_of.range; + if (!std::is_constant_evaluated()) { + assert(std::ranges::distance(elements_of_range) == 4); + } + [[maybe_unused]] std::same_as decltype(auto) elements_of_allocator = elements_of.allocator; + } + { + // designated initializer + std::same_as decltype(auto) elements_of = std::ranges::elements_of{ + .range = r, + .allocator = Allocator(), + }; + [[maybe_unused]] std::same_as decltype(auto) elements_of_range = elements_of.range; + if (!std::is_constant_evaluated()) { + assert(std::ranges::distance(elements_of_range) == 4); + } + [[maybe_unused]] std::same_as decltype(auto) elements_of_allocator = elements_of.allocator; + } + { + // copy constructor + std::same_as decltype(auto) elements_of_1 = std::ranges::elements_of(r, Allocator()); + std::same_as auto elements_of_2 = elements_of_1; + [[maybe_unused]] std::same_as decltype(auto) elements_of_1_range = elements_of_1.range; + [[maybe_unused]] std::same_as decltype(auto) elements_of_2_range = elements_of_2.range; + if (!std::is_constant_evaluated()) { + assert(std::ranges::distance(elements_of_1_range) == 4); + assert(std::ranges::distance(elements_of_2_range) == 4); + } + [[maybe_unused]] std::same_as decltype(auto) elements_of_2_allocator = elements_of_2.allocator; + } + + using elements_of_r_t = std::ranges::elements_of; + { + // move constructor + std::same_as decltype(auto) elements_of_1 = std::ranges::elements_of(std::move(r), Allocator()); + std::same_as auto elements_of_2 = std::move(elements_of_1); + [[maybe_unused]] std::same_as decltype(auto) elements_of_1_range = std::move(elements_of_1.range); + [[maybe_unused]] std::same_as decltype(auto) elements_of_2_range = std::move(elements_of_2.range); + if (!std::is_constant_evaluated()) { + assert(std::ranges::distance(elements_of_1_range) == 4); + assert(std::ranges::distance(elements_of_2_range) == 4); + } + [[maybe_unused]] std::same_as decltype(auto) elements_of_2_allocator = elements_of_2.allocator; + } + return true; +} + +constexpr bool test() { + types::for_each(types::type_list, min_allocator, test_allocator>{}, + [] { + types::for_each(types::cpp20_input_iterator_list{}, [] { + test_range, Allocator>(); + }); + }); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +}