-
Notifications
You must be signed in to change notification settings - Fork 32
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
Add iterable concept #219
Open
tcbrindle
wants to merge
45
commits into
main
Choose a base branch
from
pr/iterable
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add iterable concept #219
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #219 +/- ##
==========================================
- Coverage 98.60% 98.40% -0.21%
==========================================
Files 69 70 +1
Lines 2578 2629 +51
==========================================
+ Hits 2542 2587 +45
- Misses 36 42 +6 ☔ View full report in Codecov by Sentry. |
An iterable is an object which can perform internal iteration, accepting a predicate which indicates if the iteration should terminate early. Iterables need to supply an `iterate(it, pred)` function, and an `element_type(it)` function whose return type indicates the element type of the sequence. (For C++-y reasons this is a function rather than a plain alias like `using element_type = int`). Every sequence is iterable, but we can also have iterables which are not sequences. In particular, every range can safely be iterable, because iterators are never exposed and so we never need to worry about invalidation, i.e. ```cpp auto iterate(std::ranges::input_range auto&& rng, auto Pred) -> bool { for (auto&& elem : rng) { if (!pred(FWD(elem)) { return false; } } return true; } ```
Contiguous + sized ranges are still contiguous, sized, bounded sequences
Seems like a good place to start
...rather than sequences. These are the algorithms which perform a single pass from the start of a sequence up to some end point, without needing to restart again. Which is actually a suprising number of algorithms. (Although not flux::to yet, because that's more complicated.)
Also const_iterable_sequence -> const_iterable And make `flux::ref` work for iterables rather than just sequences
The current std::invocable and std::regular_invocable concepts are a bit of a pain to use. This commit adds three new callable concepts instead, each of which is specified with a signature argument: * `func_once` is a callable that will be invoked at most once * `func_mut` may be called more than once, and may hold and modify internal state * `func` may be called more than once, but may not modify state: it must be equality preserving, like `std::regular_invocable` Rather than variadic template arguments, these concepts use function signatures instead, so you say something like template <typename F> requires func<F, void(int, float)> auto g(F f); or just auto g(func<void(int, float)> auto f)
We already use this in the documentation, it's probably a good idea to make it exist in reality too
Like `adaptable_sequence`, this accepts either rvalue iterables or trivially copyable lvalue iterables, and (as the name suggests) is intended for use in sink functions, allowing the user to pass (presumably) cheap-to-copy types effectively by value, while requiring an explicit copy or move for things like std::vector.
This is a lot of changes
...and also filter_deref()
That was actually easier than expected
That was more work than expected
The final boss... In theory, we could make `cartesian_product` and `cartesian_product_map` iterable when the first argument is a non-sequence iterable, but that doesn't seem worth the effort.
tcbrindle
force-pushed
the
pr/iterable
branch
from
November 26, 2024 19:25
88ee775
to
872fd8d
Compare
Somehow the changes in this PR triggered an ICE in GCC 12 while compiling `reverse_adaptor`... even though we haven't actually made any changes to it at all (other than changing the name of a function). It's a mystery, but hopefully no longer a problematic one.
For some reason MSVC really doesn't like the static consteval auto element_type(auto& self) -> element_t<decltype((self.base_))>; formulation that we've been using in various adaptors. Fortunately, the very first thing I tried, namely using a template parameter rather than an auto param, seems to fix it, i.e. template <typename Self> static consteval auto element_type(Self& self) -> element_t<decltype((self.base_))>; even though the two formulations should be completely identical in meaning.
The new name is even worse than the old one. I really need to think of something better. Naming is hard...
Sequence flatten requires both outer and inner to be sequences
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR adds a new
iterable
concept and updates various algorithms and adaptors to use it.An
iterable
is, as the name suggests, something we can iterate over -- specifically, by using internal iteration. The required interface is:This is basically the existing
for_each_while
abstracted out into its own concept, returning abool
to indicate whether iteration was completed successfully rather than a cursor.Once
iterate()
has returned, it is unspecified whether a second call toiterate()
will start again from the beginning, carry on where it left off, do nothing, or do something else. Iterables are strictly weaker than sequences: that is, every sequence is an iterable, but not vice versa. Nonetheless, a large number of algorithms work on iterables (just about anything that can be written as a short-circuiting fold), and most of the existing sequence adaptors can work on iterables as well.Since iterables only use internal iteration, there is no external state (i.e. a cursor) which could be invalidated. Not only does this make the iterable interface easier to implement for some types, but most importantly it means that we can provide a safe default implementation of the
iterable
protocol for all C++20 ranges.This dramatically improves our interoperability with ranges, while the use of internal iteration means that converting ranges pipelines to the equivalent Flux code may yield some nice performance benefits.