Skip to content

Commit 59176b1

Browse files
authored
Merge pull request #6234 from hjmjohnson/print-numeric-trait
ENH: Replace PrintSelf NumericTraits-cast boilerplate with itk::print_helper::PrintNumericTrait helper (supersedes #3909)
2 parents b6eb189 + 17a76eb commit 59176b1

223 files changed

Lines changed: 856 additions & 1123 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Modules/Core/Common/include/itkAnnulusOperator.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "itkNeighborhoodOperator.h"
2222
#include "itkVector.h"
23+
#include "itkPrintHelper.h"
2324

2425
namespace itk
2526
{
@@ -225,13 +226,10 @@ class ITK_TEMPLATE_EXPORT AnnulusOperator : public NeighborhoodOperator<TPixel,
225226
os << indent << "Thickness: " << m_Thickness << std::endl;
226227
os << indent << "Normalize: " << m_Normalize << std::endl;
227228
os << indent << "BrightCenter: " << m_BrightCenter << std::endl;
228-
os << indent << "InteriorValue: " << static_cast<typename NumericTraits<PixelType>::PrintType>(m_InteriorValue)
229-
<< std::endl;
230-
os << indent << "AnnulusValue: " << static_cast<typename NumericTraits<PixelType>::PrintType>(m_AnnulusValue)
231-
<< std::endl;
232-
os << indent << "ExteriorValue: " << static_cast<typename NumericTraits<PixelType>::PrintType>(m_ExteriorValue)
233-
<< std::endl;
234-
os << indent << "Spacing: " << static_cast<typename NumericTraits<SpacingType>::PrintType>(m_Spacing) << std::endl;
229+
print_helper::PrintNumericTrait(os, indent, "InteriorValue", m_InteriorValue);
230+
print_helper::PrintNumericTrait(os, indent, "AnnulusValue", m_AnnulusValue);
231+
print_helper::PrintNumericTrait(os, indent, "ExteriorValue", m_ExteriorValue);
232+
print_helper::PrintNumericTrait(os, indent, "Spacing", m_Spacing);
235233
}
236234

237235
protected:

Modules/Core/Common/include/itkBinaryThresholdSpatialFunction.hxx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define itkBinaryThresholdSpatialFunction_hxx
2020

2121

22+
#include "itkPrintHelper.h"
2223
namespace itk
2324
{
2425

@@ -28,12 +29,8 @@ BinaryThresholdSpatialFunction<TFunction>::PrintSelf(std::ostream & os, Indent i
2829
{
2930
Superclass::PrintSelf(os, indent);
3031

31-
os << indent
32-
<< "LowerThreshold: " << static_cast<typename NumericTraits<FunctionOutputType>::PrintType>(m_LowerThreshold)
33-
<< std::endl;
34-
os << indent
35-
<< "UpperThreshold: " << static_cast<typename NumericTraits<FunctionOutputType>::PrintType>(m_UpperThreshold)
36-
<< std::endl;
32+
print_helper::PrintNumericTrait(os, indent, "LowerThreshold", m_LowerThreshold);
33+
print_helper::PrintNumericTrait(os, indent, "UpperThreshold", m_UpperThreshold);
3734

3835
itkPrintSelfObjectMacro(Function);
3936
}

Modules/Core/Common/include/itkImageDuplicator.hxx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define itkImageDuplicator_hxx
2020

2121
#include "itkImageAlgorithm.h"
22+
#include "itkPrintHelper.h"
2223

2324
namespace itk
2425
{
@@ -64,8 +65,7 @@ ImageDuplicator<TInputImage>::PrintSelf(std::ostream & os, Indent indent) const
6465
itkPrintSelfObjectMacro(InputImage);
6566
itkPrintSelfObjectMacro(DuplicateImage);
6667

67-
os << indent << "InternalImageTime: " << static_cast<NumericTraits<ModifiedTimeType>::PrintType>(m_InternalImageTime)
68-
<< std::endl;
68+
print_helper::PrintNumericTrait(os, indent, "InternalImageTime", m_InternalImageTime);
6969
}
7070
} // end namespace itk
7171

Modules/Core/Common/include/itkMinimumMaximumImageCalculator.hxx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define itkMinimumMaximumImageCalculator_hxx
2020

2121
#include "itkImageRegionConstIteratorWithIndex.h"
22+
#include "itkPrintHelper.h"
2223

2324
namespace itk
2425
{
@@ -113,8 +114,8 @@ MinimumMaximumImageCalculator<TInputImage>::PrintSelf(std::ostream & os, Indent
113114
{
114115
Superclass::PrintSelf(os, indent);
115116

116-
os << indent << "Minimum: " << static_cast<typename NumericTraits<PixelType>::PrintType>(m_Minimum) << std::endl;
117-
os << indent << "Maximum: " << static_cast<typename NumericTraits<PixelType>::PrintType>(m_Maximum) << std::endl;
117+
print_helper::PrintNumericTrait(os, indent, "Minimum", m_Minimum);
118+
print_helper::PrintNumericTrait(os, indent, "Maximum", m_Maximum);
118119
os << indent << "Index of Minimum: " << m_IndexOfMinimum << std::endl;
119120
os << indent << "Index of Maximum: " << m_IndexOfMaximum << std::endl;
120121
itkPrintSelfObjectMacro(Image);

Modules/Core/Common/include/itkNeighborhood.hxx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ void
117117
Neighborhood<TPixel, VDimension, TContainer>::PrintSelf(std::ostream & os, Indent indent) const
118118
{
119119
using namespace itk::print_helper;
120-
os << indent << "Size: " << static_cast<typename NumericTraits<SizeType>::PrintType>(m_Size) << std::endl;
121-
os << indent << "Radius: " << static_cast<typename NumericTraits<SizeType>::PrintType>(m_Radius) << std::endl;
120+
print_helper::PrintNumericTrait(os, indent, "Size", m_Size);
121+
print_helper::PrintNumericTrait(os, indent, "Radius", m_Radius);
122122
os << indent << "StrideTable: " << m_StrideTable << std::endl;
123123
os << indent << "OffsetTable: " << m_OffsetTable << std::endl;
124124
}

Modules/Core/Common/include/itkObjectStore.hxx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,8 @@ ObjectStore<TObjectType>::PrintSelf(std::ostream & os, Indent indent) const
134134
Superclass::PrintSelf(os, indent);
135135

136136
os << indent << "GrowthStrategy: " << m_GrowthStrategy << std::endl;
137-
os << indent << "Size: " << static_cast<NumericTraits<SizeValueType>::PrintType>(m_Size) << std::endl;
138-
os << indent << "LinearGrowthSize: " << static_cast<NumericTraits<SizeValueType>::PrintType>(m_LinearGrowthSize)
139-
<< std::endl;
137+
print_helper::PrintNumericTrait(os, indent, "Size", m_Size);
138+
print_helper::PrintNumericTrait(os, indent, "LinearGrowthSize", m_LinearGrowthSize);
140139
os << indent << "FreeList: " << m_FreeList << std::endl;
141140
}
142141
} // end namespace itk

Modules/Core/Common/include/itkPointSetToImageFilter.hxx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "itkBoundingBox.h"
2323
#include "itkNumericTraits.h"
2424
#include "itkMath.h"
25+
#include "itkPrintHelper.h"
2526

2627
namespace itk
2728
{
@@ -227,10 +228,8 @@ PointSetToImageFilter<TInputPointSet, TOutputImage>::PrintSelf(std::ostream & os
227228
os << indent << "Origin: " << m_Origin << std::endl;
228229
os << indent << "Spacing: " << m_Spacing << std::endl;
229230
os << indent << "Direction: " << m_Direction << std::endl;
230-
os << indent << "Inside Value : " << static_cast<typename NumericTraits<ValueType>::PrintType>(m_InsideValue)
231-
<< std::endl;
232-
os << indent << "Outside Value : " << static_cast<typename NumericTraits<ValueType>::PrintType>(m_OutsideValue)
233-
<< std::endl;
231+
print_helper::PrintNumericTrait(os, indent, "Inside Value ", m_InsideValue);
232+
print_helper::PrintNumericTrait(os, indent, "Outside Value ", m_OutsideValue);
234233
}
235234
} // end namespace itk
236235

Modules/Core/Common/include/itkPrintHelper.h

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#ifndef itkPrintHelper_h
2020
#define itkPrintHelper_h
2121

22+
#include "itkIndent.h"
23+
2224
#include <array>
2325
#include <iostream>
2426
#include <iterator>
@@ -27,13 +29,27 @@
2729
#include <type_traits>
2830

2931

32+
namespace itk
33+
{
34+
// Forward declaration so itkPrintHelper.h can be safely included from
35+
// itkMacro.h without re-entering itkNumericTraits.h (which itself uses
36+
// macros defined later in itkMacro.h). Every PrintNumericTrait() call
37+
// site needs the full NumericTraits<T> specialization in scope, but those
38+
// sites already #include "itkNumericTraits.h" directly or transitively.
39+
template <typename T>
40+
class NumericTraits;
41+
} // namespace itk
42+
3043
namespace itk::print_helper
3144
{
3245

3346
// Forward declarations so the per-container bodies below see all overloads at
3447
// definition time. Required for nested cases like vector<list<T>>, where the
3548
// recursive `os << *it` is parsed before the list overload would otherwise be
3649
// declared, and ADL on std container types never reaches itk::print_helper.
50+
// PrintNumericTrait further down also dispatches through `os << value` and
51+
// must see these forward declarations to instantiate against std container
52+
// member types at the call site.
3753
template <typename T>
3854
std::ostream &
3955
operator<<(std::ostream & os, const std::vector<T> & v);
@@ -50,6 +66,48 @@ template <typename T, size_t VLength, typename = std::enable_if_t<!std::is_same_
5066
std::ostream &
5167
operator<<(std::ostream & os, const T (&arr)[VLength]);
5268

69+
/** \brief Print "<name>: <value>\n" indented, matching ITK's PrintSelf style.
70+
*
71+
* Formats and writes a single named member to \a os preceded by \a indent,
72+
* followed by a newline. When \c NumericTraits<T>::PrintType differs from
73+
* \a T (the relevant case being the \c char family, whose \c PrintType is
74+
* \c int, so values render numerically rather than as ASCII characters) the
75+
* value is forwarded through a \c static_cast. When the two types coincide
76+
* (the common case, including all built-in scalars wider than \c char and
77+
* \c std::complex specialisations whose \c PrintType is \c Self) the cast
78+
* step is skipped entirely so the value's own stream insertion overload is
79+
* selected directly.
80+
*
81+
* Equivalent to the boilerplate
82+
* \code
83+
* os << indent << "Name: "
84+
* << static_cast<typename NumericTraits<T>::PrintType>(m_Name)
85+
* << std::endl;
86+
* \endcode
87+
* but with explicit \a os and \a indent parameters and no preprocessor
88+
* macro expansion.
89+
*
90+
* Typical use inside a \c PrintSelf override:
91+
* \code
92+
* print_helper::PrintNumericTrait(os, indent, "Threshold", m_Threshold);
93+
* \endcode
94+
*/
95+
template <typename T>
96+
inline void
97+
PrintNumericTrait(std::ostream & os, const Indent & indent, const char * name, const T & value)
98+
{
99+
os << indent << name << ": ";
100+
if constexpr (std::is_same_v<T, typename NumericTraits<T>::PrintType>)
101+
{
102+
os << value;
103+
}
104+
else
105+
{
106+
os << static_cast<typename NumericTraits<T>::PrintType>(value);
107+
}
108+
os << std::endl;
109+
}
110+
53111
template <typename T>
54112
std::ostream &
55113
operator<<(std::ostream & os, const std::vector<T> & v)

Modules/Core/Common/include/itkResourceProbe.hxx

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,16 @@ ResourceProbe<ValueType, MeanType>::Print(std::ostream & os, Indent indent) cons
5858
{
5959
using namespace print_helper;
6060

61-
os << indent << "StartValue: " << static_cast<typename NumericTraits<ValueType>::PrintType>(m_StartValue)
62-
<< std::endl;
63-
os << indent << "TotalValue: " << static_cast<typename NumericTraits<ValueType>::PrintType>(m_TotalValue)
64-
<< std::endl;
65-
os << indent << "MinimumValue: " << static_cast<typename NumericTraits<ValueType>::PrintType>(m_MinimumValue)
66-
<< std::endl;
67-
os << indent << "MaximumValue: " << static_cast<typename NumericTraits<ValueType>::PrintType>(m_MaximumValue)
68-
<< std::endl;
69-
os << indent
70-
<< "StandardDeviation: " << static_cast<typename NumericTraits<ValueType>::PrintType>(m_StandardDeviation)
71-
<< std::endl;
72-
os << indent << "StandardError: " << static_cast<typename NumericTraits<ValueType>::PrintType>(m_StandardError)
73-
<< std::endl;
74-
75-
os << indent << "NumberOfStarts: " << static_cast<NumericTraits<CountType>::PrintType>(m_NumberOfStarts) << std::endl;
76-
os << indent << "NumberOfStops: " << static_cast<NumericTraits<CountType>::PrintType>(m_NumberOfStops) << std::endl;
77-
os << indent << "NumberOfIteration: " << static_cast<NumericTraits<CountType>::PrintType>(m_NumberOfIteration)
78-
<< std::endl;
61+
print_helper::PrintNumericTrait(os, indent, "StartValue", m_StartValue);
62+
print_helper::PrintNumericTrait(os, indent, "TotalValue", m_TotalValue);
63+
print_helper::PrintNumericTrait(os, indent, "MinimumValue", m_MinimumValue);
64+
print_helper::PrintNumericTrait(os, indent, "MaximumValue", m_MaximumValue);
65+
print_helper::PrintNumericTrait(os, indent, "StandardDeviation", m_StandardDeviation);
66+
print_helper::PrintNumericTrait(os, indent, "StandardError", m_StandardError);
67+
68+
print_helper::PrintNumericTrait(os, indent, "NumberOfStarts", m_NumberOfStarts);
69+
print_helper::PrintNumericTrait(os, indent, "NumberOfStops", m_NumberOfStops);
70+
print_helper::PrintNumericTrait(os, indent, "NumberOfIteration", m_NumberOfIteration);
7971

8072
os << indent << "ProbeValueList: " << m_ProbeValueList << std::endl;
8173

Modules/Core/Common/test/itkPrintHelperGTest.cxx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "itkOffset.h"
2121
#include "gtest/gtest.h"
2222
#include <array>
23+
#include <complex>
2324
#include <sstream>
2425
#include <vector>
2526
#include <list>
@@ -130,3 +131,55 @@ TEST(PrintHelper, ArrayOfVector)
130131
oss << a;
131132
EXPECT_EQ(oss.str(), "([1, 2], [3])");
132133
}
134+
135+
TEST(PrintHelper, PrintNumericTraitDouble)
136+
{
137+
std::ostringstream oss;
138+
itk::print_helper::PrintNumericTrait(oss, itk::Indent{}, "Value", 3.5);
139+
EXPECT_EQ(oss.str(), "Value: 3.5\n");
140+
}
141+
142+
TEST(PrintHelper, PrintNumericTraitIntIsIdentityCast)
143+
{
144+
// PrintType<int> == int, so the constexpr branch skips static_cast and
145+
// streams the value directly.
146+
std::ostringstream oss;
147+
itk::print_helper::PrintNumericTrait(oss, itk::Indent{}, "Count", 42);
148+
EXPECT_EQ(oss.str(), "Count: 42\n");
149+
}
150+
151+
TEST(PrintHelper, PrintNumericTraitCharRendersNumerically)
152+
{
153+
// PrintType<unsigned char> == int. Without the cast the value would be
154+
// emitted as the ASCII character; the helper must produce the integer.
155+
std::ostringstream oss;
156+
const unsigned char ch = 65;
157+
itk::print_helper::PrintNumericTrait(oss, itk::Indent{}, "Byte", ch);
158+
EXPECT_EQ(oss.str(), "Byte: 65\n");
159+
}
160+
161+
TEST(PrintHelper, PrintNumericTraitSignedCharRendersNumerically)
162+
{
163+
std::ostringstream oss;
164+
const signed char sc = -7;
165+
itk::print_helper::PrintNumericTrait(oss, itk::Indent{}, "Offset", sc);
166+
EXPECT_EQ(oss.str(), "Offset: -7\n");
167+
}
168+
169+
TEST(PrintHelper, PrintNumericTraitComplexIsIdentityCast)
170+
{
171+
// NumericTraits<std::complex<T>>::PrintType is Self, so PrintNumericTrait
172+
// forwards to the value's own ostream insertion overload unchanged.
173+
std::ostringstream oss;
174+
std::complex<double> z{ 1.0, -2.5 };
175+
itk::print_helper::PrintNumericTrait(oss, itk::Indent{}, "Z", z);
176+
EXPECT_EQ(oss.str(), "Z: (1,-2.5)\n");
177+
}
178+
179+
TEST(PrintHelper, PrintNumericTraitIndentation)
180+
{
181+
std::ostringstream oss;
182+
itk::Indent indent{ 4 };
183+
itk::print_helper::PrintNumericTrait(oss, indent, "Threshold", 0.125);
184+
EXPECT_EQ(oss.str(), " Threshold: 0.125\n");
185+
}

0 commit comments

Comments
 (0)