From aa2adb72225efba8750b27e42e24de34c1f6a841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 24 May 2019 19:53:15 +0200 Subject: [PATCH] Math: add IsScalar, IsVector, IsIntegral and IsFloatingPoint type traits. The standard ones are not good enough, so let's roll out my own. --- doc/changelog.dox | 2 + src/Magnum/Math/Test/TypeTraitsTest.cpp | 61 ++++++++++ src/Magnum/Math/TypeTraits.h | 150 +++++++++++++++++++++++- 3 files changed, 210 insertions(+), 3 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 4afa89964..1381e7914 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -123,6 +123,8 @@ See also: @ref Corrade::Utility::Debug::color modifier - 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 @subsubsection changelog-latest-new-platform Platform libraries diff --git a/src/Magnum/Math/Test/TypeTraitsTest.cpp b/src/Magnum/Math/Test/TypeTraitsTest.cpp index c4f860733..13590906c 100644 --- a/src/Magnum/Math/Test/TypeTraitsTest.cpp +++ b/src/Magnum/Math/Test/TypeTraitsTest.cpp @@ -27,6 +27,7 @@ #include #include +#include "Magnum/Math/Math.h" #include "Magnum/Math/TypeTraits.h" #include "Magnum/Math/Constants.h" @@ -38,6 +39,11 @@ struct TypeTraitsTest: Corrade::TestSuite::Tester { void sizeOfLongDouble(); void name(); + void isScalar(); + void isVector(); + void isIntegral(); + void isFloatingPoint(); + template void equalsIntegral(); template void equalsFloatingPoint0(); template void equalsFloatingPoint1(); @@ -95,6 +101,11 @@ TypeTraitsTest::TypeTraitsTest() { addTests({&TypeTraitsTest::sizeOfLongDouble, &TypeTraitsTest::name, + &TypeTraitsTest::isScalar, + &TypeTraitsTest::isVector, + &TypeTraitsTest::isIntegral, + &TypeTraitsTest::isFloatingPoint, + &TypeTraitsTest::equalsIntegral, &TypeTraitsTest::equalsIntegral, &TypeTraitsTest::equalsIntegral, @@ -166,6 +177,56 @@ void TypeTraitsTest::name() { CORRADE_COMPARE(TypeTraits::name(), std::string{"Float"}); } +void TypeTraitsTest::isScalar() { + CORRADE_VERIFY(IsScalar::value); + CORRADE_VERIFY(IsScalar::value); + CORRADE_VERIFY(IsScalar>::value); + CORRADE_VERIFY((IsScalar>::value)); + CORRADE_VERIFY(!IsScalar>::value); + CORRADE_VERIFY(!IsVector>::value); + CORRADE_VERIFY(!IsScalar::value); + CORRADE_VERIFY(!IsScalar::value); +} + +void TypeTraitsTest::isVector() { + CORRADE_VERIFY(!IsVector::value); + CORRADE_VERIFY(!IsVector>::value); + CORRADE_VERIFY((IsVector>>::value)); + CORRADE_VERIFY(IsVector>::value); + CORRADE_VERIFY(!IsVector>::value); + CORRADE_VERIFY(!IsVector::value); +} + +void TypeTraitsTest::isIntegral() { + /* These three are usually different types */ + CORRADE_VERIFY(IsIntegral::value); + CORRADE_VERIFY(IsIntegral::value); + CORRADE_VERIFY(IsIntegral::value); + + CORRADE_VERIFY(IsIntegral::value); + CORRADE_VERIFY((IsIntegral>::value)); + #ifndef CORRADE_TARGET_EMSCRIPTEN + CORRADE_VERIFY(IsIntegral>::value); + #endif + CORRADE_VERIFY(!IsIntegral>::value); + CORRADE_VERIFY(!IsIntegral::value); + CORRADE_VERIFY(!IsIntegral::value); +} + +void TypeTraitsTest::isFloatingPoint() { + CORRADE_VERIFY(!IsFloatingPoint::value); + CORRADE_VERIFY(!(IsFloatingPoint>::value)); + CORRADE_VERIFY(IsFloatingPoint::value); + CORRADE_VERIFY((IsFloatingPoint>::value)); + #ifndef CORRADE_TARGET_EMSCRIPTEN + CORRADE_VERIFY(IsFloatingPoint>::value); + #endif + CORRADE_VERIFY(IsFloatingPoint>::value); + CORRADE_VERIFY((IsFloatingPoint>::value)); + CORRADE_VERIFY(!IsFloatingPoint>::value); + CORRADE_VERIFY(!IsFloatingPoint::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 044119439..fedf02357 100644 --- a/src/Magnum/Math/TypeTraits.h +++ b/src/Magnum/Math/TypeTraits.h @@ -26,11 +26,12 @@ */ /** @file - * @brief Class @ref Magnum::Math::TypeTraits + * @brief Type traits */ #include +#include "Magnum/Math/Math.h" #include "Magnum/Types.h" /** @@ -74,6 +75,148 @@ for more headroom. namespace Magnum { namespace Math { +/** +@brief Whether @p T is an arithmetic scalar type + +Equivalent to @ref std::true_type for all builtin scalar integer and +floating-point types and in addition also @ref Deg and @ref Rad; equivalent to +@ref std::false_type otherwise. The @ref Half type deliberately doesn't support +any arithmetic, so it's not treated as a scalar type. + +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 +*/ +template struct IsScalar + #ifndef DOXYGEN_GENERATING_OUTPUT + : std::false_type + #endif + {}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +/* Can't use our own typedefs because they don't cover all types (signed char + vs char, long vs long long etc.). Funny that char != signed char but signed + int = int */ +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +template<> struct IsScalar: std::true_type {}; +#ifndef CORRADE_TARGET_EMSCRIPTEN +template<> struct IsScalar: std::true_type {}; +#endif +template class Derived, class T> struct IsScalar>: std::true_type {}; +template struct IsScalar>: std::true_type {}; +template struct IsScalar>: std::true_type {}; +#endif + +/** +@brief Whether @p T is an arithmetic vector type + +Equivalent to @ref std::true_type for all @ref Vector types and their +subclasses; equivalent to @ref std::false_type otherwise. In particular, gives +@ref std::false_type for @ref BoolVector, all matrix types, @ref Complex or +@ref Quaternion. +@see @ref IsScalar, @ref IsFloatingPoint, @ref IsIntegral +*/ +template struct IsVector + #ifndef DOXYGEN_GENERATING_OUTPUT + : std::false_type + #endif + {}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +template struct IsVector>: std::true_type {}; +template struct IsVector>: std::true_type {}; +template struct IsVector>: std::true_type {}; +template struct IsVector>: std::true_type {}; +template struct IsVector>: std::true_type {}; +template struct IsVector>: std::true_type {}; +#endif + +/** +@brief Whether @p T is integral + +Equivalent to @ref std::true_type for all integral scalar and vector types +supported by Magnum math; equivalent to @ref std::false_type otherwise. + +Unlike @ref std::is_integral this is @ref std::false_type for @cpp bool @ce. +@see @ref IsFloatingPoint, @ref IsScalar, @ref IsVector +*/ +template struct IsIntegral + #ifndef DOXYGEN_GENERATING_OUTPUT + : std::false_type + #endif + {}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +/* Can't use our own typedefs because they don't cover all types (signed char + vs char, long vs long long etc.). Funny that char != signed char but signed + int = int */ +template<> struct IsIntegral: std::true_type {}; +template<> struct IsIntegral: std::true_type {}; +template<> struct IsIntegral: std::true_type {}; +template<> struct IsIntegral: std::true_type {}; +template<> struct IsIntegral: std::true_type {}; +template<> struct IsIntegral: std::true_type {}; +template<> struct IsIntegral: std::true_type {}; +template<> struct IsIntegral: std::true_type {}; +template<> struct IsIntegral: std::true_type {}; +template<> struct IsIntegral: std::true_type {}; +template<> struct IsIntegral: std::true_type {}; +template struct IsIntegral>: IsIntegral {}; +template struct IsIntegral>: IsIntegral {}; +template struct IsIntegral>: IsIntegral {}; +template struct IsIntegral>: IsIntegral {}; +template struct IsIntegral>: IsIntegral {}; +template struct IsIntegral>: IsIntegral {}; +/* I don't expect Deg/Rad to ever have an integral base type */ +#endif + +/** +@brief Whether @p T is floating-point + +Equivalent to @ref std::true_type for all floating-point scalar and vector +types supported by Magnum math including @ref Deg and @ref Rad; equivalent to +@ref std::false_type otherwise. The @ref Half type deliberately doesn't support +any arithmetic, so it's not treated as a floating-point type. +@see @ref IsIntegral, @ref IsScalar, @ref IsVector, @ref std::is_floating_point +*/ +template struct IsFloatingPoint + #ifndef DOXYGEN_GENERATING_OUTPUT + : std::false_type + #endif + {}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +template<> struct IsFloatingPoint: std::true_type {}; +template<> struct IsFloatingPoint: std::true_type {}; +#ifndef CORRADE_TARGET_EMSCRIPTEN +template<> struct IsFloatingPoint: std::true_type {}; +#endif +template struct IsFloatingPoint>: IsFloatingPoint {}; +template struct IsFloatingPoint>: IsFloatingPoint {}; +template struct IsFloatingPoint>: IsFloatingPoint {}; +template struct IsFloatingPoint>: IsFloatingPoint {}; +template struct IsFloatingPoint>: IsFloatingPoint {}; +template struct IsFloatingPoint>: IsFloatingPoint {}; +/* Deg is legal but Half is not an arithmetic type (and thus not + floating-point), so need to check the underlying type as well */ +template class Derived, class T> struct IsFloatingPoint>: IsFloatingPoint {}; +template struct IsFloatingPoint>: IsFloatingPoint {}; +template struct IsFloatingPoint>: IsFloatingPoint {}; +#endif + namespace Implementation { template struct TypeTraitsDefault { TypeTraitsDefault() = delete; @@ -89,11 +232,12 @@ namespace Implementation { } /** -@brief Traits class for numeric types +@brief Traits class for builtin arithmetic types Traits classes are usable for detecting type features at compile time without the need for repeated code such as method overloading or template -specialization for given types. +specialization for given types. All builtin arithmetic types have this class +implemented. */ template struct TypeTraits: Implementation::TypeTraitsDefault { /*