AcademySoftwareFoundation / AcademySoftwareFoundation/openvdb
[REQUEST] Provide C++20 range API for iteration
- Dominant language
- C++
- Stars
- 3.4k
- Forks
- 774
- Avg merge
- 3d 9h
- Merged PRs (30d)
- 34
Description
### 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
```cpp
//! C++20-style end sentinel for an OpenVDB iterator, which is falsey at the end of its range.
template
struct EndSentinel {
[[nodiscard]] friend constexpr bool operator==(EndSentinel, const VdbIter& it) { return !static_cast(it); }
};
```
then we could use [`std::ranges::subrange`](https://en.cppreference.com/w/cpp/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
1. Provide an end-sentinel that tests the iterator for falsiness.
2. Provide a function to turn an OpenVDB iterator into a `std::ranges::subrange` from the iterator to the end of range.
3. Optionally: Provide an adaptor that views a range as its iterators rather than values, so we can do things like
```cpp
auto bboxes = std::ranges::to(
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
```cpp
for (auto it = node.beginValueOn(); it; ++it) {
if (pred(it)) {
return true;
}
}
return false;
```
became
```cpp
return std::ranges::any_of(viewOfIterators(viewValueOn(node)), pred);
```
and with this suggestion, it could even be
```cpp
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`.
Contributor guide
Research direction
Start by reviewing the existing OpenVDB iterator API, including node.beginValueOn(), node.valuesOn(), and node.valuesAll(), then compare the requested C++20 std::ranges::subrange and sentinel behavior. Define the supported range and iterator interfaces before implementing the sentinel and conversion function; done means OpenVDB iterators work with the requested ranges examples and have corresponding tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100