[REQUEST] Provide C++20 range API for iteration
Is your feature request related to a problem? Please describe.
The current OpenVDB iterator API is non-standard and uses testing the truthiness of an iterator to see if it reaches the end of the range. This means that standard algorithms don't work with OpenVDB iterators.
In C++20's ranges library, they use sentinels to expend that traditional definition of iterators to cover things just like this. OpenVDB would just need to provide a sentinel type like
//! C++20-style end sentinel for an OpenVDB iterator, which is falsey at the end of its range.
template <typename VdbIter>
struct EndSentinel {
[[nodiscard]] friend constexpr bool operator==(EndSentinel, const VdbIter& it) { return !static_cast<bool>(it); }
};
then we could use std::ranges::subrange to create a std::rangs-compatible view of things like node.beginValueOn(). That would let us do for (auto& v : node.valuesOn()) { or std::ranges::count_if(node.valuesAll(), isInteresting).
Describe the solution you'd like
- Provide an end-sentinel that tests the iterator for falsiness.
- Provide a function to turn an OpenVDB iterator into a
std::ranges::subrangefrom the iterator to the end of range. - Optionally: Provide an adaptor that views a range as its iterators rather than values, so we can do things like
auto bboxes = std::ranges::to<std::vector>(
viewOfIterators(node.valuesAll())
| std::views::transform([](const auto& it) { return it.getBoundingBox(); })
);
Possibly it makes more sense for 3 to be the default behavior, so the iterators iterate over "values" that have a .getBoundingBox() and .getValue() member function.
Describe alternatives you've considered
I've implemented this as free functions in my codebase and found it much easier to work with: Places we were doing raw loops could easily become algorithms, like
for (auto it = node.beginValueOn(); it; ++it) {
if (pred(it)) {
return true;
}
}
return false;
became
return std::ranges::any_of(viewOfIterators(viewValueOn(node)), pred);
and with this suggestion, it could even be
return std::ranges::any_of(node.valuesOn(), pred);
Additional context
The iterator–sentinel pair notion isn't supported by the classic STL algorithms, so this is most useful with C++20, which is the same version that provides std::ranges::subrange.
Unfortunately openvdb is stuck with C++17 (for now) because it is tied to the vfx platform Cheers,KenOn Feb 28, 2024, at 1:53 PM, Ben FrantzDale @.***> wrote:
Is your feature request related to a problem? Please describe.
The current OpenVDB iterator API is non-standard and uses testing the truthiness of an iterator to see if it reaches the end of the range. This means that standard algorithms don't work with OpenVDB iterators.
In C++20's ranges library, they use sentinels to expend that traditional definition of iterators to cover things just like this. OpenVDB would just need to provide a sentinel type like
//! C++20-style end sentinel for an OpenVDB iterator, which is falsey at the end of its range.
template <typename VdbIter>
struct EndSentinel {
[[nodiscard]] friend constexpr bool operator==(EndSentinel, const VdbIter& it) { return !static_cast
Provide an end-sentinel that tests the iterator for falsiness. Provide a function to turn an OpenVDB iterator into a std::ranges::subrange from the iterator to the end of range. Optionally: Provide an adaptor that views a range as its iterators rather than values, so we can do things like
auto bboxes = std::ranges::tostd::vector( viewOfIterators(node.valuesAll()) | std::views::transform([](const auto& it) { return it.getBoundingBox(); }) ); Possibly it makes more sense for 3 to be the default behavior, so the iterators iterate over "values" that have a .getBoundingBox() and .getValue() member function. Describe alternatives you've considered I've implemented this as free functions in my codebase and found it much easier to work with: Places we were doing raw loops could easily become algorithms, like for (auto it = node.beginValueOn(); it; ++it) { if (pred(it)) { return true; } } return false; became return std::ranges::any_of(viewOfIterators(viewValueOn(node)), pred); and with this suggestion, it could even be return std::ranges::any_of(node.valuesOn(), pred); Additional context The iterator–sentinel pair notion isn't supported by the classic STL algorithms, so this is most useful with C++20, which is the same version that provides std::ranges::subrange.
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you are subscribed to this thread.Message ID: @.***>
Unfortunately openvdb is stuck with C++17 (for now) because it is tied to the vfx platform
This isn't strictly true. We can support C++20 primitives as long as they are ifdef'ed. It will however complicate our CI, though we do already have some basic C++20 CI. I don't see why we couldn't support this suggestion.
For what it’s worth, a simple approximation of std::ranges::subrange is easy (I’m happy to provide it). That gets you to the point of using language-level range-based for loops without C++20.
The biggest question in my mind is if we move toward a more-standard ranges-like world, would it make sense to iterate over values or over proxy objects that provide the "extra" stuff that OpenVDB iterators provide, or iterate over a range of iterators (which is a little weird feeling, although it works and in some ways is clearer (less magical) than iterating over proxy objects).