[Orxonox-commit 6345] r11002 - in code/branches/cpp11_v2: src/libraries/network/synchronisable src/libraries/util test/util

landauf at orxonox.net landauf at orxonox.net
Wed Dec 30 21:13:15 CET 2015


Author: landauf
Date: 2015-12-30 21:13:14 +0100 (Wed, 30 Dec 2015)
New Revision: 11002

Modified:
   code/branches/cpp11_v2/src/libraries/network/synchronisable/Synchronisable.h
   code/branches/cpp11_v2/src/libraries/util/Math.h
   code/branches/cpp11_v2/src/libraries/util/MultiType.h
   code/branches/cpp11_v2/test/util/MultiTypeTest.cc
Log:
MultiType now supports strongly typed enum classes. Their values are cast to the underlying type.
MultiType now also uses additional static_asserts to ensure that it is only used with supported values.

Modified: code/branches/cpp11_v2/src/libraries/network/synchronisable/Synchronisable.h
===================================================================
--- code/branches/cpp11_v2/src/libraries/network/synchronisable/Synchronisable.h	2015-12-30 19:53:54 UTC (rev 11001)
+++ code/branches/cpp11_v2/src/libraries/network/synchronisable/Synchronisable.h	2015-12-30 20:13:14 UTC (rev 11002)
@@ -206,25 +206,25 @@
   namespace detail
   {
     template <class T, bool = std::is_enum<T>::value>
-    struct RealType;
+    struct UnderlyingType;
     template <class T>
-    struct RealType<T, true> { typedef typename std::underlying_type<T>::type type; };
+    struct UnderlyingType<T, true> { typedef typename std::underlying_type<T>::type type; };
     template <class T>
-    struct RealType<T, false> { typedef T type; };
+    struct UnderlyingType<T, false> { typedef T type; };
   }
 
   template <class T>
   void Synchronisable::registerVariable(T& variable, uint8_t mode, NetworkCallbackBase *cb, bool bidirectional)
   {
-    typedef typename detail::RealType<T>::type RealType;
+    typedef typename detail::UnderlyingType<T>::type UnderlyingType;
     if (bidirectional)
     {
-      syncList_.push_back(new SynchronisableVariableBidirectional<RealType>(reinterpret_cast<RealType&>(variable), mode, cb));
+      syncList_.push_back(new SynchronisableVariableBidirectional<UnderlyingType>(reinterpret_cast<UnderlyingType&>(variable), mode, cb));
       this->dataSize_ += syncList_.back()->getSize(state_);
     }
     else
     {
-      syncList_.push_back(new SynchronisableVariable<RealType>(reinterpret_cast<RealType&>(variable), mode, cb));
+      syncList_.push_back(new SynchronisableVariable<UnderlyingType>(reinterpret_cast<UnderlyingType&>(variable), mode, cb));
       if ( this->state_ == mode )
         this->dataSize_ += syncList_.back()->getSize(state_);
     }
@@ -254,12 +254,12 @@
   template <class T>
   void Synchronisable::registerVariable( std::set<T>& variable, uint8_t mode, NetworkCallbackBase *cb, bool bidirectional)
   {
-    typedef typename detail::RealType<T>::type RealType;
+    typedef typename detail::UnderlyingType<T>::type UnderlyingType;
     SynchronisableVariableBase* sv;
     if (bidirectional)
-      sv = new SynchronisableVariableBidirectional<std::set<RealType>>(reinterpret_cast<std::set<RealType>&>(variable), mode, cb);
+      sv = new SynchronisableVariableBidirectional<std::set<UnderlyingType>>(reinterpret_cast<std::set<UnderlyingType>&>(variable), mode, cb);
     else
-      sv = new SynchronisableVariable<std::set<RealType>>(reinterpret_cast<std::set<RealType>&>(variable), mode, cb);
+      sv = new SynchronisableVariable<std::set<UnderlyingType>>(reinterpret_cast<std::set<UnderlyingType>&>(variable), mode, cb);
     syncList_.push_back(sv);
     stringList_.push_back(sv);
   }

Modified: code/branches/cpp11_v2/src/libraries/util/Math.h
===================================================================
--- code/branches/cpp11_v2/src/libraries/util/Math.h	2015-12-30 19:53:54 UTC (rev 11001)
+++ code/branches/cpp11_v2/src/libraries/util/Math.h	2015-12-30 20:13:14 UTC (rev 11002)
@@ -46,6 +46,7 @@
 #include <cmath>
 #include <cstdlib>
 #include <random>
+#include <type_traits>
 
 #include <OgreMath.h>
 #include <OgreVector2.h>
@@ -177,12 +178,18 @@
         for @c float it's 0.0f. For a @c std::string the function returns "" and for
         @c Vector3 you get <tt>Vector3(0, 0, 0)</tt>.
     */
-    template <typename T>
-    inline T zeroise()
+    template <typename T> /* for normal classes */ typename std::enable_if<!std::is_enum<T>::value, T>::type
+    inline /*T*/ zeroise()
     {
         // If you reach this code, you abused zeroise()!
         static_assert(sizeof(T) != sizeof(T), "No template specialization available for T");
     }
+    /// Implementation for enum classes: uses the underlying type to create a zero value.
+    template <typename T> /* for enum classes */ typename std::enable_if<std::is_enum<T>::value, T>::type
+    inline /*T*/ zeroise()
+    {
+        return static_cast<T>(zeroise<typename std::underlying_type<T>::type>());
+    }
 
     template <> inline char                 zeroise<char>()                 { return 0; }
     template <> inline unsigned char        zeroise<unsigned char>()        { return 0; }

Modified: code/branches/cpp11_v2/src/libraries/util/MultiType.h
===================================================================
--- code/branches/cpp11_v2/src/libraries/util/MultiType.h	2015-12-30 19:53:54 UTC (rev 11001)
+++ code/branches/cpp11_v2/src/libraries/util/MultiType.h	2015-12-30 20:13:14 UTC (rev 11002)
@@ -182,9 +182,22 @@
 
             /// Returns the type of the current value.
             inline const Type::Enum& getType() const { return this->type_; }
-            /// Returns true if the type of the stored value is T.
-            template <typename T> inline bool isType() const { return false; }
 
+            /// Returns true if the type of the stored value is T. Note: the actual implementations for all supported types are defined outside of the class.
+            template <typename T> /* for normal classes */ typename std::enable_if<!std::is_enum<T>::value, bool>::type
+            inline /*bool*/ isType() const
+            {
+                // If you reach this code, you used MultiType with an unsupported type T
+                static_assert(sizeof(T) != sizeof(T), "No template specialization available for T");
+                return false;
+            }
+            /// Implementation for enum classes: Returns true if the type of the stored value is the underlying type of T.
+            template <typename T> /* for enum classes */ typename std::enable_if<std::is_enum<T>::value, bool>::type
+            inline /*bool*/ isType() const
+            {
+                return this->isType<typename std::underlying_type<T>::type>();
+            }
+
             /// Checks whether the value is a default one.
             inline bool lastConversionSuccessful()   const { return this->bLastConversionSuccessful; }
 
@@ -214,6 +227,20 @@
 
             virtual bool setValue(const MultiType& other)            = 0;
 
+            template <typename T> /* for normal classes */ typename std::enable_if<!std::is_enum<T>::value, bool>::type
+            inline /*bool*/ setValue(const T& value)
+            {
+                // If you reach this code, you used MultiType with an unsupported type T
+                static_assert(sizeof(T) != sizeof(T), "No template specialization available for T");
+                return false;
+            }
+            template <typename T> /* for enum classes */ typename std::enable_if<std::is_enum<T>::value, bool>::type
+            inline /*bool*/ setValue(const T& value)
+            {
+                typedef typename std::underlying_type<T>::type UnderlyingType;
+                return this->setValue(reinterpret_cast<const UnderlyingType&>(value));
+            }
+
             virtual bool getValue(char*                 value) const = 0;
             virtual bool getValue(unsigned char*        value) const = 0;
             virtual bool getValue(short*                value) const = 0;
@@ -238,6 +265,20 @@
             virtual bool getValue(orxonox::Radian*      value) const = 0;
             virtual bool getValue(orxonox::Degree*      value) const = 0;
 
+            template <typename T> /* for normal classes */ typename std::enable_if<!std::is_enum<T>::value, bool>::type
+            inline /*bool*/ getValue(T* value) const
+            {
+                // If you reach this code, you used MultiType with an unsupported type T
+                static_assert(sizeof(T) != sizeof(T), "No template specialization available for T");
+                return false;
+            }
+            template <typename T> /* for enum classes */ typename std::enable_if<std::is_enum<T>::value, bool>::type
+            inline /*bool*/ getValue(T* value) const
+            {
+                typedef typename std::underlying_type<T>::type UnderlyingType;
+                return this->getValue(reinterpret_cast<UnderlyingType*>(value));
+            }
+
             template <typename T> T get() const
             {
                 if (this->isType<T>())
@@ -430,11 +471,19 @@
                 this->createNewValueContainer(value);
             }
             /// Creates a new value container (works only with specialized types).
-            template <typename T> inline void createNewValueContainer(const T& value)
+            template <typename T> /* for normal classes */ typename std::enable_if<!std::is_enum<T>::value>::type
+            inline /*void*/ createNewValueContainer(const T& value)
             { 
                 // If you reach this code, you used MultiType with an unsupported type T
                 static_assert(sizeof(T) != sizeof(T), "No template specialization available for T");
             }
+            /// Creates a new value container (implementation for enum classes that must be cast to the underlying type).
+            template <typename T> /* for enum classes */ typename std::enable_if<std::is_enum<T>::value>::type
+            inline /*void*/ createNewValueContainer(const T& value)
+            {
+                typedef typename std::underlying_type<T>::type UnderlyingType;
+                this->createNewValueContainer<UnderlyingType>(reinterpret_cast<const UnderlyingType&>(value));
+            }
 
             MT_ValueBase* value_; //!< A pointer to the value container
     };

