Skip to content

ENH: Select image iterator access by image constness (supersedes #7021) - #7065

Open
hjmjohnson wants to merge 12 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:enh-iterator-const-correctness
Open

hjmjohnson wants to merge 12 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:enh-iterator-const-correctness

Conversation

@hjmjohnson

@hjmjohnson hjmjohnson commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

Closes #6141 and fixes #7003: every image iterator now takes its access from the constness of the image type, so XxxIterator<const ImageType> has no Set() and no writable Value(), and no iterator in the family needs const_cast. Supersedes #7021, whose commit is the first commit here, authored by @mymonin and kept unchanged.

Merge-order note: this conflicts with #7041 in itkImageRandom*IteratorWithIndex (#7041's detail::RandomIndexInRegion moves with the class into itkImageRandomIteratorWithIndex.h); whichever lands second needs that one-hunk rebase. Two bugs that this PR exposes but that also exist on main are fixed separately: #7068 (RandomPermutation assignment loses the shuffled order) and #7069 (reverse region iterator span is one pixel off after SetIndex() or conversion). This PR takes both fixes on rebase.

Design
  • The writable name carries the implementation: ImageRegionIterator<TImage> derives from ImageIterator<TImage>, and pixel pointers are const exactly when TImage is const, so writes need no cast.
  • Set() and non-const Value() are removed for const TImage: SFINAE in C++17, a requires clause when concepts are available, which gives a "constraints not satisfied" diagnostic.
  • ImageRegionConstIterator<T> is a thin class deriving from ImageRegionIterator<const T>, with deduction guides for SmartPointer, ConstPointer, and raw pointers. Each header provides both names, and the Const headers and .hxx files remain as one-line includes.
  • detail::IsImageIteratorConvertible_v is the single statement of allowed conversions: same image type, never dropping const.
  • C++20 compilers get operator== plus operator<=> instead of six comparison operators; C++26 compilers get = delete("…") naming the forbidden read-only to writable conversion. Both are behind feature-test macros (__cpp_concepts, __cpp_impl_three_way_comparison, __cpp_deleted_function).
  • The flood-filled iterators keep the writable image pointer their constructor receives instead of inverting the ConditionalConstIterator hierarchy.
API changes and their measured cost

Removed, each by design:

  • Writing through any iterator over a const image (const-qualified images can be modified via itk::ImageRegionIterator<const ImageType> #7003), including ImageReverseConstIterator::Set(), which was public.
  • Converting a read-only iterator into a writable one (the protected XxxIterator(const XxxConstIterator &) constructors and operator=).
  • The XxxIterator<T> to XxxConstIterator<T> base-class relation: XxxIterator<T>::Superclass is now the parent iterator, and a function taking a non-const XxxConstIterator<T> & no longer accepts an XxxIterator<T>. Conversion to a read-only iterator is by constructor.

Other changes:

  • ImageReverseIterator, ImageRegionReverseIterator, and their Const forms now share one class; ImageReverseConstIterator gains operator++.
  • PathIterator::Value() referred to a nonexistent member and could not compile; it now works.
  • Converting a forward iterator to a region reverse iterator now sets the row span from the current position (the end offset was computed from an unset begin offset). The one-pixel offset in that span is fixed separately in BUG: Keep reverse region iterators inside the region after SetIndex #7069.
  • Kept from ITK 5: ImageIteratorDimension on the indexed iterators, and construction of the exclusion and frequency iterators from a plain ImageIteratorWithIndex.

Blast radius, measured in the downstream build forest (ITK with Python wrapping and this branch, merged locally with #7041 and #7056): ANTs builds and passes 179/179, BRAINSTools builds and passes 458/458, and SimpleITK, RTK, Cleaver, Impact, TractographyTRX, and PerformanceBenchmarking build. The failures, in elastix (vnl_determinant), c3d (removed VNL and orientation APIs), SimpleITKFilters (duplicate target), TubeTK (missing include and members), and several remote modules (deprecated ITK_DISALLOW_COPY_AND_ASSIGN), are identical with main's headers. No downstream code depended on the removed base-class relation or the writable conversions.

Testing
  • itkImageIteratorsGTest: compile-time checks that every writable iterator has Set() for Image<int> and not for const Image<int>, that the Const names have none, and conversion and comparison rules, Value() assignable only for a non-const image, ImageIteratorDimension on the indexed iterators, and construction from ImageIteratorWithIndex.
  • itkFrequencyIteratorsGTest: the four frequency iterators accept any ImageIteratorWithIndex over the same image type.
  • All iterator families compiled with -Wall -Wextra under -std=c++17, c++20, c++23, and c++26 with Apple clang 21 and Homebrew clang 23 (exercising both sides of every #if); no GCC was available locally, so GCC's C++20 path is first exercised by CI.
  • Local full ITK build (macOS arm64, conda clang, Release): 0 new warnings, 6508/6508 tests pass; each of the original 10 commits built on its own. After the review fixes, the full build is clean and the ITKCommon and ITKImageFrequency suites pass 738/738
  • pre-commit run --all-files passes.

AI assistance (Claude Code) carried out the conversion with scripts kept for review, ran the probes, and ran the downstream forest; the author reviewed the changes.

mymonin and others added 4 commits November 1, 2026 06:49
The original inheritance-based design of the iterator class pairs has several
problems, notably the pervasive need to const_cast away the pointer-to-const
member whenever an iterator needs write access. This opens up opportunities
for accidental or deliberate const-correctness violation.

This refactor makes itk::ImageIterator a standalone template class that
models both read-only and read/write access semantics based solely on the
constness of the template type.

ImageConstIterator<T> then functionally acts as an alias template for
ImageIterator<const T>. It is implemented as a minimal derived class due to
CTAD not being available for alias templates pre-C++20.
ImageIterator<const T> converted only from ImageIterator<T>, so the
documented equivalence with ImageConstIterator<T> held one way, and
the comparison operators accepted iterators over unrelated images.
One trait now states the allowed conversion (same image, never
dropping const) for all iterators; comparisons require the same image.

C++20 compilers use requires-clauses and operator<=>, and C++26
compilers name the forbidden read-only to writable conversion.
ImageRegionIterator<T> inherited from ImageRegionConstIterator<T> and
cast away const to write, so ImageRegionIterator<const T> could write
to a const image (InsightSoftwareConsortium#7003). ImageRegionIterator now derives from
ImageIterator<T> and writes only when T is not const;
ImageRegionConstIterator<T> is ImageRegionIterator<const T>, with
deduction guides for smart and raw pointers. Both headers provide
both classes.
Apply the ImageRegionIterator design to the scanline pair, so
ImageScanlineIterator<const T> cannot write and the const_cast in
Set() and Value() goes away.
@github-actions github-actions Bot added type:Enhancement Improvement of existing methods or implementation type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct area:Core Issues affecting the Core module area:Filtering Issues affecting the Filtering module labels Nov 1, 2026
hjmjohnson added a commit to hjmjohnson/ITK that referenced this pull request Nov 1, 2026
# Conflicts:
#	Modules/Core/Common/include/itkImageRandomConstIteratorWithIndex.h
#	Modules/Core/Common/include/itkImageRandomConstIteratorWithIndex.hxx
@hjmjohnson
hjmjohnson marked this pull request as ready for review November 1, 2026 16:53
@greptile-apps

This comment was marked as resolved.

@hjmjohnson
hjmjohnson force-pushed the enh-iterator-const-correctness branch from 614d0e2 to 6dca3f7 Compare November 1, 2026 19:49
hjmjohnson added a commit to hjmjohnson/ITK that referenced this pull request Nov 1, 2026
# Conflicts:
#	Modules/Core/Common/include/itkImageRandomConstIteratorWithIndex.h
#	Modules/Core/Common/include/itkImageRandomConstIteratorWithIndex.hxx
@hjmjohnson

Copy link
Copy Markdown
Member Author

@greptileai please review the latest commits (6dca3f7).

ImageReverseConstIterator offered a public Set() that cast away const,
and the writable reverse iterators accepted read-only sources. The
four reverse classes now share ImageRegionReverseIterator<T>, writable
only when T is not const; the other names derive from it, so every
existing method remains except writes through a const image.

Conversion from a forward iterator now sets the row span from the
current position; before, the span end was computed from a span begin
that was not yet set. The GetBufferPointer test now converts from a
writable iterator, since conversion from a read-only one is removed.
Apply the ImageIterator design to the root of the WithIndex family:
ImageIteratorWithIndex<T> holds the position as a pointer to const
pixels only when T is const, so Set() and Value() need no const_cast
and are absent for const images. ImageConstIteratorWithIndex<T> is
ImageIteratorWithIndex<const T>.
@hjmjohnson
hjmjohnson force-pushed the enh-iterator-const-correctness branch from 6dca3f7 to 1e416e2 Compare November 2, 2026 00:09
hjmjohnson added a commit to hjmjohnson/ITK that referenced this pull request Nov 2, 2026
# Conflicts:
#	Modules/Core/Common/include/itkImageRandomConstIteratorWithIndex.h
#	Modules/Core/Common/include/itkImageRandomConstIteratorWithIndex.hxx
@hjmjohnson

Copy link
Copy Markdown
Member Author

@greptileai please review the latest commits (1e416e2).

Apply the ImageIteratorWithIndex design to the region, linear, slice,
random, random non-repeating, and region exclusion iterators. Each
XxxIteratorWithIndex<T> derives from its writable base, inherits the
constness-gated Set() and Value(), and XxxConstIteratorWithIndex<T>
is XxxIteratorWithIndex<const T>. The random non-repeating conversion
deep-copies the permutation it owns.
The frequency-layout iterators and ReflectiveImageRegionIterator
inherited from read-only bases and cast away const to write. They now
derive from the writable WithIndex iterators, so writes need no cast
and are unavailable for const images; each Const name is the iterator
over the const image.
LineIterator and PathIterator wrote through a const_cast of the
const image held by their read-only bases. Each is now the iterator,
writable only when TImage is not const, and the Const name iterates
over the const image. PathIterator::Value() read a nonexistent
member, so it could not compile; it now returns the current pixel.
The writable flood-filled iterators cast away the const image held by
ConditionalConstIterator to write a pixel. They now keep the writable
image pointer their constructor receives, offer Set() only for
non-const images, and read through the const image without a cast.
Adds compile-time checks that the indexed iterators keep the ITK 5
ImageIteratorDimension constant, that the exclusion iterator can be built
from a plain ImageIteratorWithIndex, and that Value() is assignable only
for iterators over a non-const image.
@hjmjohnson
hjmjohnson force-pushed the enh-iterator-const-correctness branch from 1e416e2 to 3f3d071 Compare November 2, 2026 04:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Core Issues affecting the Core module area:Filtering Issues affecting the Filtering module type:Enhancement Improvement of existing methods or implementation type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct

Projects

None yet

2 participants