Skip to content

Commit 0d1a52b

Browse files
committed
ENH: Add constexpr overloads for itk::Math::abs
Include bool, integral, and floating-point support Refactored `itk::Math::abs` to use `std::enable_if_t` for type constraints, supporting both integral and floating-point types. Improved compile-time evaluation with `constexpr`. Enhanced test coverage for `itk::Math::abs`.
1 parent f396e52 commit 0d1a52b

File tree

2 files changed

+64
-42
lines changed

2 files changed

+64
-42
lines changed

Modules/Core/Common/include/itkMath.h

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#include <cassert>
3232
#include <cmath>
33+
#include <type_traits>
3334
#include "itkMathDetail.h"
3435
#include "itkConceptChecking.h"
3536
#include <vnl/vnl_math.h>
@@ -825,55 +826,48 @@ using vnl_math::squared_magnitude;
825826

826827
/*============================================
827828
Decouple dependence and exposure of vnl_math::abs operations
828-
in ITK. Placing this small amount of duplicate vnl_math
829-
code directly in ITK removes backward compatibility
830-
issues with system installed VXL versions.
829+
in ITK. Replacing the vnl_math::abs operations with
830+
c++17 equivalent implementations can provide compile time
831+
performance improvements.
831832
*/
832-
inline bool
833-
abs(bool x)
834-
{
835-
return x;
836-
}
837-
inline unsigned char
838-
abs(unsigned char x)
839-
{
840-
return x;
841-
}
842-
inline unsigned char
843-
abs(signed char x)
844-
{
845-
return x < 0 ? static_cast<unsigned char>(-x) : x;
846-
}
847-
inline unsigned char
848-
abs(char x)
849-
{
850-
return static_cast<unsigned char>(x);
851-
}
852-
inline unsigned short
853-
abs(short x)
854-
{
855-
return x < 0 ? static_cast<unsigned short>(-x) : x;
856-
}
857-
inline unsigned short
858-
abs(unsigned short x)
859-
{
860-
return x;
861-
}
862-
inline unsigned int
863-
abs(unsigned int x)
833+
834+
835+
constexpr bool
836+
abs(bool x) noexcept
864837
{
865838
return x;
866839
}
867-
inline unsigned long
868-
abs(unsigned long x)
840+
841+
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
842+
constexpr std::make_unsigned_t<T>
843+
abs(T x) noexcept
869844
{
870-
return x;
845+
if constexpr (std::is_unsigned_v<T>)
846+
{
847+
return x;
848+
}
849+
else
850+
{
851+
if constexpr (sizeof(T) <= sizeof(int))
852+
{
853+
// Using unsigned int instead of std::make_unsigned_t<T>
854+
// to avoid potential overflow when T is a signed char (value -128).
855+
// Note that -(-128) is still -128 for an 8-bit signed char.
856+
return static_cast<std::make_unsigned_t<T>>(x < 0 ? static_cast<unsigned int>(-static_cast<int>(x))
857+
: static_cast<unsigned int>(x));
858+
}
859+
else
860+
{
861+
return x < 0 ? static_cast<std::make_unsigned_t<T>>(-x) : static_cast<std::make_unsigned_t<T>>(x);
862+
}
863+
}
871864
}
872-
// long long - target type will have width of at least 64 bits. (since C++11)
873-
inline unsigned long long
874-
abs(unsigned long long x)
865+
866+
template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
867+
constexpr T
868+
abs(T x) noexcept
875869
{
876-
return x;
870+
return x < 0 ? -x : x;
877871
}
878872

879873
using std::abs;

Modules/Core/Common/test/itkMathTest.cxx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,34 @@ main(int, char *[])
853853
std::cout << "Test passed" << std::endl;
854854
}
855855

856+
// Test the itk::Math::abs methods
857+
std::cout << "Testing itk::Math::abs integral overloads" << std::endl;
858+
static_assert(itk::Math::abs(false) == false);
859+
static_assert(itk::Math::abs(true) == true);
860+
static_assert(itk::Math::abs((unsigned char)5) == 5);
861+
static_assert(itk::Math::abs((signed char)-5) == 5);
862+
static_assert(itk::Math::abs((signed char)-128) == 128);
863+
static_assert(itk::Math::abs((short)-5) == 5);
864+
// Specify template arguments to avoid choosing non-constexpr std::abs overloads
865+
static_assert(itk::Math::abs<int>(-5) == 5u);
866+
static_assert(itk::Math::abs<long>(-5L) == 5ul);
867+
static_assert(itk::Math::abs<long long>(-5LL) == 5ull);
868+
869+
static_assert(itk::Math::abs<double>(-5.0) == 5.0);
870+
static_assert(itk::Math::abs<float>(-5.0f) == 5.0f);
871+
872+
if (itk::Math::abs((signed char)-128) != 128)
873+
{
874+
std::cout << "itk::Math::abs((signed char)-128) FAILED!" << std::endl;
875+
testPassStatus = EXIT_FAILURE;
876+
}
877+
878+
if (itk::Math::abs(-5) != 5)
879+
{
880+
std::cout << "itk::Math::abs(-5) FAILED!" << std::endl;
881+
testPassStatus = EXIT_FAILURE;
882+
}
883+
856884

857885
return testPassStatus;
858886
}

0 commit comments

Comments
 (0)