Skip to content

BUG: Remove shadow declarations of NumberOfClasses in RGBGibbsPriorFilter#6254

Merged
hjmjohnson merged 2 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:rgbgibbs-shadow-fix
May 12, 2026
Merged

BUG: Remove shadow declarations of NumberOfClasses in RGBGibbsPriorFilter#6254
hjmjohnson merged 2 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:rgbgibbs-shadow-fix

Conversation

@hjmjohnson
Copy link
Copy Markdown
Member

Remove RGBGibbsPriorFilter's redeclarations of m_NumberOfClasses and m_MaximumNumberOfIterations that shadowed the inherited MRFImageFilter members, along with the 4 hand-written accessor overrides that existed only to redirect at the shadow. New GTest suite confirms zero observable API change (defaults, Set/Get via subclass and base pointer, MTime semantics).

The shadow and why it was wrong

itkRGBGibbsPriorFilter.h declared:

unsigned int m_NumberOfClasses{ 0 };        // shadows MRFImageFilter::m_NumberOfClasses (also 0)
unsigned int m_MaximumNumberOfIterations{ 10 };  // shadows MRFImageFilter::m_MaximumNumberOfIterations (default 50)

And four hand-written accessor overrides whose only purpose was to direct this->m_* at the subclass shadow rather than the inherited base member:

void SetNumberOfClasses(const unsigned int numberOfClasses) override { ... }
unsigned int GetNumberOfClasses() const override { ... }
void SetMaximumNumberOfIterations(const unsigned int numberOfIterations) override { ... }
unsigned int GetMaximumNumberOfIterations() const override { ... }

Virtual dispatch through the base pointer made the shadow work correctly for Set/Get callers, but the design carried real costs:

  • Every instance held two orphaned 8-byte fields (the inherited base members were never read or written, because RGBGibbsPriorFilter::GenerateData() is a complete override that never calls Superclass::GenerateData()).
  • Any future MRFImageFilter method that read m_NumberOfClasses or m_MaximumNumberOfIterations directly would silently see stale defaults for RGBGibbsPriorFilter instances — a regression hazard hidden by virtual dispatch.
  • m_NumberOfClasses{0} in both classes meant one of the shadows was purely accidental — only the MaximumNumberOfIterations shadow served the design goal of changing the default from 50 to 10.
The fix
  1. Delete the 4 redundant accessor overrides in itkRGBGibbsPriorFilter.h. The inherited MRFImageFilter::SetNumberOfClasses and friends (declared via itkVirtualSetMacro / itkGetConstMacro) now operate on the single inherited member.
  2. Delete the shadow member declarations in itkRGBGibbsPriorFilter.h.
  3. Preserve the subclass-specific default MaximumNumberOfIterations = 10 (vs base default 50) by calling the inherited setter from the RGBGibbsPriorFilter constructor: this->SetMaximumNumberOfIterations(10);. m_NumberOfClasses default 0 matches the base, so no constructor work is needed.
  4. Drop 2 redundant PrintSelf lines that re-printed NumberOfClasses and MaximumNumberOfIterations already emitted by Superclass::PrintSelf.
  5. Switch the one direct-field reference in .hxx (m_ClassifierPtr->SetNumberOfClasses(m_NumberOfClasses);) to this->GetNumberOfClasses() — the inherited member is private in the base, so the public accessor is the right entry point.

Net diff: −43 lines in the header/hxx, +2 lines.

Test methodology — characterize-then-refactor

A new itkRGBGibbsPriorFilterGTest.cxx was added before the fix and committed first, so the test suite establishes the observable API contract independent of the code change. The fix commit is then verified non-regressive against that suite.

The 9 GTests cover:

  • RGBGibbsPriorFilter.DefaultNumberOfClassesIsZero — default 0
  • RGBGibbsPriorFilter.DefaultMaximumNumberOfIterationsIsTen — default 10 (the subclass-specific value)
  • MRFImageFilter.DefaultMaximumNumberOfIterationsIsFifty — base default 50 (documents the contract difference)
  • RGBGibbsPriorFilter.SetGetNumberOfClassesViaSubclass
  • RGBGibbsPriorFilter.SetGetMaximumNumberOfIterationsViaSubclass
  • RGBGibbsPriorFilter.SetGetNumberOfClassesViaBasePointer — verifies virtual dispatch through MRFImageFilter*
  • RGBGibbsPriorFilter.SetGetMaximumNumberOfIterationsViaBasePointer — same
  • RGBGibbsPriorFilter.ModifiedTimeAdvancesOnNumberOfClassesChangeMTime advances when the value changes
  • RGBGibbsPriorFilter.ModifiedTimeUnchangedOnIdenticalAssignmentMTime unchanged on no-op assignment

All 9 pass on the pre-fix tree (commit 1) and the post-fix tree (commit 2). The pre-existing itkRGBGibbsPriorFilterTest image-pipeline CTest also continues to pass — RGBGibbsPriorFilter::GenerateData() now operates on the inherited fields end-to-end.

Local verification
  • pre-commit run --all-files clean on both commits.
  • Full ITK ninja build: 0 real errors (the only failures are pre-existing KWStyle ExternalProject SDK issues unrelated to this PR).
  • ctest -R "MarkovRandomFields|RGBGibbs|MRFImageFilter": 13/13 pass (9 new GTests + 2 framework + 2 existing CTests).
  • Broader ctest -R Segmentation: all pass except itkVectorThresholdSegmentationLevelSetImageFilter, which is in Modules/Segmentation/LevelSets/ and has zero file-level dependency on either of the modified files (verified via grep -rln "MRFImageFilter\\|RGBGibbsPriorFilter" Modules/Segmentation/LevelSets/ → no matches). Pre-existing baseline-PNG drift unrelated to this PR.

@github-actions github-actions Bot added type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct area:Segmentation Issues affecting the Segmentation module labels May 11, 2026
Cover defaults, Set/Get round-trip via both subclass and base
pointer, and MTime semantics. Establishes the API contract for the
follow-up shadow-removal commit.
RGBGibbsPriorFilter shadowed MRFImageFilter's m_NumberOfClasses and
m_MaximumNumberOfIterations, requiring 4 redundant accessor overrides
to direct `this->m_*` at the subclass copy. Remove the shadows and
overrides; preserve the subclass-specific default of 10 (vs base 50)
by calling SetMaximumNumberOfIterations(10) in the constructor.

GTest in preceding commit verifies zero observable API change.
@hjmjohnson hjmjohnson force-pushed the rgbgibbs-shadow-fix branch from 619446c to c9e5538 Compare May 11, 2026 23:13
@hjmjohnson
Copy link
Copy Markdown
Member Author

@greptile review

@greptile-apps

This comment was marked as resolved.

@hjmjohnson hjmjohnson marked this pull request as ready for review May 12, 2026 15:23
Copy link
Copy Markdown
Member

@dzenanz dzenanz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good on a glance.

@hjmjohnson hjmjohnson merged commit 3915420 into InsightSoftwareConsortium:main May 12, 2026
19 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Segmentation Issues affecting the Segmentation module type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots 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

Development

Successfully merging this pull request may close these issues.

2 participants