diff --git a/doc/changelog.dox b/doc/changelog.dox index 1381e7914..4c6e7b1ca 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -124,7 +124,8 @@ See also: - Added convenience @ref BoolVector2, @ref BoolVector3 and @ref BoolVector4 typedefs to the root namespace - New @ref Math::IsScalar, @ref Math::IsVector, @ref Math::IsIntegral and - @ref Math::IsFloatingPoint type traits + @ref Math::IsFloatingPoint type traits and a @ref Math::UnderlyingTypeOf + utility @subsubsection changelog-latest-new-platform Platform libraries diff --git a/src/Magnum/Math/Test/TypeTraitsTest.cpp b/src/Magnum/Math/Test/TypeTraitsTest.cpp index 13590906c..26f7518eb 100644 --- a/src/Magnum/Math/Test/TypeTraitsTest.cpp +++ b/src/Magnum/Math/Test/TypeTraitsTest.cpp @@ -44,6 +44,8 @@ struct TypeTraitsTest: Corrade::TestSuite::Tester { void isIntegral(); void isFloatingPoint(); + void underlyingTypeOf(); + template void equalsIntegral(); template void equalsFloatingPoint0(); template void equalsFloatingPoint1(); @@ -106,6 +108,8 @@ TypeTraitsTest::TypeTraitsTest() { &TypeTraitsTest::isIntegral, &TypeTraitsTest::isFloatingPoint, + &TypeTraitsTest::underlyingTypeOf, + &TypeTraitsTest::equalsIntegral, &TypeTraitsTest::equalsIntegral, &TypeTraitsTest::equalsIntegral, @@ -227,6 +231,12 @@ void TypeTraitsTest::isFloatingPoint() { CORRADE_VERIFY(!IsFloatingPoint::value); } +void TypeTraitsTest::underlyingTypeOf() { + CORRADE_VERIFY((std::is_same, Int>::value)); + CORRADE_VERIFY((std::is_same>, Float>::value)); + CORRADE_VERIFY((std::is_same>, Double>::value)); +} + template void TypeTraitsTest::equalsIntegral() { setTestCaseName(std::string{"equalsIntegral<"} + TypeTraits::name() + ">"); diff --git a/src/Magnum/Math/TypeTraits.h b/src/Magnum/Math/TypeTraits.h index fedf02357..c3e214733 100644 --- a/src/Magnum/Math/TypeTraits.h +++ b/src/Magnum/Math/TypeTraits.h @@ -87,7 +87,7 @@ Note that this is *different* from @ref std::is_scalar, which is @cpp true @ce also for enums or pointers --- it's rather closer to @ref std::is_arithmetic, except that it doesn't give @ref std::true_type for @cpp bool @ce. The name is chosen particularly for the @ref IsVector / @ref IsScalar distinction. -@see @ref IsFloatingPoint, @ref IsIntegral +@see @ref IsFloatingPoint, @ref IsIntegral, @ref UnderlyingTypeOf */ template struct IsScalar #ifndef DOXYGEN_GENERATING_OUTPUT @@ -217,6 +217,31 @@ template struct IsFloatingPoint>: IsFloatingPoint {}; template struct IsFloatingPoint>: IsFloatingPoint {}; #endif +namespace Implementation { + template struct UnderlyingType { + static_assert(IsScalar::value, "type is not scalar"); + typedef T Type; + }; + template class Derived, class T> struct UnderlyingType> { + typedef T Type; + }; + template struct UnderlyingType> { typedef T Type; }; + template struct UnderlyingType> { typedef T Type; }; +} + +/** +@brief Underlying builtin type for a scalar type + +For builtin types returns the type itself, for wrapped types like @ref Deg or +@ref Rad returns the underlying builtin type. It's guaranteed that the input +type is always explicitly convertible to the output type and the output type +is usable with standard APIs such as @ref std::isinf(). + +Passed types are required to satisfy @ref IsScalar. All non-scalar Magnum math +types have a member @cpp typedef @ce ``Type`` containing the underlying type. +*/ +template using UnderlyingTypeOf = typename Implementation::UnderlyingType::Type; + namespace Implementation { template struct TypeTraitsDefault { TypeTraitsDefault() = delete;