Modified: code/branches/cpp11_v2/test/util/MultiTypeTest.cc
===================================================================
--- code/branches/cpp11_v2/test/util/MultiTypeTest.cc	2015-12-30 19:53:54 UTC (rev 11001)
+++ code/branches/cpp11_v2/test/util/MultiTypeTest.cc	2015-12-30 20:13:14 UTC (rev 11002)
@@ -696,4 +696,243 @@
 
         EXPECT_TRUE(mt.null());
     }
+
+    ///////////////////////////////
+    // Strongly typed enum class //
+    ///////////////////////////////
+    enum class EnumWithChar : unsigned char
+    {
+        ValueA = 'A',
+        ValueB = 'B',
+        ValueC = 'C',
+    };
+    enum class EnumWithInt
+    {
+        Value1 = 50,
+        Value2 = 0,
+        Value3,
+    };
+
+    TEST(MultiType, Enum_Constructor)
+    {
+        // Constructor:
+        {
+            MultiType mt = EnumWithChar::ValueA;
+
+            EXPECT_TRUE(mt.isType<EnumWithChar>());
+            EXPECT_EQ(EnumWithChar::ValueA, mt.get<EnumWithChar>());
+        }
+        {
+            MultiType mt = MultiType(EnumWithInt::Value1);
+
+            EXPECT_TRUE(mt.isType<EnumWithInt>());
+            EXPECT_EQ(EnumWithInt::Value1, mt.get<EnumWithInt>());
+        }
+    }
+
+    TEST(MultiType, Enum_Assignment)
+    {
+        // operator=:
+        MultiType mt;
+        mt = EnumWithChar::ValueB;
+
+        EXPECT_TRUE(mt.isType<EnumWithChar>());
+        EXPECT_EQ(EnumWithChar::ValueB, mt.get<EnumWithChar>());
+    }
+
+    TEST(MultiType, Enum_Set)
+    {
+        // set(value):
+        {
+            MultiType mt;
+            mt.set(EnumWithInt::Value2); // assign enum to an empty MultiType
+
+            EXPECT_TRUE(mt.isType<EnumWithInt>());
+            EXPECT_EQ(EnumWithInt::Value2, mt.get<EnumWithInt>());
+        }
+        {
+            MultiType mt = "string";
+            mt.set(EnumWithChar::ValueC); // assign enum to a MultiType with type std::string
+
+            EXPECT_TRUE(mt.isType<std::string>());
+            EXPECT_EQ("C", mt.get<std::string>());
+        }
+        {
+            MultiType mt = EnumWithChar::ValueA;
+            mt.set(EnumWithChar::ValueB); // assign enum to a MultiType with type std::string
+
+            EXPECT_TRUE(mt.isType<EnumWithChar>());
+            EXPECT_EQ(EnumWithChar::ValueB, mt.get<EnumWithChar>());
+        }
+        {
+            MultiType mt = EnumWithInt::Value3;
+            mt.set("50"); // assign enum to a MultiType with type std::string
+
+            EXPECT_TRUE(mt.isType<EnumWithInt>());
+            EXPECT_EQ(EnumWithInt::Value1, mt.get<EnumWithInt>());
+        }
+    }
+
+    TEST(MultiType, Enum_Force)
+    {
+        // force(value):
+        {
+            MultiType mt = "string";
+            EXPECT_TRUE(mt.isType<std::string>());
+            EXPECT_EQ("string", mt.get<std::string>());
+
+            mt.force<EnumWithChar>("C");
+
+            EXPECT_TRUE(mt.isType<EnumWithChar>());
+            EXPECT_EQ(EnumWithChar::ValueC, mt.get<EnumWithChar>());
+        }
+        {
+            MultiType mt = EnumWithChar::ValueA;
+            EXPECT_TRUE(mt.isType<EnumWithChar>());
+            EXPECT_EQ(EnumWithChar::ValueA, mt.get<EnumWithChar>());
+
+            mt.force<std::string>(EnumWithChar::ValueB);
+
+            EXPECT_TRUE(mt.isType<std::string>());
+            EXPECT_EQ("B", mt.get<std::string>());
+        }
+    }
+
+    TEST(MultiType, Enum_Convert)
+    {
+        // convert():
+        {
+            MultiType mt = "C";
+            mt.convert<EnumWithChar>();
+
+            EXPECT_TRUE(mt.isType<EnumWithChar>());
+            EXPECT_EQ(EnumWithChar::ValueC, mt.get<EnumWithChar>());
+        }
+        {
+            MultiType mt = EnumWithChar::ValueA;
+            mt.convert<std::string>();
+
+            EXPECT_TRUE(mt.isType<std::string>());
+            EXPECT_EQ("A", mt.get<std::string>());
+        }
+    }
+
+    TEST(MultiType, Enum_Reset)
+    {
+        // reset():
+        {
+            MultiType mt = EnumWithChar::ValueA;
+            mt.reset<EnumWithChar>();
+
+            EXPECT_TRUE(mt.isType<EnumWithChar>());
+            EXPECT_TRUE(mt.isType<unsigned char>());
+            EXPECT_EQ('\0', mt.get<unsigned char>());
+        }
+        {
+            MultiType mt = "string";
+            mt.reset<EnumWithInt>();
+
+            EXPECT_TRUE(mt.isType<EnumWithInt>());
+            EXPECT_TRUE(mt.isType<int>());
+            EXPECT_EQ(0, mt.get<int>());
+        }
+    }
+
+    TEST(MultiType, Enum_IsType)
+    {
+        // isType():
+        {
+            MultiType mt = EnumWithChar::ValueB;
+            EXPECT_TRUE(mt.isType<EnumWithChar>());
+            EXPECT_TRUE(mt.isType<unsigned char>());
+            EXPECT_FALSE(mt.isType<char>());
+            EXPECT_FALSE(mt.isType<int>());
+            EXPECT_FALSE(mt.isType<bool>());
+            EXPECT_FALSE(mt.isType<std::string>());
+        }
+        {
+            MultiType mt = EnumWithInt::Value3;
+            EXPECT_TRUE(mt.isType<EnumWithInt>());
+            EXPECT_TRUE(mt.isType<int>());
+            EXPECT_FALSE(mt.isType<unsigned char>());
+            EXPECT_FALSE(mt.isType<char>());
+            EXPECT_FALSE(mt.isType<bool>());
+            EXPECT_FALSE(mt.isType<std::string>());
+        }
+    }
+
+    TEST(MultiType, Enum_ConversionOperator)
+    {
+        // conversion operator:
+        {
+            MultiType mt = EnumWithChar::ValueA;
+            EnumWithChar value = mt;
+            EXPECT_EQ(EnumWithChar::ValueA, value);
+        }
+        {
+            MultiType mt = 'B';
+            EnumWithChar value = mt;
+            EXPECT_EQ(EnumWithChar::ValueB, value);
+        }
+        {
+            MultiType mt = EnumWithInt::Value1;
+            std::string value = mt;
+            EXPECT_EQ("50", value);
+        }
+    }
+
+    TEST(MultiType, Enum_GetValue)
+    {
+        // getValue():
+        {
+            MultiType mt = EnumWithChar::ValueA;
+            EnumWithChar value;
+            mt.getValue(&value);
+            EXPECT_EQ(EnumWithChar::ValueA, value);
+        }
+        {
+            MultiType mt = 'B';
+            EnumWithChar value;
+            mt.getValue(&value);
+            EXPECT_EQ(EnumWithChar::ValueB, value);
+        }
+        {
+            MultiType mt = EnumWithInt::Value1;
+            std::string value;
+            mt.getValue(&value);
+            EXPECT_EQ("50", value);
+        }
+    }
+
+    TEST(MultiType, Enum_Get)
+    {
+        // get():
+        {
+            MultiType mt = EnumWithChar::ValueB;
+            EXPECT_EQ(EnumWithChar::ValueB, mt.get<EnumWithChar>());
+
+            EXPECT_EQ('B', mt.get<unsigned char>());
+            EXPECT_EQ("B", mt.get<std::string>());
+            EXPECT_EQ(66, mt.get<int>());
+            EXPECT_TRUE(mt.get<bool>());
+        }
+        {
+            MultiType mt = EnumWithInt::Value1;
+            EXPECT_EQ(EnumWithInt::Value1, mt.get<EnumWithInt>());
+
+            EXPECT_EQ('2', mt.get<unsigned char>());
+            EXPECT_EQ("50", mt.get<std::string>());
+            EXPECT_EQ(50, mt.get<int>());
+            EXPECT_TRUE(mt.get<bool>());
+        }
+        {
+            MultiType mt = EnumWithInt::Value2;
+            EXPECT_EQ(EnumWithInt::Value2, mt.get<EnumWithInt>());
+
+            EXPECT_EQ('\0', mt.get<unsigned char>());
+            EXPECT_EQ("0", mt.get<std::string>());
+            EXPECT_EQ(0, mt.get<int>());
+            EXPECT_FALSE(mt.get<bool>());
+        }
+    }
 }




More information about the Orxonox-commit mailing list