Skip to content

ENH: Present the C++17 form of ITK headers in every C++ standard (supersedes #7118) - #7119

Merged
hjmjohnson merged 3 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:enh-cxx17-header-abi
Nov 5, 2026
Merged

hjmjohnson merged 3 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:enh-cxx17-header-abi

Conversation

@hjmjohnson

@hjmjohnson hjmjohnson commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

ITK headers now present one binary interface, the C++17 one, whatever C++ standard includes them, so ITK and every client see the same definitions. This removes the One Definition Rule concern @N-Dekker raised on #7065 without any configure-time machinery. Supersedes #7118, which added too much complexity for very little benefit. Addresses #7116; related cleanup is tracked in #7120.

Changes
  • Conditions that change the ABI take their C++17 form unconditionally. There were two: ITK_UNEQUAL_OPERATOR_MEMBER_FUNCTION in itkMacro.h (a class member, used in about 122 files) and the std::atomic_ref branch in itkParallelSparseFieldLevelSetImageFilter.hxx (bodies of template member functions).
  • Their C++20 forms stay in the source behind ITK_ALPHA_FEATURE_CXX20, an unsupported, testing-only opt-in that records which form to keep when ITK later requires C++20.
  • ITK_ALPHA_FEATURE_CXX20, _CXX23, and _CXX26 are advanced CMake options, off by default, declared in one loop and written to itkConfigure.h with one #cmakedefine each, so ITK and its clients cannot disagree. Turning one on with an older CMAKE_CXX_STANDARD is a configure error. They are deliberately coarse: one switch per standard, found with grep ITK_ALPHA_FEATURE_. _CXX23 and _CXX26 are placeholders with no users yet.
  • Conditions that cannot change the ABI keep their test, and the #if line ends with // ABI-neutral: the ITK_NODISCARD(message) attribute form in itkMacro.h, the <numbers> include and bit-exactness static_asserts in itkMath.h, and the std::endian initializer in itkByteSwapper.h (same value on either path).
  • A pygrep pre-commit hook rejects an unmarked #if on a __cplusplus value, _MSVC_LANG, or __cpp_* in Modules/**/*.h and *.hxx (outside ThirdParty). #ifdef __cplusplus is allowed. It reads single lines only.
  • itkNewerStandardClientTest is compiled with a newer standard than ITK and asserts it still sees the member operator!=. It is a separate executable because it needs its own CXX_STANDARD. It includes only itkMacro.h, so it does not depend on the toolchain's C++20 library headers, and it turns off CMake's C++20 module scanning, which needs a clang-scan-deps that some toolchains lack.
  • Separate commits add the migration guide entry and the agent guidance (AGENTS.md, Documentation/AI/compiler-cautions.md section 16).
Behavior changes to review
Why not #7118

#7118 recorded the build standard as ITK_CXX_ABI and probed three library features at configure time into itkConfigure.h. It needed a level macro, three feature macros, three try_compile probes on every configure, an exported CMake variable, and three tests, to keep code paths that only a C++20 build of ITK would use. Downstream projects are on C++17, so those paths had almost no users. VTK takes the same position as this PR: all code is valid C++17, with no standard gating in headers.

Local verification (macOS arm64, Homebrew LLVM)
  • C++17 tree: the three ITKCommon test drivers and the new client test build; 37/37 related tests pass. The newer client is compiled as C++20. Configuring that tree with ITK_ALPHA_FEATURE_CXX20=ON stops with the expected error.
  • C++20 tree (CMAKE_CXX_STANDARD=20, ITK_LEGACY_REMOVE=ON), switch off: the same targets build with no compiler warnings and 37/37 pass, with the newer client compiled as C++23. This is the check that keeping the member operator!= is clean in C++20.
  • C++20 tree, ITK_ALPHA_FEATURE_CXX20=ON: itkConfigure.h defines the macro, the same targets build with no compiler warnings, and 37/37 pass. The client test then asserts the member operator!= is absent.
  • A separate syntax-only pass over 67 test sources that use ITK_UNEQUAL_OPERATOR_MEMBER_FUNCTION, with -Wambiguous-reversed-operator, was clean under clang at C++20 and C++23 and under GCC 16 at C++20.
  • pre-commit run --all-files passes, including the new hook, which finds no unmarked standard test in any ITK header.
  • Not run here: a full ITK build, Windows, and the level-set test driver, which is not in these build trees, so the std::atomic_ref alpha branch is not compiled.
AI assistance

Claude Code (Fable 5.1) drafted the change and documentation, surveyed how other libraries handle the problem, and ran the local checks. The author reviewed and is responsible for the change.

@github-actions github-actions Bot added type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots 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:Segmentation Issues affecting the Segmentation module area:Documentation Issues affecting the Documentation module labels Nov 5, 2026
@hjmjohnson
hjmjohnson force-pushed the enh-cxx17-header-abi branch from 8f89cd9 to 1263b82 Compare November 5, 2026 17:33
@hjmjohnson

Copy link
Copy Markdown
Member Author

Force-push 8f89cd9 -> 1263b82 is content only (no rebase).

  • The opt-in switches are renamed ITK_ALPHA_FEATURE_CXX20, _CXX23, and _CXX26, so one grep ITK_ALPHA_FEATURE_ finds them all.
  • They are now advanced CMake options, off by default, declared in one loop and recorded in itkConfigure.h with one #cmakedefine each. ITK and its clients therefore cannot disagree, which a plain compiler flag allowed.
  • Built and tested at C++17, at C++20 with the switch off, and at C++20 with ITK_ALPHA_FEATURE_CXX20=ON.

@dzenanz

dzenanz commented Nov 5, 2026

Copy link
Copy Markdown
Member

Is there any other, pre-existing muckery with compiler features that should be refactored into this style?

@hjmjohnson

Copy link
Copy Markdown
Member Author

@dzenanz Yes. I surveyed ITK's own headers, CMake probes, and generated headers for it, and filed the result as #7120 so this PR can stay small.

  • Same kind of problem, selected by compiler flags or build type instead of by standard. The notable one: ITK_COMPILER_SUPPORTS_SSE2_32/64 sit in itkConfigure.h, but the including compiler's flags decide them, so a 32-bit x86 client without SSE2 gets different inline rounding code than ITK. Also ITK_LOCATION under #ifndef NDEBUG, a __GLIBC__ branch in an assertion macro, and a SWIG-only inline member in itkTransform.h. Each needs a decision, not a mechanical edit.
  • Dead branches for SunPro, Borland, MetroWerks, SGI, Cygwin, "GCC 4.3 onwards", and MSVC90, plus an unused ITK_HAS_CPP11_TYPETRAITS.
  • Configure probes that C++17 made obsolete, such as ITK_HAVE_FENV_H and ITK_HAS_FEGETENV, and two apparent probe bugs.

After this PR, the new pre-commit hook finds no unmarked test of __cplusplus, _MSVC_LANG, or a __cpp_* macro in any .h or .hxx outside ThirdParty; the older-client warning in itkConfigure.h.in is the one intended use left. The survey was done by search and each item in #7120 should be re-checked before it changes.

@hjmjohnson
hjmjohnson force-pushed the enh-cxx17-header-abi branch from 1263b82 to b96fd84 Compare November 5, 2026 18:05
@hjmjohnson

Copy link
Copy Markdown
Member Author

Force-push 1263b82 -> b96fd84 is content only (no rebase).

  • The rule is now about binary compatibility only. Conditions that cannot change the ABI keep their original test, and the #if line ends with // ABI-neutral: the ITK_NODISCARD(message) attribute, the <numbers> static_asserts in itkMath.h, and the std::endian initializer in itkByteSwapper.h. Those two headers now differ from main only by that comment.
  • Only the two conditions that change a class member or an inline body sit behind ITK_ALPHA_FEATURE_CXX20: ITK_UNEQUAL_OPERATOR_MEMBER_FUNCTION and the level-set std::atomic_ref branch.
  • The hook is renamed check-no-cxx-standard-tests-in-headers, accepts marked lines, and catches a condition containing a division.
  • Review nits: one-line comment in itkMacro.h, unset CMake temporaries, the migration guide mentions ITK_EXPERIMENTAL_CXX20_REWRITTEN_UNEQUAL_OPERATOR.
  • The code commit credits the design contributors.

@hjmjohnson

Copy link
Copy Markdown
Member Author

Force-push b96fd84 -> 2818f95 is content only (no rebase), no C++ change: the hook requires the // ABI-neutral marker at the end of the line, the agent guide also allows the marker for code that computes the same value either way, and the migration-guide commit message is corrected.

@hjmjohnson
hjmjohnson force-pushed the enh-cxx17-header-abi branch from b96fd84 to 2818f95 Compare November 5, 2026 18:16
@hjmjohnson
hjmjohnson marked this pull request as ready for review November 5, 2026 18:21
@greptile-apps

This comment was marked as resolved.

Comment thread Modules/Core/Common/test/CMakeLists.txt Resolved
Comment thread .pre-commit-config.yaml Resolved
@hjmjohnson

Copy link
Copy Markdown
Member Author

Force-push 2818f95 -> d2c3ce3 is content only (no rebase): itkNewerStandardClientTest sets CXX_SCAN_FOR_MODULES OFF. CMake 3.28+ scans C++20 targets for module dependencies, and the Pixi compiler on macOS has no clang-scan-deps, so the Pixi-Cxx (macos-15) build stopped with CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS-NOTFOUND: command not found.

@hjmjohnson
hjmjohnson force-pushed the enh-cxx17-header-abi branch from 2818f95 to d2c3ce3 Compare November 5, 2026 18:33
hjmjohnson and others added 3 commits November 5, 2026 12:37
ITK supports clients built with a newer C++ standard, or another
compiler, than ITK. Headers that test __cplusplus or a feature-test
macro then give ITK's libraries and the client different definitions of
one class or inline function, which violates the One Definition Rule.

The conditions that change a class member or an inline body now take
their C++17 form unconditionally; newer forms stay behind
ITK_ALPHA_FEATURE_CXX20, an unsupported advanced option recorded in
itkConfigure.h (CXX23 and CXX26 are placeholders). Conditions that
cannot change the ABI keep their test and are marked. A pre-commit hook
rejects the rest. itkNewerStandardClientTest needs its own
CXX_STANDARD, so it is a separate executable.

Co-Authored-By: Niels Dekker <N.Dekker@lumc.nl>
Co-Authored-By: Dženan Zukić <dzenan.zukic@kitware.com>
Co-Authored-By: Bradley Lowekamp <blowekamp@mail.nih.gov>
Projects built with a newer standard than ITK now see ITK's C++17
definitions, and one experimental macro is no longer defined there.
Agents reach for __cplusplus and feature-test macros by default; the
guide states the rule and the reason so new headers follow it.
@hjmjohnson
hjmjohnson force-pushed the enh-cxx17-header-abi branch from d2c3ce3 to c6a059b Compare November 5, 2026 18:38
@hjmjohnson

Copy link
Copy Markdown
Member Author

Force-push d2c3ce3 -> c6a059b is content only (no rebase): addresses both Greptile findings. The newer-standard client test includes only itkMacro.h, and the hook steps over block comments.

@dzenanz dzenanz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Changes look good on a glance.

@hjmjohnson
hjmjohnson merged commit fb15a30 into InsightSoftwareConsortium:main Nov 5, 2026
19 checks passed
// GOOD - write the C++17 form, with no condition.

// ACCEPTABLE - a newer form kept for the day ITK requires that standard:
#if defined(ITK_ALPHA_FEATURE_CXX20)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

First of all, thanks @hjmjohnson ! Regarding the term ALPHA:

  • It suggests to me that it is related to alpha releases of ITK. Is that was it is meant to be?
  • It also suggests that there could be a "beta" follow-up, e.g., ITK_BETA_FEATURE_CXX20. Is that intended?

If both interpretations are wrong, I would rather use a different word, for example EXPERIMENTAL.

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:Documentation Issues affecting the Documentation module area:Segmentation Issues affecting the Segmentation module type:Enhancement Improvement of existing methods or implementation 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.

3 participants