Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Remove virtual from Get/Set macro definitions #2785

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

N-Dekker
Copy link
Contributor

@N-Dekker N-Dekker commented Oct 7, 2021

Declared Get/Set member functions non-virtual by default when ITK_FUTURE_LEGACY_REMOVE is set.

Added itkVirtual and itkNonVirtual Get/Set macro's, to explicitly choose between virtual and non-virtual.

Observed a significant reduction in the size of generated "ITK*.lib" files when declaring Get/Set member functions non-virtual by default. For example, "ITKTransformFactory-5.3.lib" went from 18.3 MB down to 17.5 MB, and "ITKCommon-5.3.lib" went from 8.1 MB down to 7.6 MB, using VS2019 (Release).

@N-Dekker N-Dekker requested a review from jhlegarreta October 7, 2021 10:23
@github-actions github-actions bot added area:Core Issues affecting the Core module area:Filtering Issues affecting the Filtering module area:IO Issues affecting the IO module area:Numerics Issues affecting the Numerics module area:Registration Issues affecting the Registration module area:Segmentation Issues affecting the Segmentation module area:Video Issues affecting the Video module labels Oct 7, 2021
@N-Dekker
Copy link
Contributor Author

N-Dekker commented Oct 7, 2021

@jhlegarreta I see now, this PR partially overlaps with 10fce06 from your pull request #2568 "STYLE: Use macros to Set/Get ivars in Common operators"

@N-Dekker N-Dekker changed the title Remove virtual from Get/Set macros Remove virtual from Get/Set macro definitions Oct 7, 2021
@N-Dekker N-Dekker force-pushed the Remove-virtual-from-Get-and-Set-macros branch 2 times, most recently from 816b8b4 to 832c78f Compare October 7, 2021 13:21
@jhlegarreta
Copy link
Member

jhlegarreta commented Oct 7, 2021

@jhlegarreta

  • I did not want to change the name of the current macros to contain virtual in their names. This affects virtually all header files.

My proposal is to leave the existing macro calls unchanged, except for the very few that must be virtual, because they actually have an override (in a derived class). Only those that really need to be virtual should call the corresponding itkVirtual macro.

  • When does one know that a class will not be subclassed and thus the non-virtual macro should be used? If this estimation is inaccurate, and the class and the Set/Get methods need to be made virtual, we would fall into what this comment boldly discouraged/refused.

I think it is rarely necessary to override a Get or a Set member function. But it should really be an explicit design choice to do so. When would you override a Get or a Set member function? Honestly, I did encounter a few cases where a virtual Get or Set was expected: "COMP: Replace Get/Set macro calls by the corresponding itkVirtual call"

  • This should be made crystal clear in the ITK SW Guide (and I feel it cannot be left to criteria of the contributor/clear guidelines are required).

Agreed!

@blowekamp
Copy link
Member

This change is not going to be backwards compatible, and is not an appropriate change to ITK API for a minor release.

I am concerned with the frequency of aggressive changes that are not backwards compatible being proposed.

@N-Dekker
Copy link
Contributor Author

N-Dekker commented Oct 7, 2021

This change is not going to be backwards compatible, and is not an appropriate change to ITK API for a minor release.

Thanks @blowekamp but I hope we may still agree that this change is a step in the right direction 😃 Do you agree that given our experience over the last few years, declaring Set and Get member functions non-virtual by default is in principle the right thing to do?

It's not just an improvement of performance, and a reduction of the size of the lib files, it also reduces the code complexity. I mean:

Does ++m_CurrentIteration semantically do the same as:

this->SetCurrentIteration( this->GetCurrentIteration() + 1 );

?

When the Set and Get member functions are non-virtual, the answer is obviously yes. But what if they are virtual?

@blowekamp
Copy link
Member

Do you agree that given our experience over the last few years, declaring Set and Get member functions non-virtual by default is in principle the right thing to do?

Broadly, across the whole toolkit without consideration for base classes and derived usages, I say: no. For that case of derived filter's "parameters" and other internal variables, maybe. However, one of the original goals of ITK's design was that developers and researchers could take the ITK implementation and derived a custom class with experimental or custom algorithm for changes improvements. Virtual setters and getters are needed for the appropriate polymorphic behavior.

Regarding the change is size of the libraries, what is the difference in size of the minsize build type? I believe this build type strips some of the strings of the names of the functions so it may be less of a difference.

Copy link
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.

