#Range adapters: dangling ranges

19 messages · Page 1 of 1 (latest)

rancid brook
#

I want to write a range adapter called progress_view which can wrap any other sized range. It should provide iterators which wrap the wrapped range's iterators, except for the fact that incrementing them calls an internal function to increment a progress object of some sort. The idea is that you should be able to wrap a range-based for loop operand in this progress_view.

for (auto& x : progress_view(my_range, my_progress)); // minimum viable product
for (auto& x : my_range | progress_view(my_progress)); // ideally supporting something like this as well?

The resulting progress_view should be an input_range only, I think. I am not sure what other std::ranges properties it should meet.

I am not understanding the idiomatic and way to implement this while handling temporaries correctly. My first idea was to store a reference/pointer to the wrapped range, but this is no good for temporaries in for loops, right? I could also std::move into the adapter, but this seems a bit clunky. Also, what if the passed range is a temporary but is a view, so taking it by value would be fine? Also also, how should I support pipelining? Is it as straightforward as an overloading with forwarding into a constructor?

Would appreciate any tips in how something like this should be handled! Thank you.

pallid basaltBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

sharp tulip
#

why would you need to worry about temporaries?

#

do you expect that you'd pass a temporary range to it?

#

if all you do is call a function on my_progress on every increment then perhaps you could just implement it using std::views::transform and not by making a custom view from scratch

#
constexpr auto progress(auto& prog) {
  return std::views::transform([prog&](auto&& element) -> auto&& {
    prog.make_progress();
    return std::forward<decltype(element)>(element);
  });
}```
rancid brook
#

I'm worried about temporaries because it's something that could realistically happen and it wouldn't produce very useful diagnostics if it did, but I think I managed to solve this problem by forwarding my argument into a std::views::any_t.

#

The transform idea is very cool but I do need to determine step count before beginning, and I'd also like to return an end iterator early if the progress is cancelled.
I want this to basically implement the entire typical usage case of the object it wraps (create subprogress, loop through, increment, check cancellation and break early)

#

Also I think I worked out that pipelining for custom types is c++23, unless I provide my own pipeline operator which I don't really care to do.

river token
sharp tulip
#

hmm ok doesn’t look that bad

#

on your range adaptor closure you’d want to constrain the range argument of operator() to be a sized range so you can determine the progress steps

#

as for the returned range, if you don’t want to go through the trouble of making your own iterators you could cheat and use std::generator

rancid brook
#

Cool, that makes sense

#

Unfortunately I don't have access to C++23 so I can't do the closure unless I use MSVC implementation details

#

I got something working pretty much as you guys described above but I decided not to take it further because it had a very clumsy API if you actually wanted to use subprogressing features

#

I think std::generator would have been a good saviour on that front but I'm gonna shelve the idea for now 😔 too radical for our codebase and the progress system we have doesn't play so well with it