InsightSoftwareConsortium / InsightSoftwareConsortium/ITK
ENH: Modernize image iterators to eliminate const_cast via VIsConst templating
- Dominant language
- C++
- Stars
- 1.7k
- Forks
- 748
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 64
Description
Replace ITK's parallel `XxxConstIterator` / `XxxIterator` class pairs with a single `XxxIteratorBase` class template, eliminating the `const_cast` escape hatches that the legacy non-const leaves rely on.
Motivation
The legacy iterator family is structured as twin classes — `ImageRegionConstIterator` and `ImageRegionIterator` — where the writable leaf inherits from the const base and uses `const_cast` to recover write access:
```cpp
void Set(const PixelType & value) const {
this->m_PixelAccessorFunctor.Set(
*(const_cast(this->m_Buffer + this->m_Offset)),
value);
}
```
This pattern repeats across the iterator family. It works, but:
- Cast-removed write access is invisible at the type system level — any future bug that lets a `ConstIterator` reach the cast site silently mints a writable pointer to a const buffer.
- Maintaining two parallel headers per iterator (header + .hxx, const + non-const = 4 files) doubles the surface for drift; bug fixes regularly land in only one half of the pair (see commit history for `m_PixelAccessor` vs. `m_PixelAccessorFunctor`).
- Conversion between non-const and const iterators is by inheritance + slicing, which interferes with reverse-iterator construction patterns.
The fix is to collapse each pair into a single class template parameterized on a compile-time `bool VIsConst` flag. Pointer-like members switch element-const via `std::conditional_t`. Write methods (`Set`, non-const `Value`) are SFINAE-gated on `!VIsConst`. The legacy class names are preserved as alias templates so all consumers compile unchanged.
Status: branch modernize-iterators-remove-const-cast
Local branch on `hjmjohnson/ITK` carries 13 ENH commits covering the full image-iterator family:
| Unit | Cluster |
|------|---------|
| 1 | `ImageConstIterator` / `ImageIterator` (root) |
| 2 (×7 pairs) | `ImageIteratorWithIndex` family: Region / Linear / Slice / Random / RandomNonRepeating / RegionExclusion |
| 3 | `ImageScanlineIterator` |
| 4 | `ImageReverseIterator` / `ImageRegionReverseIterator` |
| 5 | `LineIterator` |
| 6 | `ConditionalIterator` + `FloodFilled*` cluster |
| 7 | `ImageRegionIterator` (non-WithIndex) |
Each unit is one self-contained ENH commit. Build is green at 1360/1360 targets; 3270/3270 ctests passed at the last full validation.
Plus the design spike `ENH: Add NeighborhoodIteratorBase spike with runtime tests` which prototypes the same templated base for the neighborhood family but does not yet wire it into the legacy class names.
Out of scope (follow-up PRs)
- **Neighborhood iterators** (`ConstNeighborhoodIterator`, `NeighborhoodIterator`, `ConstShapedNeighborhoodIterator`, `ShapedNeighborhoodIterator`). Four `const_cast` sites remain. Removing them requires more than a templating pass: the legacy `ConstShapedNeighborhoodIterator` *privately inherits from* the writable `NeighborhoodIterator`, so the constructor cannot accept its declared `const ImageType *` without casting. The spike replaces this with composition. That replacement is roughly 1700 lines and warrants its own focused review cycle.
- **Comment-only follow-up**: a small set of legacy docblocks would benefit from rewording, but doing so in this PR would inflate the diff and obscure the structural change. Tracked separately in the working branch.
Contributor guide
Assessment
This issue has not been assessed yet.