Browse Source

Math: add IsScalar, IsVector, IsIntegral and IsFloatingPoint type traits.

The standard ones are not good enough, so let's roll out my own.
pull/342/head
Vladimír Vondruš 7 years ago
parent
commit
aa2adb7222
  1. 2
      doc/changelog.dox
  2. 61
      src/Magnum/Math/Test/TypeTraitsTest.cpp
  3. 150
      src/Magnum/Math/TypeTraits.h

2
doc/changelog.dox

@ -123,6 +123,8 @@ See also:
@ref Corrade::Utility::Debug::color modifier @ref Corrade::Utility::Debug::color modifier
- Added convenience @ref BoolVector2, @ref BoolVector3 and @ref BoolVector4 - Added convenience @ref BoolVector2, @ref BoolVector3 and @ref BoolVector4
typedefs to the root namespace 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 @subsubsection changelog-latest-new-platform Platform libraries

61
src/Magnum/Math/Test/TypeTraitsTest.cpp

@ -27,6 +27,7 @@
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include <Corrade/Utility/DebugStl.h> #include <Corrade/Utility/DebugStl.h>
#include "Magnum/Math/Math.h"
#include "Magnum/Math/TypeTraits.h" #include "Magnum/Math/TypeTraits.h"
#include "Magnum/Math/Constants.h" #include "Magnum/Math/Constants.h"
@ -38,6 +39,11 @@ struct TypeTraitsTest: Corrade::TestSuite::Tester {
void sizeOfLongDouble(); void sizeOfLongDouble();
void name(); void name();
void isScalar();
void isVector();
void isIntegral();
void isFloatingPoint();
template<class T> void equalsIntegral(); template<class T> void equalsIntegral();
template<class T> void equalsFloatingPoint0(); template<class T> void equalsFloatingPoint0();
template<class T> void equalsFloatingPoint1(); template<class T> void equalsFloatingPoint1();
@ -95,6 +101,11 @@ TypeTraitsTest::TypeTraitsTest() {
addTests({&TypeTraitsTest::sizeOfLongDouble, addTests({&TypeTraitsTest::sizeOfLongDouble,
&TypeTraitsTest::name, &TypeTraitsTest::name,
&TypeTraitsTest::isScalar,
&TypeTraitsTest::isVector,
&TypeTraitsTest::isIntegral,
&TypeTraitsTest::isFloatingPoint,
&TypeTraitsTest::equalsIntegral<UnsignedByte>, &TypeTraitsTest::equalsIntegral<UnsignedByte>,
&TypeTraitsTest::equalsIntegral<Byte>, &TypeTraitsTest::equalsIntegral<Byte>,
&TypeTraitsTest::equalsIntegral<UnsignedShort>, &TypeTraitsTest::equalsIntegral<UnsignedShort>,
@ -166,6 +177,56 @@ void TypeTraitsTest::name() {
CORRADE_COMPARE(TypeTraits<Float>::name(), std::string{"Float"}); CORRADE_COMPARE(TypeTraits<Float>::name(), std::string{"Float"});
} }
void TypeTraitsTest::isScalar() {
CORRADE_VERIFY(IsScalar<char>::value);
CORRADE_VERIFY(IsScalar<UnsignedShort>::value);
CORRADE_VERIFY(IsScalar<Deg<Float>>::value);
CORRADE_VERIFY((IsScalar<Unit<Rad, Double>>::value));
CORRADE_VERIFY(!IsScalar<Vector2<Float>>::value);
CORRADE_VERIFY(!IsVector<Matrix2x3<Float>>::value);
CORRADE_VERIFY(!IsScalar<char*>::value);
CORRADE_VERIFY(!IsScalar<bool>::value);
}
void TypeTraitsTest::isVector() {
CORRADE_VERIFY(!IsVector<UnsignedByte>::value);
CORRADE_VERIFY(!IsVector<Deg<UnsignedByte>>::value);
CORRADE_VERIFY((IsVector<Vector<2, Deg<Float>>>::value));
CORRADE_VERIFY(IsVector<Color3<UnsignedByte>>::value);
CORRADE_VERIFY(!IsVector<Matrix2x3<Float>>::value);
CORRADE_VERIFY(!IsVector<char*>::value);
}
void TypeTraitsTest::isIntegral() {
/* These three are usually different types */
CORRADE_VERIFY(IsIntegral<char>::value);
CORRADE_VERIFY(IsIntegral<Byte>::value);
CORRADE_VERIFY(IsIntegral<UnsignedByte>::value);
CORRADE_VERIFY(IsIntegral<Int>::value);
CORRADE_VERIFY((IsIntegral<Vector<7, UnsignedInt>>::value));
#ifndef CORRADE_TARGET_EMSCRIPTEN
CORRADE_VERIFY(IsIntegral<Vector2<Long>>::value);
#endif
CORRADE_VERIFY(!IsIntegral<Deg<Float>>::value);
CORRADE_VERIFY(!IsIntegral<char*>::value);
CORRADE_VERIFY(!IsIntegral<bool>::value);
}
void TypeTraitsTest::isFloatingPoint() {
CORRADE_VERIFY(!IsFloatingPoint<Int>::value);
CORRADE_VERIFY(!(IsFloatingPoint<Vector<7, UnsignedInt>>::value));
CORRADE_VERIFY(IsFloatingPoint<Double>::value);
CORRADE_VERIFY((IsFloatingPoint<Vector<2, Float>>::value));
#ifndef CORRADE_TARGET_EMSCRIPTEN
CORRADE_VERIFY(IsFloatingPoint<Vector2<long double>>::value);
#endif
CORRADE_VERIFY(IsFloatingPoint<Deg<Float>>::value);
CORRADE_VERIFY((IsFloatingPoint<Unit<Rad, Float>>::value));
CORRADE_VERIFY(!IsFloatingPoint<Deg<Half>>::value);
CORRADE_VERIFY(!IsFloatingPoint<char*>::value);
}
template<class T> void TypeTraitsTest::equalsIntegral() { template<class T> void TypeTraitsTest::equalsIntegral() {
setTestCaseName(std::string{"equalsIntegral<"} + TypeTraits<T>::name() + ">"); setTestCaseName(std::string{"equalsIntegral<"} + TypeTraits<T>::name() + ">");

150
src/Magnum/Math/TypeTraits.h

@ -26,11 +26,12 @@
*/ */
/** @file /** @file
* @brief Class @ref Magnum::Math::TypeTraits * @brief Type traits
*/ */
#include <Corrade/Utility/StlMath.h> #include <Corrade/Utility/StlMath.h>
#include "Magnum/Math/Math.h"
#include "Magnum/Types.h" #include "Magnum/Types.h"
/** /**
@ -74,6 +75,148 @@ for more headroom.
namespace Magnum { namespace Math { 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<class T> 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<char>: std::true_type {};
template<> struct IsScalar<signed char>: std::true_type {};
template<> struct IsScalar<unsigned char>: std::true_type {};
template<> struct IsScalar<short>: std::true_type {};
template<> struct IsScalar<unsigned short>: std::true_type {};
template<> struct IsScalar<int>: std::true_type {};
template<> struct IsScalar<unsigned int>: std::true_type {};
template<> struct IsScalar<long>: std::true_type {};
template<> struct IsScalar<unsigned long>: std::true_type {};
template<> struct IsScalar<long long>: std::true_type {};
template<> struct IsScalar<unsigned long long>: std::true_type {};
template<> struct IsScalar<float>: std::true_type {};
template<> struct IsScalar<double>: std::true_type {};
#ifndef CORRADE_TARGET_EMSCRIPTEN
template<> struct IsScalar<long double>: std::true_type {};
#endif
template<template<class> class Derived, class T> struct IsScalar<Unit<Derived, T>>: std::true_type {};
template<class T> struct IsScalar<Deg<T>>: std::true_type {};
template<class T> struct IsScalar<Rad<T>>: 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<class T> struct IsVector
#ifndef DOXYGEN_GENERATING_OUTPUT
: std::false_type
#endif
{};
#ifndef DOXYGEN_GENERATING_OUTPUT
template<std::size_t size, class T> struct IsVector<Vector<size, T>>: std::true_type {};
template<class T> struct IsVector<Vector2<T>>: std::true_type {};
template<class T> struct IsVector<Vector3<T>>: std::true_type {};
template<class T> struct IsVector<Vector4<T>>: std::true_type {};
template<class T> struct IsVector<Color3<T>>: std::true_type {};
template<class T> struct IsVector<Color4<T>>: 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<class T> 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<char>: std::true_type {};
template<> struct IsIntegral<signed char>: std::true_type {};
template<> struct IsIntegral<unsigned char>: std::true_type {};
template<> struct IsIntegral<short>: std::true_type {};
template<> struct IsIntegral<unsigned short>: std::true_type {};
template<> struct IsIntegral<int>: std::true_type {};
template<> struct IsIntegral<unsigned int>: std::true_type {};
template<> struct IsIntegral<long>: std::true_type {};
template<> struct IsIntegral<unsigned long>: std::true_type {};
template<> struct IsIntegral<long long>: std::true_type {};
template<> struct IsIntegral<unsigned long long>: std::true_type {};
template<std::size_t size, class T> struct IsIntegral<Vector<size, T>>: IsIntegral<T> {};
template<class T> struct IsIntegral<Vector2<T>>: IsIntegral<T> {};
template<class T> struct IsIntegral<Vector3<T>>: IsIntegral<T> {};
template<class T> struct IsIntegral<Vector4<T>>: IsIntegral<T> {};
template<class T> struct IsIntegral<Color3<T>>: IsIntegral<T> {};
template<class T> struct IsIntegral<Color4<T>>: IsIntegral<T> {};
/* 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<class T> struct IsFloatingPoint
#ifndef DOXYGEN_GENERATING_OUTPUT
: std::false_type
#endif
{};
#ifndef DOXYGEN_GENERATING_OUTPUT
template<> struct IsFloatingPoint<Float>: std::true_type {};
template<> struct IsFloatingPoint<Double>: std::true_type {};
#ifndef CORRADE_TARGET_EMSCRIPTEN
template<> struct IsFloatingPoint<long double>: std::true_type {};
#endif
template<std::size_t size, class T> struct IsFloatingPoint<Vector<size, T>>: IsFloatingPoint<T> {};
template<class T> struct IsFloatingPoint<Vector2<T>>: IsFloatingPoint<T> {};
template<class T> struct IsFloatingPoint<Vector3<T>>: IsFloatingPoint<T> {};
template<class T> struct IsFloatingPoint<Vector4<T>>: IsFloatingPoint<T> {};
template<class T> struct IsFloatingPoint<Color3<T>>: IsFloatingPoint<T> {};
template<class T> struct IsFloatingPoint<Color4<T>>: IsFloatingPoint<T> {};
/* Deg<Half> is legal but Half is not an arithmetic type (and thus not
floating-point), so need to check the underlying type as well */
template<template<class> class Derived, class T> struct IsFloatingPoint<Unit<Derived, T>>: IsFloatingPoint<T> {};
template<class T> struct IsFloatingPoint<Deg<T>>: IsFloatingPoint<T> {};
template<class T> struct IsFloatingPoint<Rad<T>>: IsFloatingPoint<T> {};
#endif
namespace Implementation { namespace Implementation {
template<class T> struct TypeTraitsDefault { template<class T> struct TypeTraitsDefault {
TypeTraitsDefault() = delete; 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 Traits classes are usable for detecting type features at compile time without
the need for repeated code such as method overloading or template 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<class T> struct TypeTraits: Implementation::TypeTraitsDefault<T> { template<class T> struct TypeTraits: Implementation::TypeTraitsDefault<T> {
/* /*

Loading…
Cancel
Save