From f585d39146d3e1a86f48b55b1cd89b8ae0b69a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 30 Jan 2012 01:44:51 +0100 Subject: [PATCH] Added Math::TypeTraits, implementing fuzzy comparison based on type. Fuzzy comparison is implemented only on floating-point types with type-specific epsilon, on integral types classic operator== is used. TypeTraits class is now extension of Math::TypeTraits (where it makes sense). --- src/Math/Test/CMakeLists.txt | 2 + src/Math/Test/TypeTraitsTest.cpp | 60 +++++++++++++++++ src/Math/Test/TypeTraitsTest.h | 36 ++++++++++ src/Math/TypeTraits.h | 110 +++++++++++++++++++++++++++++++ src/TypeTraits.h | 41 +++++++----- 5 files changed, 232 insertions(+), 17 deletions(-) create mode 100644 src/Math/Test/TypeTraitsTest.cpp create mode 100644 src/Math/Test/TypeTraitsTest.h create mode 100644 src/Math/TypeTraits.h diff --git a/src/Math/Test/CMakeLists.txt b/src/Math/Test/CMakeLists.txt index 4badfbaf6..fbc2d8f39 100644 --- a/src/Math/Test/CMakeLists.txt +++ b/src/Math/Test/CMakeLists.txt @@ -1,3 +1,5 @@ +corrade_add_test(TypeTraitsTest TypeTraitsTest.h TypeTraitsTest.cpp) + corrade_add_test(VectorTest VectorTest.h VectorTest.cpp) target_link_libraries(VectorTest ${CORRADE_UTILITY_LIBRARY}) corrade_add_test(Vector2Test Vector2Test.h Vector2Test.cpp) diff --git a/src/Math/Test/TypeTraitsTest.cpp b/src/Math/Test/TypeTraitsTest.cpp new file mode 100644 index 000000000..6f0b2791a --- /dev/null +++ b/src/Math/Test/TypeTraitsTest.cpp @@ -0,0 +1,60 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "TypeTraitsTest.h" + +#include + +#include "TypeTraits.h" + +QTEST_APPLESS_MAIN(Magnum::Math::Test::TypeTraitsTest) + +namespace Magnum { namespace Math { namespace Test { + +void TypeTraitsTest::equalsIntegral() { + _equalsIntegral(); + _equalsIntegral(); + _equalsIntegral(); + _equalsIntegral(); + _equalsIntegral(); + _equalsIntegral(); + _equalsIntegral(); + _equalsIntegral(); + _equalsIntegral(); + _equalsIntegral(); +} + +void TypeTraitsTest::equalsFloatingPoint() { + _equalsFloatingPoint(); + _equalsFloatingPoint(); +} + +template void TypeTraitsTest::_equalsIntegral() { + QVERIFY(TypeTraits::equals(1, 1+TypeTraits::epsilon())); + QVERIFY(!TypeTraits::equals(1, 2)); +} + +template void TypeTraitsTest::_equalsFloatingPoint() { + QVERIFY(TypeTraits::equals(1.0f+TypeTraits::epsilon()/2, 1.0f)); + QVERIFY(!TypeTraits::equals(1.0f+TypeTraits::epsilon()*2, 1.0f)); + + QEXPECT_FAIL(0, "Comparing to infinity is broken", Continue); + QVERIFY(TypeTraits::equals(std::numeric_limits::infinity(), + std::numeric_limits::infinity())); + QVERIFY(!TypeTraits::equals(std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN())); +} + +}}} diff --git a/src/Math/Test/TypeTraitsTest.h b/src/Math/Test/TypeTraitsTest.h new file mode 100644 index 000000000..426071a6d --- /dev/null +++ b/src/Math/Test/TypeTraitsTest.h @@ -0,0 +1,36 @@ +#ifndef Magnum_Math_Test_TypeTraitsTest_h +#define Magnum_Math_Test_TypeTraitsTest_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include + +namespace Magnum { namespace Math { namespace Test { + +class TypeTraitsTest: public QObject { + Q_OBJECT + + private slots: + void equalsFloatingPoint(); + void equalsIntegral(); + + private: + template void _equalsFloatingPoint(); + template void _equalsIntegral(); +}; + +}}} + +#endif diff --git a/src/Math/TypeTraits.h b/src/Math/TypeTraits.h new file mode 100644 index 000000000..512668143 --- /dev/null +++ b/src/Math/TypeTraits.h @@ -0,0 +1,110 @@ +#ifndef Magnum_Math_TypeTraits_h +#define Magnum_Math_TypeTraits_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Math::TypeTraits + */ + +#include + +namespace Magnum { namespace Math { + +/** +@brief Traits class for numeric 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. + +This class and class methods are specialized only for types where it makes +sense, it has empty implementation for unknown types or types which don't +support given feature, thus forcing the compilation stop with an error. +*/ +template struct TypeTraits { + #ifdef DOXYGEN_GENERATING_OUTPUT + /* Development note: the following values are implemented as inline + functions, not as static const variables, because the compiler will + inline the return values instead of referencing to static data and + unlike static const variables these functions can return floats. */ + + /** + * @brief Epsilon value for fuzzy compare + * + * Returns 0 for integer types and reasonably small value for + * floating-point types. + */ + constexpr inline static T epsilon(); + + /** + * @brief Fuzzy compare + * + * Uses equality for integer types and fuzzy compare for floating-point + * types (using @ref epsilon value). + */ + static bool equals(T a, T b); + #endif +}; + +/** @bug Infinity comparison! */ + +/** + * @todo Implement better fuzzy comparison algorithm, like at + * http://floating-point-gui.de/errors/comparison/ or + * http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm + */ + +#ifndef DOXYGEN_GENERATING_OUTPUT +template struct _TypeTraitsIntegral { + constexpr inline static T epsilon() { return 0; } + + inline constexpr static bool equals(T a, T b) { + return a == b; + } +}; +template<> struct TypeTraits: public _TypeTraitsIntegral {}; +template<> struct TypeTraits: public _TypeTraitsIntegral {}; + +template<> struct TypeTraits: public _TypeTraitsIntegral {}; +template<> struct TypeTraits: public _TypeTraitsIntegral {}; + +template<> struct TypeTraits: public _TypeTraitsIntegral {}; +template<> struct TypeTraits: public _TypeTraitsIntegral {}; + +/* long is 32 bits somewhere and 64 bits elsewhere, so it cannot be mapped to + any of them */ +template<> struct TypeTraits: public _TypeTraitsIntegral {}; +template<> struct TypeTraits: public _TypeTraitsIntegral {}; + +template<> struct TypeTraits: public _TypeTraitsIntegral {}; +template<> struct TypeTraits: public _TypeTraitsIntegral {}; + +template struct _TypeTraitsFloatingPoint { + inline static bool equals(T a, T b) { + return std::abs(a - b) < TypeTraits::epsilon(); + } +}; +template<> struct TypeTraits: public _TypeTraitsFloatingPoint { + constexpr inline static float epsilon() { return 1.0e-6f; } +}; +template<> struct TypeTraits: public _TypeTraitsFloatingPoint { + constexpr inline static double epsilon() { return 1.0e-12; } +}; +#endif + +}} + +#endif diff --git a/src/TypeTraits.h b/src/TypeTraits.h index b7e9729df..539e3b76d 100644 --- a/src/TypeTraits.h +++ b/src/TypeTraits.h @@ -26,16 +26,13 @@ namespace Magnum { /** @brief Traits class for plain OpenGL 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. +@copydetails Math::TypeTraits -This class and class methods are specialized only for types where it makes -sense, it has empty implementation for unknown types or types which don't -support given feature, thus forcing the compilation stop with an error. +Where it makes sense, this class extends Math::TypeTraits with OpenGL-specific +traits. */ -template struct TypeTraits { - #ifdef DOXYGEN_GENERATING_OUTPUT +#ifdef DOXYGEN_GENERATING_OUTPUT +template struct TypeTraits: public Math::TypeTraits { /** * @brief Type which can be used for indices * @@ -75,14 +72,17 @@ template struct TypeTraits { * Returns 1 for plain OpenGL types like GLint, but e.g. 3 for Vector3. */ constexpr inline static size_t count(); - #endif }; +#else +template struct TypeTraits {}; +#endif /** @todo Other texture types, referenced in glTexImage2D function manual */ /** @todo Using Vector3 for textures? */ #ifndef DOXYGEN_GENERATING_OUTPUT -template<> struct TypeTraits { +static_assert(sizeof(GLubyte) == sizeof(unsigned char), "GLubyte is not the same as unsigned char"); +template<> struct TypeTraits: public Math::TypeTraits { typedef GLubyte IndexType; typedef GLubyte TextureType; @@ -91,7 +91,8 @@ template<> struct TypeTraits { inline constexpr static size_t count() { return 1; } }; -template<> struct TypeTraits { +static_assert(sizeof(GLbyte) == sizeof(char), "GLbyte is not the same as char"); +template<> struct TypeTraits: public Math::TypeTraits { /* Can not be used for indices */ typedef GLbyte TextureType; @@ -100,7 +101,8 @@ template<> struct TypeTraits { inline constexpr static size_t count() { return 1; } }; -template<> struct TypeTraits { +static_assert(sizeof(GLushort) == sizeof(unsigned short), "GLushort is not the same as unsigned short"); +template<> struct TypeTraits: public Math::TypeTraits { typedef GLushort IndexType; typedef GLushort TextureType; @@ -109,7 +111,8 @@ template<> struct TypeTraits { inline constexpr static size_t count() { return 1; } }; -template<> struct TypeTraits { +static_assert(sizeof(GLshort) == sizeof(short), "GLshort is not the same as short"); +template<> struct TypeTraits: public Math::TypeTraits { /* Can not be used for indices */ typedef GLshort TextureType; @@ -118,7 +121,8 @@ template<> struct TypeTraits { inline constexpr static size_t count() { return 1; } }; -template<> struct TypeTraits { +static_assert(sizeof(GLuint) == sizeof(unsigned int), "GLuint is not the same as unsigned int"); +template<> struct TypeTraits: public Math::TypeTraits { typedef GLuint IndexType; typedef GLuint TextureType; @@ -127,7 +131,8 @@ template<> struct TypeTraits { inline constexpr static size_t count() { return 1; } }; -template<> struct TypeTraits { +static_assert(sizeof(GLint) == sizeof(unsigned int), "GLint is not the same as int"); +template<> struct TypeTraits: public Math::TypeTraits { /* Can not be used for indices */ typedef GLint TextureType; @@ -136,7 +141,8 @@ template<> struct TypeTraits { inline constexpr static size_t count() { return 1; } }; -template<> struct TypeTraits { +static_assert(sizeof(GLfloat) == sizeof(float), "GLfloat is not the same as float"); +template<> struct TypeTraits: public Math::TypeTraits { /* Can not be used for indices */ typedef GLfloat TextureType; @@ -145,7 +151,8 @@ template<> struct TypeTraits { inline constexpr static size_t count() { return 1; } }; -template<> struct TypeTraits { +static_assert(sizeof(GLdouble) == sizeof(double), "GLdouble is not the same as double"); +template<> struct TypeTraits: public Math::TypeTraits { /* Can not be used for indices */ /* Can not be used for textures */