As discussed with jokereph over IRC a few weeks ago, this augments the STLExtras toolset with a zip iterator and range adapter. Zip comes in two varieties: zip, which will zip to the shortest of the input ranges, and zip_first, which limits its begin() == end() checks to just the first range.
Details
Diff Detail
- Repository
- rL LLVM
Event Timeline
Generally lacking test coverage, and could consider using std::begin/std::end rather than member begin/end to make sure these algorithms work with arrays and other things without member begin/end.
include/llvm/ADT/STLExtras.h | ||
---|---|---|
238–259 | These look like implementation details (& arguably ZipFirst/ZipShortest/Zippy are implementation details too) that should be hidden from the llvm namespace (either as local/nested types or in a 'detail' namespace, etc). |
Use a more efficient method of generating the integer sequence. Encase NatList, Zippy, ZipFirst, ZipShortest in their own detail namespace.
I'd be happy to write tests for this, but where would they live, filesystem-wise? I would assume under test/*, but am unable to locate any tests of other STLExtra constructs.
Regarding using this with raw C arrays, I'm not sure that would be possible. From memory, C++11 compilers have special case handling for ranged-fors on raw arrays. You could certainly pass in initializer_list<T> into zip/zip_first, but then the initializer_list proxy would already have begin/end member functions.
include/llvm/ADT/STLExtras.h | ||
---|---|---|
538 | Split into a separate patch for this addition (unless it is related to zip in a way that I missed) |
- When folding bools, replace std::array/std::all_of with a simple constexpr recursive function.
- Add basic unit test demonstrating rvalue input, mutability, and raw arrays.
include/llvm/ADT/STLExtras.h | ||
---|---|---|
301 | This keeps a copy (or move) of all its arguments to allow for the rvalue case in a range-for loop? That would have surprising performance characteristics (implicit whole-container copies) for non-temporary parameters... I think that gets into some pretty major design questions about the behavior here - and the major design problem/question with range proposals in general. We might need to have a broader discussion about the design for anything like this - might be worth moving that part of the conversation to llvm-dev as I expect it'll be a bit more involved. But maybe here's OK & we can rope a few people in. |
(might want to update the description to remove the mention of copy/copy_if now that they've been moved into a separate patch & make the title more specifically about zip range adapter, etc)
include/llvm/ADT/STLExtras.h | ||
---|---|---|
241–243 | Recursive implementations only used in constexpr contexts are fine - but a recursive implementation used in a non-constexpr context may still be implemented recursively by the compiler & is usually best avoided. (this is a problem with constexpr currently - where the ideal implementation for constexpr is not the same as the ideal implementation for non-constexpr contexts) & this isn't being called in a constexpr context, so I'm not sure of the value of flagging it as constexpr or designing it in a way that would be valid constexpr. |
include/llvm/ADT/STLExtras.h | ||
---|---|---|
301 | if by "non-temporary" you mean lvalue input, then no. the corresponding tuple member would be a reference. if it were a copy, the mutability demo in unit test wouldn't work. please correct me if i've misunderstood your question. |
include/llvm/ADT/STLExtras.h | ||
---|---|---|
241–243 | can you find a case where all_true is codegened into a recursive call? |
include/llvm/ADT/STLExtras.h | ||
---|---|---|
241–243 | Can't say I've tried, no. Fair point, though - just not exactly idiomatic (& admittedly variadic templates are on the fringes since they're not often used so idiom is still certainly evolving). Maybe Richard'll have some opinion here too. (or other people - don't mean to limit the review in any way) | |
301 | Ah, indeed. You've not misunderstood - thanks for pointing out the test/explaining. If that's all it takes to solve the temporary/non-temporary issue of range adapters, I'm really happy to hear it. But given my lack of understanding here I'm going to punt to Richard Smith, Chandler Carruth, on the C++ committee & who've been, at least somewhat (more than me, at least) aware of the goings on of range proposals, etc. |
Thanks for the patch!
Just a few drive-by comments. :)
include/llvm/ADT/STLExtras.h | ||
---|---|---|
241–243 | I think constexpr makes some of the compilers we support unhappy; please use LLVM_CONSTEXPR instead. (Also, FWIW, template <typename... Bs> bool all_true(Bs... bools) { return all_of({bool(bools)...}, identity<bool>{}); } would eliminate the potential recursion. Looks like this zip magic is declared above llvm::all_of, though.) | |
245 | Is there a reason we can't just use std::index_sequence/std::make_index_sequence? | |
317 | Please use /// for function/class documentation. | |
325 | and here | |
unittests/Support/IteratorTest.cpp | ||
101 ↗ | (On Diff #67472) | Can we have one or more tests for zip_first, please? :) |
include/llvm/ADT/STLExtras.h | ||
---|---|---|
241–243 | this is good. i'm going to steal this, if you don't mind. | |
245 | according to http://en.cppreference.com/w/cpp/utility/integer_sequence it's c++14, whereas i'm aiming for c++11. |
- factor out mutability tests; test zip_first
- use triple slashes for fn doc comments
- replace potentially recursive fn with llvm::all_of
What is the status of this? I ended up writing my own zip implementation (see D25109) before it was pointed out to me that this already exists, but I see there has been no activity in some time.
It looks like this is waiting for comments from chandlerc@ and rsmith@?
include/llvm/ADT/STLExtras.h | ||
---|---|---|
245 | yes but we have llvm::integer_sequence in this very file. Can we use that instead? | |
251 | For STL-like classes and functions, I believe we prefer to follow STL naming conventions. So I would imagine this should be zip_first |
If it increases code we shouldn't do it, I thought it would reduce lines of code because I thought I remembered that you had rolled something similar to apply on your own. Sorry for the bad suggestion :-/
Looks fine here too. Feel free to remove the apply() if you think it makes the code shorter or better.
The two main problems are that:
- apply_tuple is defined at the bottom of the file which necessitates some very large forward decls.
- I don't know of a direct way to invoke apply on a ctor without a wrapper func.
If the entire patch were moved to the bottom, then the clunky decls could be left out. However, zip would be separated from the rest of the iterator adapter definitions. Do you think this is a worthwhile adjustment?
- revert from using apply_tuple.
- follow stdlib naming conventions for the support classes in detail.
Recursive implementations only used in constexpr contexts are fine - but a recursive implementation used in a non-constexpr context may still be implemented recursively by the compiler & is usually best avoided. (this is a problem with constexpr currently - where the ideal implementation for constexpr is not the same as the ideal implementation for non-constexpr contexts)
& this isn't being called in a constexpr context, so I'm not sure of the value of flagging it as constexpr or designing it in a way that would be valid constexpr.