-
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
Conversation
…nd out_value_result. Updating Cxx23Papers.csv too.
…nd adding testing for more iterators
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't looked at the tests yet. The implementation itself looks mostly good.
@@ -0,0 +1,124 @@ | |||
//===----------------------------------------------------------------------===// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The 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.
struct __iota_fn { | ||
template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp> | ||
requires indirectly_writable<_Out, const _Tp&> | ||
constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const { | |
static constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) { |
same below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@philnik777, I'm happy to make these changes, but could you explain why static
is more appropriate than const
here?
As an alternative, after I've added the helper function, I could make that static and leave the public functions as const
like it says in the original paper. I stumbled on this strategy (it's used by ranges::for_each
and ranges::generate
at least) while looking at how other libcxx helper functions were structured.
EDIT: I realized it would probably be easier to just implement this second strategy (in a455f42) and see what you think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
static operator()
has been introduced in C++23, so we can't (currently) use it for C++20 algorithms. Otherwise we would also use it there. The simple reason to use it is that we don't care about the object and the static
version has slightly better code gen (https://godbolt.org/z/8E1f77q6f).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, I wasn't aware that static operator()
was introduced in C++23. That clears things up, thanks for the example.
libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
Outdated
Show resolved
Hide resolved
|
||
template <weakly_incrementable _Tp, ranges::output_range<const _Tp&> _Range> | ||
constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> operator()(_Range&& __r, _Tp __value) const { | ||
return (*this)(ranges::begin(__r), ranges::end(__r), std::move(__value)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll probably run into problems with this when updating the robust_against_*
tests. You should move the algorithm itself into a helper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see why this would cause problems?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I've been slow on this. @philnik777 do you have any suggestions/comments about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have "robust" tests to make sure we're not copying or moving comparators and projections, but I don't think we do that for "values" as is the case here. Whether it's an omission or not is a separate question, but as it stands, I think the original form of this code (forwarding directly from the ranges overload to the iterator overload) should work -- can you please check that, @jamesETsmith?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought I had rechecked that the original implementation had caused problems with the robust_against_*
tests, but after checking again today, it doesn't seem to cause any problems.
As a result, I've reverted to the original strategy at @cjdb and @var-const's suggestion.
Thanks for taking a look @philnik777! I'll try to address your comments in the next couple of days and let you know if I have any questions. |
Sorry for the delay, just circling back to this. @philnik777 should I update the Probably a simple follow-up question, but you're interested in adding |
…tion and updating docs.
Yes, it should be the |
…t_* tests (proxy iterators is failing currently).
✅ With the latest revision this PR passed the C/C++ code formatter. |
@philnik777, I've added tests for
|
Why does the proxy not satisfy |
@philnik777, I think you're right that |
You should make the operators conditionally available based on whether the underlying type has them. The rest looks good. |
Making the operations conditionals was pretty straightforward, but My gut reaction was to try and make that alias require a concept, but the more I try to get something like a "conditional using-declaration" to work the more it feels like that isn't allowed and I'm thinking about this the wrong way. Do you have any suggestions @philnik777? Here's an updated minimal example showing the crash that I'm talking about: https://godbolt.org/z/KocGjhPhz |
You can add a base class like this: https://godbolt.org/z/ccG7jcqee |
That's much cleaner than what I was coming up with, thanks! I'll give that a try |
I have not started it, it'd be great if you could do a merge commit 😄 |
@strega-nil, ok I'll do that now |
…esolving merge conflicts
@strega-nil, I think this is good to go from my end unless people have further comments. |
… version.version.compile tests rather than generating it with the python script so the formatting was off
I forgot (again) to update |
I'll take a look at this tomorrow 😃 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! Just some minor nits, but other than that almost ready to merge 😄
@@ -2,4 +2,5 @@ Standard,Name,Assignee,CL,Status | |||
C++23,`ranges::to <https://wg21.link/P1206R7>`_,Konstantin Varlamov,`D142335 <https://reviews.llvm.org/D142335>`_,Complete | |||
C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,Unassigned,No patch yet,Not started | |||
C++23,`Formatting Ranges <https://wg21.link/P2286R8>`_,Mark de Wever,Various,Complete | |||
C++23, `ranges::iota <https://wg21.link/P2440R1>`_, James E T Smith, `PR68494 <https://github.com/llvm/llvm-project/pull/68494>`_, In Progress |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe I'd call this a "major ranges feature"; this is an important ranges feature, but isn't quite as large as the other things on this list; I'd remove this change.
libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp
Outdated
Show resolved
Hide resolved
@@ -100,6 +101,11 @@ constexpr void run_tests() { | |||
test(std::ranges::search, in, in2); | |||
test(std::ranges::search_n, in, count, x); | |||
test(std::ranges::find_end, in, in2); | |||
#if TEST_STD_VER >= 23 | |||
if constexpr (std::copyable<T>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also do if constexpr (weakly_incrementable<T>) { test(std::ranges::iota, in, T{}); }
?
@strega-nil, thanks again for looking this over. I hope I've addressed all your comments with b4008ad (with the exception of this one, which is I'm waiting on until we reach a resolution) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for finishing this up! Now we just need one more reviewer, and you can merge 😄
Thanks again @strega-nil! @var-const, @cjdb, and @philnik777 could you take another look at this PR and let me know if I've addressed your comments and if you have any other changes you'd like to see? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nearly there! Thanks for you patience :)
// which we need in certain situations. For example when we want | ||
// std::weakly_incrementable<Proxy<T>> to be true. | ||
template <class T> | ||
struct ProxyDiffTBase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a rule of thumb, I don't mind duplication in tests so that things are known to be correct on inspection. Abstraction is fine, but only if it doesn't cast doubt on our confidence that the test is correct.
I think in this case, I'd prefer a proxy_with_difference_type
and proxy_missing_difference_type
. That might be deserving of its own PR, and before this one is merged so you can use it.
template <class T> | ||
struct ProxyDiffTBase { | ||
// Add default `operator<=>` so that the derived type, Proxy, can also use the default `operator<=>` | ||
friend constexpr auto operator<=>(const ProxyDiffTBase&, const ProxyDiffTBase&) = default; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: pass by value (same elsewhere for types that you wouldn't ordinarily pass by ref-to-const).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree it seems a bit unnecessary to pass by ref-to-const here, but I was basing these on the existing Proxy<T>
which uses ref-to-const
…ripped from algorithm header, adding it back
@jamesETsmith I think it makes sense to create a subissues in #105184 so you can associate this PR with it. |
….h for module builds to pass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the work! LGTM. I don't think you need to wait for @var-const. He's not been doing much work upstream recently, so I'm not sure he'll see this. Maybe wait for a day or two just in case.
@Zingam I don't have permission to add this as a subissue, but I agree that seems like a good idea. I don't have much experience contributing to libc++ so I'll defer to more senior people. @ldionne, would it be ok to add this as a subissue? |
libcxx/docs/Status/Cxx23Papers.csv
Outdated
@@ -46,7 +46,7 @@ | |||
"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","Implemented the type traits only." | |||
"`P2273R3 <https://wg21.link/P2273R3>`__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16","" | |||
"`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","" | |||
"`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","","","" | |||
"`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial| ``std::ranges::shift_left`` and ``std::ranges::shift_right`` have not been implemented","","|ranges|" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line doesn't seem right. The comment should go in the last column I think. Also maybe just claim what's implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, should be fixed now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Zingam lmk if it looks good to you now
I created the sub-issues for you and associated the PR with it. |
… was out of order in the algorithm header which was triggering clang-fmt and clang-tidy CI failures
Just fixed the out of order imports that were causing the clang-fmt and clang-tidy failures on CI. For reference, I couldn't reproduce the clang-tidy errors locally when running the |
@jamesETsmith I think we should have an entry for this in the Release Notes: |
Overview
As a disclaimer, this is my first PR to LLVM and while I've tried to ensure I've followed the LLVM and libc++ contributing guidelines, there's probably a good chance I missed something. If I have, just let me know and I'll try to correct it as soon as I can.
This PR implements
std::ranges::iota
andstd::ranges::out_value_result
outlined in P2440r1.As outlined in the paper above, I've:
out_value_result
and added to<algorithm>
out_value_result
,iota_result
, and two overloads ofiota
tostd::ranges
in<numeric>
__cpp_lib_ranges_iota
in<version>
I've also added tests for
ranges::iota
andranges::out_value_result
. Lastly, I added those structs to the appropriate module files.Partially implements #105184
EDIT: Forgot to mention in the original post, thanks to @hawkinsw for taking a look at a preliminary version of this PR!
TODOs
I'm open to implementing the rest of P2440r1 (ranges::shift_left
andranges::shift_right
) if that's ok, I just wanted to get feedback onranges::iota
firstCloses: #134060