I agree with Brad that this is somewhat of a big change. If doing it now, make it FUTURE_LEGACY, which would graduate into LEGACY at time of 6.0. And, of course, wait until 5.3 final is out before merging it.

For virtual dispatches to work correctly, the setters must be invoked consistently (instead of setting the ivars directly). Because there are not that many instances where virtual setters are overridden, the consistency in calling the setters is absent. So there is a case for making the setters non-virtual by default. Also, this is an open source library which is open to contributions. If someone needs some class' functionality to be virtualized, they can propose a PR which makes some or all of the setters virtual.

@N-Dekker
Copy link
Contributor Author

N-Dekker commented Oct 8, 2021

@blowekamp

Regarding the change is size of the libraries, what is the difference in size of the minsize build type? I believe this build type strips some of the strings of the names of the functions so it may be less of a difference.

For a MinSizeRel VS2019 build, ITK lib files are also significantly smaller when removing virtual from the Get and Set macro's.

"ITKTransformFactory-5.3.lib" before removing virtual: 18.8 MB
"ITKTransformFactory-5.3.lib" after removing virtual: 17.9 MB
"ITKCommon-5.3.lib" before removing virtual: 9.37 MB
"ITKCommon-5.3.lib" after removing virtual: 8.92 MB

@N-Dekker N-Dekker force-pushed the Remove-virtual-from-Get-and-Set-macros branch from 832c78f to 6cd61e1 Compare October 8, 2021 10:25
@N-Dekker
Copy link
Contributor Author

N-Dekker commented Oct 8, 2021

@dzenanz

I agree with Brad that this is somewhat of a big change. If doing it now, make it FUTURE_LEGACY, which would graduate into LEGACY at time of 6.0.

OK, thanks Dženan. I just made the removal "ITK_FUTURE_LEGACY_REMOVE-only", with the latest force-push, please check!

Copy link
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.

LGTM

commit 8f26661 creates blob 12398f35ac335d57dafd510e044b91bd28b4d29f at Modules/Core/Common/include/itkMacro.h with size 101090 bytes (98.72 KiB) which is greater than the maximum size 100000 bytes (97.66 KiB). If the file is intended to be committed, set the hooks-max-size attribute on its path.

Wow, itkMacro.h is 100KB! This might be the default limit, so we will need a special entry in .gitattributes for it.

@N-Dekker
Copy link
Contributor Author

N-Dekker commented Oct 8, 2021

LGTM

Thanks @dzenanz!

Wow, itkMacro.h is 100KB!

I think we can save a considerable amount of bytes by removing all those trailing spaces 😺 Look here, for example:

#define itkSimpleNewMacro(x)                                                                                           \
  static Pointer New()                                                                                                 \
  {                                                                                                                    \
    Pointer smartPtr = ::itk::ObjectFactory<x>::Create();                                                              \
    if (smartPtr == nullptr)                                                                                           \
    {                                                                                                                  \
      smartPtr = new x;                                                                                                \
    }                                                                                                                  \
    smartPtr->UnRegister();                                                                                            \
    return smartPtr;                                                                                                   \
  }                                                                                                                    \
 ITK_MACROEND_NOOP_STATEMENT

Could be reduced to:

#define itkSimpleNewMacro(x)                              \
  static Pointer New()                                    \
  {                                                       \
    Pointer smartPtr = ::itk::ObjectFactory<x>::Create(); \
    if (smartPtr == nullptr)                              \
    {                                                     \
      smartPtr = new x;                                   \
    }                                                     \
    smartPtr->UnRegister();                               \
    return smartPtr;                                      \
  }                                                       \
 ITK_MACROEND_NOOP_STATEMENT

Or maybe even:

#define itkSimpleNewMacro(x) \
  static Pointer New() \
  { \
    Pointer smartPtr = ::itk::ObjectFactory<x>::Create(); \
    if (smartPtr == nullptr) \
    { \
      smartPtr = new x; \
    } \
    smartPtr->UnRegister(); \
    return smartPtr; \
  } \
 ITK_MACROEND_NOOP_STATEMENT

What do you think?

As a last resort, we may also convert the indentation style from spaces to tabs 😺😺😺

@dzenanz
Copy link
Member

dzenanz commented Oct 8, 2021

Option 2 is reasonable, but does clang-format fight against it?

Also, I was not complaining. I was merely being wowed by how big that file has gotten 😄

N-Dekker added a commit to N-Dekker/ITK that referenced this pull request Oct 8, 2021
Aims to fix:

