-
Notifications
You must be signed in to change notification settings - Fork 12k
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
[libc++][ranges] LWG4001: iota_view
should provide empty
#79687
[libc++][ranges] LWG4001: iota_view
should provide empty
#79687
Conversation
@llvm/pr-subscribers-libcxx Author: Hristo Hristov (H-G-Hristov) ChangesImplements: https://wg21.link/LWG4001 Full diff: https://github.com/llvm/llvm-project/pull/79687.diff 3 Files Affected:
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index b69b0948325412..6401c7c6d8fc7d 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -38,7 +38,7 @@
"`3974 <https://wg21.link/LWG3974>`__","``mdspan::operator[]`` should not copy ``OtherIndexTypes``","Kona November 2023","","",""
"`3987 <https://wg21.link/LWG3987>`__","Including ``<flat_foo>`` doesn't provide ``std::begin``/``end``","Kona November 2023","","","|flat_containers|"
"`3990 <https://wg21.link/LWG3990>`__","Program-defined specializations of ``std::tuple`` and ``std::variant`` can't be properly supported","Kona November 2023","","",""
-"`4001 <https://wg21.link/LWG4001>`__","``iota_view`` should provide ``empty``","Kona November 2023","","","|ranges|"
+"`4001 <https://wg21.link/LWG4001>`__","``iota_view`` should provide ``empty``","Kona November 2023","|Complete|","19.0","|ranges|"
"","","","","",""
"`3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Yet Adopted","|Complete|","16.0",""
"","","","","",""
diff --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index c8314dd848b447..6c5923c7ef013c 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -345,6 +345,8 @@ class iota_view : public view_interface<iota_view<_Start, _BoundSentinel>> {
return __iterator{__bound_sentinel_};
}
+ _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const { return __value_ == __bound_sentinel_; }
+
_LIBCPP_HIDE_FROM_ABI constexpr auto size() const
requires(same_as<_Start, _BoundSentinel> && __advanceable<_Start>) ||
(integral<_Start> && integral<_BoundSentinel>) || sized_sentinel_for<_BoundSentinel, _Start>
diff --git a/libcxx/test/std/ranges/range.factories/range.iota.view/empty.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/empty.pass.cpp
new file mode 100644
index 00000000000000..e7adffaf63b4c9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/empty.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr bool empty() const;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+#include "types.h"
+
+template <typename R>
+concept HasFreeEmpty = requires(R r) { std::ranges::empty(r); };
+
+template <typename R>
+concept HasMemberEmpty = requires(R r) {
+ { r.empty() } -> std::same_as<bool>;
+};
+
+constexpr void test_empty_iota() {
+ std::vector<int> ev;
+
+ // Both parameters are non-const
+ {
+ auto iv = std::views::iota(std::ranges::begin(ev), std::ranges::end(ev));
+
+ static_assert(HasFreeEmpty<decltype(iv)>);
+ static_assert(HasMemberEmpty<decltype(iv)>);
+
+ assert(iv.empty());
+ }
+ // Left paramter is const
+ {
+ auto iv = std::views::iota(std::ranges::begin(std::as_const(ev)), std::ranges::end(ev));
+
+ static_assert(HasFreeEmpty<decltype(iv)>);
+ static_assert(HasMemberEmpty<decltype(iv)>);
+
+ assert(iv.empty());
+ }
+ // Right paramter is const
+ {
+ auto iv = std::views::iota(std::ranges::begin(ev), std::ranges::end(std::as_const(ev)));
+
+ static_assert(HasFreeEmpty<decltype(iv)>);
+ static_assert(HasMemberEmpty<decltype(iv)>);
+
+ assert(iv.empty());
+ }
+ // Both parameters are const
+ {
+ auto iv = std::views::iota(std::ranges::begin(std::as_const(ev)), std::ranges::end(std::as_const(ev)));
+
+ static_assert(HasFreeEmpty<decltype(iv)>);
+ static_assert(HasMemberEmpty<decltype(iv)>);
+
+ assert(iv.empty());
+ }
+
+ std::vector<char> v{'b', 'a', 'b', 'a', 'z', 'm', 't'};
+ auto fv = v | std::views::filter([](auto val) { return val == '0'; });
+
+ {
+ auto iv = std::views::iota(std::ranges::begin(fv), std::ranges::end(fv));
+
+ static_assert(HasFreeEmpty<decltype(iv)>);
+ static_assert(HasMemberEmpty<decltype(iv)>);
+
+ assert(iv.empty());
+ }
+}
+
+constexpr void test_nonempty_iota() {
+ // Default ctr
+ {
+ std::ranges::iota_view<Int42<DefaultTo42>> iv;
+
+ static_assert(HasFreeEmpty<decltype(iv)>);
+ static_assert(HasMemberEmpty<decltype(iv)>);
+
+ assert(!iv.empty());
+ }
+ // Value pass
+ {
+ std::ranges::iota_view<SomeInt> iv(SomeInt(94));
+
+ static_assert(HasFreeEmpty<decltype(iv)>);
+ static_assert(HasMemberEmpty<decltype(iv)>);
+
+ assert(!iv.empty());
+ }
+
+ {
+ std::vector<char> v;
+ auto it = std::back_inserter(v);
+ auto iv = std::views::iota(it);
+
+ static_assert(HasFreeEmpty<decltype(iv)>);
+ static_assert(HasMemberEmpty<decltype(iv)>);
+
+ assert(!iv.empty());
+ }
+ {
+ std::vector<char> v{'b', 'a', 'b', 'a', 'z', 'm', 't'};
+ auto it = std::back_inserter(v);
+ auto iv = std::views::iota(it);
+
+ static_assert(HasFreeEmpty<decltype(iv)>);
+ static_assert(HasMemberEmpty<decltype(iv)>);
+
+ assert(!iv.empty());
+ }
+}
+
+constexpr bool test() {
+ test_empty_iota();
+ test_nonempty_iota();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
|
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.
Hope these little suggestions help! Thanks for doing the work!
libcxx/test/std/ranges/range.factories/range.iota.view/empty.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/ranges/range.factories/range.iota.view/empty.pass.cpp
Outdated
Show resolved
Hide resolved
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 about the slow review! Almost LGTM with just a couple of nits.
{ | ||
auto iv = std::views::iota(std::ranges::begin(ev), std::ranges::end(ev)); | ||
|
||
static_assert(HasFreeEmpty<decltype(iv)>); |
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: normally, the ranges tests follow a pattern where constraints are tested in a separate block before testing the actual function -- can you please do the same? It's mostly for consistency, but IMO it's also easier to make sure we're testing all the constraints when the tests are all next to each other and not spread out in the file.
}; | ||
|
||
constexpr void test_empty_iota() { | ||
std::vector<int> ev; |
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.
Question: what do ev
and iv
stand for?
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.
Normally I prefer long descriptive names but the libc++ tests use short names, so I try to be consistent:
ev -> empty vector vs v -> non-empty vector.
iv -> iota_view
I can change these.
} | ||
// Right parameter is const | ||
{ | ||
auto iv = std::views::iota(std::ranges::begin(ev), std::ranges::end(std::as_const(ev))); |
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.
Question: does using as_const
really matter to test iv.empty()
?
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 it was good to be detailed. Should I remove those?
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.
Removed
libcxx/test/std/ranges/range.factories/range.iota.view/empty.pass.cpp
Outdated
Show resolved
Hide resolved
…ass.cpp Co-authored-by: Will Hawkins <whh8b@obs.cr>
…ass.cpp Co-authored-by: Will Hawkins <whh8b@obs.cr>
76c72a5
to
02c4e71
Compare
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.
Minor comments about the tests but this overall LGTM.
#include "types.h" | ||
|
||
template <typename R> | ||
concept HasEmpty = requires(R r) { |
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.
concept HasEmpty = requires(R r) { | |
concept HasEmpty = requires(R const r) { |
Otherwise you don't test anywhere that the method is marked as 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.
Done
// Left parameter is const | ||
{ | ||
auto iv = std::views::iota(std::ranges::begin(std::as_const(ev)), std::ranges::end(ev)); | ||
|
||
assert(iv.empty()); | ||
} | ||
// Right parameter is const | ||
{ | ||
auto iv = std::views::iota(std::ranges::begin(ev), std::ranges::end(std::as_const(ev))); | ||
|
||
assert(iv.empty()); | ||
} | ||
// Both parameters are const | ||
{ | ||
auto iv = std::views::iota(std::ranges::begin(std::as_const(ev)), std::ranges::end(std::as_const(ev))); | ||
|
||
assert(iv.empty()); | ||
} |
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 really understand the purpose of these tests. We're interested in checking that empty()
can be called on a const iota_view
, not that we can call it on a mutable iota_view
constructed from const 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.
Removed
Thanks! I think I addressed the comments. |
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.
LGTM. Can be merged once CI is green. Thanks!
Thank you! The CI is green finally! |
) Implements: https://wg21.link/LWG4001 - https://eel.is/c++draft/range.iota.view --------- Co-authored-by: Zingam <zingam@outlook.com> Co-authored-by: Will Hawkins <whh8b@obs.cr>
Summary: Implements: https://wg21.link/LWG4001 - https://eel.is/c++draft/range.iota.view --------- Co-authored-by: Zingam <zingam@outlook.com> Co-authored-by: Will Hawkins <whh8b@obs.cr> Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60251523
Implements: https://wg21.link/LWG4001