-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[libc++] Implement ranges::iota #68494
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c4a3ccf
1b808ec
574c94c
e4cfe00
7cdfea2
a1d015c
ab4c67d
c44d7d6
a455f42
08e3c77
e658ec2
67fcac1
b132784
88e271a
ff5bf79
b8f07b8
a4d34fd
334088f
8153244
a872e39
0f538aa
b84859b
3bd4c6d
6be79e2
7cde8ed
074d685
72125ea
ce909e8
028ce92
b3f260e
be7faa6
20b848b
2803493
5f0389c
79d6cc5
07b8183
3d5ad73
fd81400
3285f4e
cea6379
3f0670f
a7e77b1
d50b7a1
b4008ad
240d4f0
8f5ffc3
b99c8d8
dc29b80
e325de4
913a8d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// -*- 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___ALGORITHM_OUT_VALUE_RESULT_H | ||
#define _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H | ||
|
||
#include <__concepts/convertible_to.h> | ||
#include <__config> | ||
#include <__utility/move.h> | ||
|
||
#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 <class _OutIter1, class _ValType1> | ||
struct out_value_result { | ||
_LIBCPP_NO_UNIQUE_ADDRESS _OutIter1 out; | ||
_LIBCPP_NO_UNIQUE_ADDRESS _ValType1 value; | ||
|
||
template <class _OutIter2, class _ValType2> | ||
requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2> | ||
_LIBCPP_HIDE_FROM_ABI constexpr operator out_value_result<_OutIter2, _ValType2>() const& { | ||
return {out, value}; | ||
} | ||
|
||
template <class _OutIter2, class _ValType2> | ||
requires convertible_to<_OutIter1, _OutIter2> && convertible_to<_ValType1, _ValType2> | ||
_LIBCPP_HIDE_FROM_ABI constexpr operator out_value_result<_OutIter2, _ValType2>() && { | ||
return {std::move(out), std::move(value)}; | ||
} | ||
}; | ||
|
||
} // namespace ranges | ||
|
||
#endif // _LIBCPP_STD_VER >= 23 | ||
|
||
_LIBCPP_END_NAMESPACE_STD | ||
|
||
_LIBCPP_POP_MACROS | ||
|
||
#endif // _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// -*- 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___NUMERIC_RANGES_IOTA_H | ||
#define _LIBCPP___NUMERIC_RANGES_IOTA_H | ||
|
||
#include <__algorithm/out_value_result.h> | ||
#include <__config> | ||
#include <__iterator/concepts.h> | ||
#include <__ranges/access.h> | ||
#include <__ranges/concepts.h> | ||
#include <__ranges/dangling.h> | ||
#include <__utility/as_const.h> | ||
#include <__utility/move.h> | ||
|
||
philnik777 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#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 <typename _Out, typename _Tp> | ||
using iota_result = ranges::out_value_result<_Out, _Tp>; | ||
|
||
struct __iota_fn { | ||
public: | ||
template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp> | ||
requires indirectly_writable<_Out, const _Tp&> | ||
_LIBCPP_HIDE_FROM_ABI static constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) { | ||
while (__first != __last) { | ||
*__first = std::as_const(__value); | ||
++__first; | ||
++__value; | ||
} | ||
return {std::move(__first), std::move(__value)}; | ||
} | ||
|
||
template <weakly_incrementable _Tp, ranges::output_range<const _Tp&> _Range> | ||
_LIBCPP_HIDE_FROM_ABI static constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> | ||
philnik777 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
operator()(_Range&& __r, _Tp __value) { | ||
return operator()(ranges::begin(__r), ranges::end(__r), std::move(__value)); | ||
} | ||
}; | ||
|
||
inline constexpr auto iota = __iota_fn{}; | ||
} // namespace ranges | ||
|
||
#endif // _LIBCPP_STD_VER >= 23 | ||
|
||
_LIBCPP_END_NAMESPACE_STD | ||
|
||
_LIBCPP_POP_MACROS | ||
|
||
#endif // _LIBCPP___NUMERIC_RANGES_IOTA_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
//===----------------------------------------------------------------------===// | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've started working on this in https://reviews.llvm.org/D121436 (and didn't really work on it because nobody knew what it was for). You should probably make sure that any comments there are addressed here and add any additional tests from there. |
||
// | ||
jamesETsmith marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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 | ||
|
||
// template <class O, class T> | ||
// struct out_value_result; | ||
|
||
#include <algorithm> | ||
#include <cassert> | ||
#include <type_traits> | ||
|
||
#include "MoveOnly.h" | ||
|
||
using std::ranges::out_value_result; | ||
|
||
// | ||
// Helper structs | ||
// | ||
|
||
// only explicit construction | ||
struct IterTypeExplicit { | ||
explicit IterTypeExplicit(int*); | ||
}; | ||
|
||
// implicit construction | ||
struct IterTypeImplicit { | ||
IterTypeImplicit(int*); | ||
}; | ||
|
||
struct IterTypeImplicitRef { | ||
IterTypeImplicitRef(int&); | ||
}; | ||
|
||
struct NotConvertible {}; | ||
|
||
template <class T> | ||
struct ConvertibleFrom { | ||
constexpr ConvertibleFrom(T c) : content{c} {} | ||
T content; | ||
}; | ||
|
||
// Standard layout classes can't have virtual functions | ||
struct NonStandardLayoutTypeBase { | ||
virtual ~NonStandardLayoutTypeBase(); | ||
}; | ||
struct NonStandardLayoutType : public NonStandardLayoutTypeBase {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: why is it necessary to have a derived class? Could we just use |
||
|
||
// | ||
constexpr bool test_constraints() { | ||
// requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2> | ||
static_assert(std::is_constructible_v<out_value_result<int*, int>, out_value_result<int*, int>>); | ||
|
||
// test failure when implicit conversion isn't allowed | ||
static_assert(!std::is_constructible_v<out_value_result<IterTypeExplicit, int>, out_value_result<int*, int>>); | ||
|
||
// test success when implicit conversion is allowed, checking combinations of value, reference, and const | ||
static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int>>); | ||
static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int> const>); | ||
static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int>&>); | ||
static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int> const&>); | ||
|
||
static_assert(!std::is_constructible_v<out_value_result<IterTypeImplicitRef, int>, out_value_result<int, int>&>); | ||
|
||
// has to be convertible via const& | ||
static_assert(std::is_convertible_v<out_value_result<int, int>&, out_value_result<long, long>>); | ||
static_assert(std::is_convertible_v<const out_value_result<int, int>&, out_value_result<long, long>>); | ||
static_assert(std::is_convertible_v<out_value_result<int, int>&&, out_value_result<long, long>>); | ||
static_assert(std::is_convertible_v<const out_value_result<int, int>&&, out_value_result<long, long>>); | ||
|
||
// should be move constructible | ||
static_assert(std::is_move_constructible_v<out_value_result<MoveOnly, int>>); | ||
static_assert(std::is_move_constructible_v<out_value_result<int, MoveOnly>>); | ||
|
||
// conversions should not work if there is no conversion | ||
static_assert(!std::is_convertible_v<out_value_result<NotConvertible, int>, out_value_result<int, NotConvertible>>); | ||
static_assert(!std::is_convertible_v<out_value_result<int, NotConvertible>, out_value_result<NotConvertible, int>>); | ||
|
||
// check standard layout | ||
static_assert(std::is_standard_layout_v<out_value_result<int, int>>); | ||
static_assert(!std::is_standard_layout_v<out_value_result<NonStandardLayoutType, int>>); | ||
|
||
return true; | ||
} | ||
|
||
// Test results | ||
constexpr bool test() { | ||
{ | ||
// Check that conversion operator works | ||
out_value_result<double, int> res{10, 1}; | ||
assert(res.out == 10); | ||
assert(res.value == 1); | ||
out_value_result<ConvertibleFrom<double>, ConvertibleFrom<int>> res2 = res; | ||
assert(res2.out.content == 10); | ||
assert(res2.value.content == 1); | ||
} | ||
{ | ||
// Check that out_value_result isn't overconstrained w.r.t. move/copy constructors | ||
out_value_result<MoveOnly, int> res{MoveOnly{}, 10}; | ||
assert(res.out.get() == 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: consider passing the value explicitly (honestly, I think |
||
assert(res.value == 10); | ||
auto res2 = std::move(res); | ||
assert(res.out.get() == 0); | ||
assert(res.value == 10); | ||
assert(res2.out.get() == 1); | ||
assert(res2.value == 10); | ||
} | ||
{ | ||
// Check structured binding | ||
auto [out, val] = out_value_result<int, int>{1, 2}; | ||
assert(out == 1); | ||
assert(val == 2); | ||
} | ||
{ | ||
// Check default construction | ||
out_value_result<int, double> res; | ||
static_assert(std::is_same_v<int, decltype(res.out)>); | ||
static_assert(std::is_same_v<double, decltype(res.value)>); | ||
} | ||
{ | ||
// Check aggregate initiazliation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just wanted to make sure you saw this. |
||
out_value_result<int, int> res = {1, 2}; | ||
assert(res.out == 1); | ||
assert(res.value == 2); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
int main(int, char**) { | ||
test_constraints(); | ||
static_assert(test_constraints()); | ||
test(); | ||
static_assert(test()); | ||
return 0; | ||
} | ||
strega-nil marked this conversation as resolved.
Show resolved
Hide resolved
|
Uh oh!
There was an error while loading. Please reload this page.