> commit 8f26661 creates blob [...] at Modules/Core/Common/include/itkMacro.h
> with size 101090 bytes (98.72 KiB) which is greater than the maximum
> size 100000 bytes (97.66 KiB). If the file is intended to be
> committed, set the hooks-max-size attribute on its path.

From pull request InsightSoftwareConsortium#2785
"Remove virtual from Get/Set macro definitions"
N-Dekker added a commit to N-Dekker/ITK that referenced this pull request Oct 8, 2021
Aligns escaped (`\`) newlines as far left as possible, according to
https://clang.llvm.org/docs/ClangFormatStyleOptions.html
Applies especially to macro definitions.

Aims to fix a kwrobot-v1/ghostflow-check-master error at pull request
InsightSoftwareConsortium#2785
"Remove virtual from Get/Set macro definitions", as reported by
Dženan Zukić:

> commit 8f26661 creates blob [...] at Modules/Core/Common/include/itkMacro.h
> with size 101090 bytes (98.72 KiB) which is greater than the maximum
> size 100000 bytes (97.66 KiB). If the file is intended to be
> committed, set the hooks-max-size attribute on its path.
N-Dekker added a commit to N-Dekker/ITK that referenced this pull request Oct 8, 2021
Aligns escaped (`\`) newlines as far left as possible, according to
https://clang.llvm.org/docs/ClangFormatStyleOptions.html
Applies especially to macro definitions.

Aims to fix a kwrobot-v1/ghostflow-check-master error at pull request
InsightSoftwareConsortium#2785
"Remove virtual from Get/Set macro definitions", as reported by
Dženan Zukić:

> commit 8f26661 creates blob [...] at Modules/Core/Common/include/itkMacro.h
> with size 101090 bytes (98.72 KiB) which is greater than the maximum
> size 100000 bytes (97.66 KiB). If the file is intended to be
> committed, set the hooks-max-size attribute on its path.
N-Dekker added a commit to N-Dekker/ITK that referenced this pull request Oct 8, 2021
Aligns escaped (`\`) newlines as far left as possible, according to
https://clang.llvm.org/docs/ClangFormatStyleOptions.html
Applies especially to macro definitions.

Aims to fix a kwrobot-v1/ghostflow-check-master error at pull request
InsightSoftwareConsortium#2785
"Remove virtual from Get/Set macro definitions", as reported by
Dženan Zukić:

> commit 8f26661 creates blob [...] at Modules/Core/Common/include/itkMacro.h
> with size 101090 bytes (98.72 KiB) which is greater than the maximum
> size 100000 bytes (97.66 KiB). If the file is intended to be
> committed, set the hooks-max-size attribute on its path.
N-Dekker added a commit to N-Dekker/ITK that referenced this pull request Oct 8, 2021
Aligns escaped (`\`) newlines as far left as possible, according to
https://clang.llvm.org/docs/ClangFormatStyleOptions.html
Applies especially to macro definitions.

Aims to fix a kwrobot-v1/ghostflow-check-master error at pull request
InsightSoftwareConsortium#2785
"Remove virtual from Get/Set macro definitions", as reported by
Dženan Zukić:

> commit 8f26661 creates blob [...] at Modules/Core/Common/include/itkMacro.h
> with size 101090 bytes (98.72 KiB) which is greater than the maximum
> size 100000 bytes (97.66 KiB). If the file is intended to be
> committed, set the hooks-max-size attribute on its path.
N-Dekker added a commit to N-Dekker/ITK that referenced this pull request Oct 8, 2021
Aligns escaped (`\`) newlines as far left as possible, according to
https://clang.llvm.org/docs/ClangFormatStyleOptions.html
Applies especially to macro definitions.

Aims to fix a kwrobot-v1/ghostflow-check-master error at pull request
InsightSoftwareConsortium#2785
"Remove virtual from Get/Set macro definitions", as reported by
Dženan Zukić:

> commit 8f26661 creates blob [...] at Modules/Core/Common/include/itkMacro.h
> with size 101090 bytes (98.72 KiB) which is greater than the maximum
> size 100000 bytes (97.66 KiB). If the file is intended to be
> committed, set the hooks-max-size attribute on its path.

Manually adjusted the six "clang-format off" sections in itkMacro.h according to
the clang-format preferences.
N-Dekker added a commit to N-Dekker/ITK that referenced this pull request Oct 11, 2021
Aligns escaped (`\`) newlines as far left as possible, according to
https://clang.llvm.org/docs/ClangFormatStyleOptions.html
Applies especially to macro definitions.

Aims to fix a kwrobot-v1/ghostflow-check-master error at pull request
InsightSoftwareConsortium#2785
"Remove virtual from Get/Set macro definitions", as reported by
Dženan Zukić:

> commit 8f26661 creates blob [...] at Modules/Core/Common/include/itkMacro.h
> with size 101090 bytes (98.72 KiB) which is greater than the maximum
> size 100000 bytes (97.66 KiB). If the file is intended to be
> committed, set the hooks-max-size attribute on its path.

Manually adjusted the six "clang-format off" sections in itkMacro.h according to
the clang-format preferences.
N-Dekker added a commit that referenced this pull request Oct 13, 2021
Aligns escaped (`\`) newlines as far left as possible, according to
https://clang.llvm.org/docs/ClangFormatStyleOptions.html
Applies especially to macro definitions.

Aims to fix a kwrobot-v1/ghostflow-check-master error at pull request
#2785
"Remove virtual from Get/Set macro definitions", as reported by
Dženan Zukić:

> commit 8f26661 creates blob [...] at Modules/Core/Common/include/itkMacro.h
> with size 101090 bytes (98.72 KiB) which is greater than the maximum
> size 100000 bytes (97.66 KiB). If the file is intended to be
> committed, set the hooks-max-size attribute on its path.

Manually adjusted the six "clang-format off" sections in itkMacro.h according to
the clang-format preferences.
@N-Dekker N-Dekker force-pushed the Remove-virtual-from-Get-and-Set-macros branch from 6cd61e1 to 970aa45 Compare October 13, 2021 16:24
@N-Dekker N-Dekker marked this pull request as ready for review October 13, 2021 21:15
@thewtex thewtex added this to the ITK 6.0.0 milestone Oct 20, 2021
Declared the member functions defined by Get/Set macro's non-virtual,
when `ITK_FUTURE_LEGACY_REMOVE` is set. (Otherwise those member
functions will still be `virtual`.)
Allowed specifying explicitly whether or not a Get/Set member function
should be declared `virtual`.
Only for those Get/Set member functions that actually have an override.
@N-Dekker N-Dekker force-pushed the Remove-virtual-from-Get-and-Set-macros branch from 970aa45 to 21d3a54 Compare October 25, 2021 22:21
@N-Dekker N-Dekker mentioned this pull request Feb 19, 2022
@stale
Copy link

stale bot commented Apr 16, 2022

This issue has been automatically marked as stale because it has not had recent activity. Thank you for your contributions.

@stale stale bot added the status:Use_Milestone_Backlog Use "Backlog" milestone instead of label for issues without a fixed deadline label Apr 16, 2022
@hjmjohnson hjmjohnson removed the status:Use_Milestone_Backlog Use "Backlog" milestone instead of label for issues without a fixed deadline label Apr 17, 2022
@hjmjohnson
Copy link
Member

@N-Dekker After years in the making, this is finally ready to be updated (rebased) and reconsidered!

@hjmjohnson
Copy link
Member

@N-Dekker Now that we are at ITK 6.0, can we revisit this? It seems like now is the time to put effort into evaluating this effort.

@N-Dekker
Copy link
Contributor Author

Now that we are at ITK 6.0, can we revisit this?

@hjmjohnson I have to think about it. Over the last few years (after submitting this PR) I did encounter some cases where Get and/or Set member functions were overridden. I don't really like that, I think it's error prone. (What if someone overrides the Get, but forgets to override the corresponding Set?) And when a class has a specific data member allocated for a Get and Set, it seems wasteful to override it to bypass the allocated data. But having said so, I know now that this feature of overridable Get and Set is actually being used in practice. So I hesitate. 🤔

@hjmjohnson
Copy link
Member

hjmjohnson commented Nov 11, 2024 via email

@hjmjohnson hjmjohnson modified the milestones: ITK 6.0.0, ITK 6.0 Beta 1 Jan 24, 2025
@dzenanz
Copy link
Member

dzenanz commented Jan 24, 2025

Maybe mark this PR as draft until you make up your mind?

@N-Dekker N-Dekker marked this pull request as draft January 26, 2025 21:12
@N-Dekker N-Dekker changed the title Remove virtual from Get/Set macro definitions WIP: Remove virtual from Get/Set macro definitions Jan 26, 2025
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 area:IO Issues affecting the IO module area:Numerics Issues affecting the Numerics module area:Registration Issues affecting the Registration module area:Segmentation Issues affecting the Segmentation module area:Video Issues affecting the Video module
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants