Browse Source

Make Variant able to store any type.

Eugene Kozlov 8 years ago
parent
commit
3fdc30fa6a

+ 2 - 1
Source/Urho3D/Core/Context.cpp

@@ -196,7 +196,8 @@ void Context::RemoveSubsystem(StringHash objectType)
 void Context::RegisterAttribute(StringHash objectType, const AttributeInfo& attr)
 void Context::RegisterAttribute(StringHash objectType, const AttributeInfo& attr)
 {
 {
     // None or pointer types can not be supported
     // None or pointer types can not be supported
-    if (attr.type_ == VAR_NONE || attr.type_ == VAR_VOIDPTR || attr.type_ == VAR_PTR)
+    if (attr.type_ == VAR_NONE || attr.type_ == VAR_VOIDPTR || attr.type_ == VAR_PTR
+        || attr.type_ == VAR_CUSTOM_HEAP || attr.type_ == VAR_CUSTOM_STACK)
     {
     {
         URHO3D_LOGWARNING("Attempt to register unsupported attribute type " + Variant::GetTypeName(attr.type_) + " to class " +
         URHO3D_LOGWARNING("Attempt to register unsupported attribute type " + Variant::GetTypeName(attr.type_) + " to class " +
             GetTypeName(objectType));
             GetTypeName(objectType));

+ 39 - 3
Source/Urho3D/Core/Variant.cpp

@@ -67,6 +67,8 @@ static const char* typeNames[] =
     "Rect",
     "Rect",
     "IntVector3",
     "IntVector3",
     "Int64",
     "Int64",
+    "CustomPtr",
+    "Custom",
     0
     0
 };
 };
 
 
@@ -122,6 +124,11 @@ Variant& Variant::operator =(const Variant& rhs)
         *value_.matrix4_ = *rhs.value_.matrix4_;
         *value_.matrix4_ = *rhs.value_.matrix4_;
         break;
         break;
 
 
+    case VAR_CUSTOM_HEAP:
+    case VAR_CUSTOM_STACK:
+        SetCustomValue(*rhs.GetCustomVariantValue());
+        break;
+
     default:
     default:
         memcpy(&value_, &rhs.value_, sizeof(VariantValue));
         memcpy(&value_, &rhs.value_, sizeof(VariantValue));
         break;
         break;
@@ -168,7 +175,7 @@ bool Variant::operator ==(const Variant& rhs) const
         return value_.vector4_ == rhs.value_.vector4_;
         return value_.vector4_ == rhs.value_.vector4_;
 
 
     case VAR_QUATERNION:
     case VAR_QUATERNION:
-        return value_.quaterion_ == rhs.value_.quaterion_;
+        return value_.quaternion_ == rhs.value_.quaternion_;
 
 
     case VAR_COLOR:
     case VAR_COLOR:
         return value_.color_ == rhs.value_.color_;
         return value_.color_ == rhs.value_.color_;
@@ -425,7 +432,7 @@ String Variant::ToString() const
         return value_.vector4_.ToString();
         return value_.vector4_.ToString();
 
 
     case VAR_QUATERNION:
     case VAR_QUATERNION:
-        return value_.quaterion_.ToString();
+        return value_.quaternion_.ToString();
 
 
     case VAR_COLOR:
     case VAR_COLOR:
         return value_.color_.ToString();
         return value_.color_.ToString();
@@ -470,6 +477,13 @@ String Variant::ToString() const
     case VAR_RECT:
     case VAR_RECT:
         return value_.rect_.ToString();
         return value_.rect_.ToString();
 
 
+    case VAR_CUSTOM_HEAP:
+    case VAR_CUSTOM_STACK:
+        if (const CustomVariantValue* custom = GetCustomVariantValue())
+            return custom->ToString();
+        else
+            return String::EMPTY;
+
     default:
     default:
         // VAR_RESOURCEREF, VAR_RESOURCEREFLIST, VAR_VARIANTVECTOR, VAR_STRINGVECTOR, VAR_VARIANTMAP
         // VAR_RESOURCEREF, VAR_RESOURCEREFLIST, VAR_VARIANTVECTOR, VAR_STRINGVECTOR, VAR_VARIANTMAP
         // Reference string serialization requires typehash-to-name mapping from the context. Can not support here
         // Reference string serialization requires typehash-to-name mapping from the context. Can not support here
@@ -504,7 +518,7 @@ bool Variant::IsZero() const
         return value_.vector4_ == Vector4::ZERO;
         return value_.vector4_ == Vector4::ZERO;
 
 
     case VAR_QUATERNION:
     case VAR_QUATERNION:
-        return value_.quaterion_ == Quaternion::IDENTITY;
+        return value_.quaternion_ == Quaternion::IDENTITY;
 
 
     case VAR_COLOR:
     case VAR_COLOR:
         // WHITE is considered empty (i.e. default) color in the Color class definition
         // WHITE is considered empty (i.e. default) color in the Color class definition
@@ -569,6 +583,10 @@ bool Variant::IsZero() const
     case VAR_RECT:
     case VAR_RECT:
         return value_.rect_ == Rect::ZERO;
         return value_.rect_ == Rect::ZERO;
 
 
+    case VAR_CUSTOM_HEAP:
+    case VAR_CUSTOM_STACK:
+        return false;
+
     default:
     default:
         return true;
         return true;
     }
     }
@@ -625,6 +643,14 @@ void Variant::SetType(VariantType newType)
         delete value_.matrix4_;
         delete value_.matrix4_;
         break;
         break;
 
 
+    case VAR_CUSTOM_HEAP:
+        delete value_.customValueHeap_;
+        break;
+
+    case VAR_CUSTOM_STACK:
+        static_cast<CustomVariantValue&>(value_.customValueStack_).~CustomVariantValue();
+        break;
+
     default:
     default:
         break;
         break;
     }
     }
@@ -677,6 +703,16 @@ void Variant::SetType(VariantType newType)
         value_.matrix4_ = new Matrix4();
         value_.matrix4_ = new Matrix4();
         break;
         break;
 
 
+    case VAR_CUSTOM_HEAP:
+        // Will be filled later
+        value_.customValueHeap_ = nullptr;
+        break;
+
+    case VAR_CUSTOM_STACK:
+        // Initialize virtual table with any custom object
+        new (&value_.customValueStack_) CustomVariantValueImpl<void*>(nullptr);
+        break;
+
     default:
     default:
         break;
         break;
     }
     }

+ 217 - 5
Source/Urho3D/Core/Variant.h

@@ -30,6 +30,8 @@
 #include "../Math/Rect.h"
 #include "../Math/Rect.h"
 #include "../Math/StringHash.h"
 #include "../Math/StringHash.h"
 
 
+#include <typeinfo>
+
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
@@ -63,6 +65,8 @@ enum VariantType
     VAR_RECT,
     VAR_RECT,
     VAR_INTVECTOR3,
     VAR_INTVECTOR3,
     VAR_INT64,
     VAR_INT64,
+    VAR_CUSTOM_HEAP,
+    VAR_CUSTOM_STACK,
     MAX_VAR_TYPES
     MAX_VAR_TYPES
 };
 };
 
 
@@ -165,6 +169,81 @@ struct URHO3D_API ResourceRefList
     bool operator !=(const ResourceRefList& rhs) const { return type_ != rhs.type_ || names_ != rhs.names_; }
     bool operator !=(const ResourceRefList& rhs) const { return type_ != rhs.type_ || names_ != rhs.names_; }
 };
 };
 
 
+/// Custom variant value.
+class CustomVariantValue
+{
+protected:
+    /// Construct from type info.
+    CustomVariantValue(const std::type_info& typeInfo) : typeInfo_(typeInfo) {}
+
+public:
+    /// Destruct.
+    virtual ~CustomVariantValue() { }
+    /// Get the type info.
+    const std::type_info& GetTypeInfo() const { return typeInfo_; }
+    /// Return whether the specified type is stored.
+    template <class T> bool IsType() const { return GetTypeInfo() == typeid(T); }
+    /// Return pointer to value of the specified type. Return null pointer if type does not match.
+    template <class T> T* GetValuePtr();
+    /// Return const pointer to value of the specified type. Return null pointer if type does not match.
+    template <class T> const T* GetValuePtr() const;
+
+    /// Assign value.
+    virtual bool Assign(const CustomVariantValue& rhs) = 0;
+    /// Clone.
+    virtual CustomVariantValue* Clone() const = 0;
+    /// Placement clone.
+    virtual void Clone(void* dest) const = 0;
+    /// Get size.
+    virtual unsigned GetSize() const = 0;
+
+    /// Convert custom value to string.
+    virtual String ToString() const = 0;
+
+private:
+    /// Type info.
+    const std::type_info& typeInfo_;
+};
+
+/// Custom variant value implementation. Specialize member functions to change behavior.
+template <class T> class CustomVariantValueImpl final : public CustomVariantValue
+{
+public:
+    /// Construct from value.
+    CustomVariantValueImpl(const T& value) : CustomVariantValue(typeid(T)), value_(value) {}
+    /// Set value.
+    void SetValue(const T& value) { value_ = value; }
+    /// Get value.
+    T& GetValue() { return value_; }
+    /// Get const value.
+    const T& GetValue() const { return value_; }
+
+    /// Assign value.
+    virtual bool Assign(const CustomVariantValue& rhs) override
+    {
+        if (GetTypeInfo() == rhs.GetTypeInfo())
+        {
+            const auto& rhsImpl = static_cast<const CustomVariantValueImpl<T>&>(rhs);
+            SetValue(rhsImpl.GetValue());
+            return true;
+        }
+        return false;
+    }
+    /// Clone.
+    virtual CustomVariantValue* Clone() const override { return new CustomVariantValueImpl<T>(value_); }
+    /// Placement clone.
+    virtual void Clone(void* dest) const override { new (dest) CustomVariantValueImpl<T>(value_); }
+    /// Get size.
+    virtual unsigned GetSize() const override { return sizeof(CustomVariantValueImpl<T>); }
+
+    /// Convert custom value to string.
+    virtual String ToString() const override { return String::EMPTY; }
+
+private:
+    /// Value.
+    T value_;
+};
+
 /// Size of variant value. 16 bytes on 32-bit platform, 32 bytes on 64-bit platform.
 /// Size of variant value. 16 bytes on 32-bit platform, 32 bytes on 64-bit platform.
 static const unsigned VARIANT_VALUE_SIZE = sizeof(void*) * 4;
 static const unsigned VARIANT_VALUE_SIZE = sizeof(void*) * 4;
 
 
@@ -178,7 +257,7 @@ union VariantValue
     Vector2 vector2_;
     Vector2 vector2_;
     Vector3 vector3_;
     Vector3 vector3_;
     Vector4 vector4_;
     Vector4 vector4_;
-    Quaternion quaterion_;
+    Quaternion quaternion_;
     Color color_;
     Color color_;
     String string_;
     String string_;
     PODVector<unsigned char> buffer_;
     PODVector<unsigned char> buffer_;
@@ -198,6 +277,8 @@ union VariantValue
     Rect rect_;
     Rect rect_;
     IntVector3 intVector3_;
     IntVector3 intVector3_;
     long long int64_;
     long long int64_;
+    CustomVariantValue* customValueHeap_;
+    CustomVariantValueImpl<void*> customValueStack_;
 
 
     /// Construct uninitialized.
     /// Construct uninitialized.
     VariantValue() { }
     VariantValue() { }
@@ -402,6 +483,13 @@ public:
         *this = value;
         *this = value;
     }
     }
 
 
+    /// Construct from custom value.
+    template <class T>
+    Variant(const CustomVariantValueImpl<T>& value)
+    {
+        *this = value;
+    }
+
     /// Construct from type and value.
     /// Construct from type and value.
     Variant(const String& type, const String& value)
     Variant(const String& type, const String& value)
     {
     {
@@ -504,7 +592,7 @@ public:
     }
     }
 
 
     /// Assign from a double.
     /// Assign from a double.
-    Variant& operator = (double rhs)
+    Variant& operator =(double rhs)
     {
     {
         SetType(VAR_DOUBLE);
         SetType(VAR_DOUBLE);
         value_.double_ = rhs;
         value_.double_ = rhs;
@@ -539,7 +627,7 @@ public:
     Variant& operator =(const Quaternion& rhs)
     Variant& operator =(const Quaternion& rhs)
     {
     {
         SetType(VAR_QUATERNION);
         SetType(VAR_QUATERNION);
-        value_.quaterion_ = rhs;
+        value_.quaternion_ = rhs;
         return *this;
         return *this;
     }
     }
 
 
@@ -690,6 +778,14 @@ public:
         return *this;
         return *this;
     }
     }
 
 
+    /// Assign from custom value.
+    template <class T>
+    Variant& operator =(const CustomVariantValueImpl<T>& value)
+    {
+        SetCustomValue(value.GetValue());
+        return *this;
+    }
+
     /// Test for equality with another variant.
     /// Test for equality with another variant.
     bool operator ==(const Variant& rhs) const;
     bool operator ==(const Variant& rhs) const;
 
 
@@ -735,7 +831,7 @@ public:
     /// Test for equality with a quaternion. To return true, both the type and value must match.
     /// Test for equality with a quaternion. To return true, both the type and value must match.
     bool operator ==(const Quaternion& rhs) const
     bool operator ==(const Quaternion& rhs) const
     {
     {
-        return type_ == VAR_QUATERNION ? value_.quaterion_ == rhs : false;
+        return type_ == VAR_QUATERNION ? value_.quaternion_ == rhs : false;
     }
     }
 
 
     /// Test for equality with a color. To return true, both the type and value must match.
     /// Test for equality with a color. To return true, both the type and value must match.
@@ -952,6 +1048,59 @@ public:
     void FromString(VariantType type, const char* value);
     void FromString(VariantType type, const char* value);
     /// Set buffer type from a memory area.
     /// Set buffer type from a memory area.
     void SetBuffer(const void* data, unsigned size);
     void SetBuffer(const void* data, unsigned size);
+    /// Set custom value.
+    void SetCustomValue(const CustomVariantValue& value)
+    {
+        // Assign value if destination is already initialized
+        if (CustomVariantValue* custom = GetCustomVariantValue())
+        {
+            if (custom->GetTypeInfo() == value.GetTypeInfo())
+            {
+                custom->Assign(value);
+                return;
+            }
+        }
+
+        if (value.GetSize() <= VARIANT_VALUE_SIZE)
+        {
+            SetType(VAR_CUSTOM_STACK);
+            static_cast<CustomVariantValue&>(value_.customValueStack_).~CustomVariantValue();
+            value.Clone(&value_.customValueStack_);
+        }
+        else
+        {
+            SetType(VAR_CUSTOM_HEAP);
+            delete value_.customValueHeap_;
+            value_.customValueHeap_ = value.Clone();
+        }
+    }
+    /// Set custom value.
+    template <class T> void SetCustomValue(const T& value)
+    {
+        // Assign value if destination is already initialized
+        if (CustomVariantValue* custom = GetCustomVariantValue())
+        {
+            if (custom->IsType<T>())
+            {
+                auto customImpl = static_cast<CustomVariantValueImpl<T>*>(custom);
+                customImpl->SetValue(value);
+                return;
+            }
+        }
+
+        if (sizeof(CustomVariantValueImpl<T>) <= VARIANT_VALUE_SIZE)
+        {
+            SetType(VAR_CUSTOM_STACK);
+            static_cast<CustomVariantValue&>(value_.customValueStack_).~CustomVariantValue();
+            new (&value_.customValueStack_) CustomVariantValueImpl<T>(value);
+        }
+        else
+        {
+            SetType(VAR_CUSTOM_HEAP);
+            delete value_.customValueHeap_;
+            value_.customValueHeap_ = new CustomVariantValueImpl<T>(value);
+        }
+    }
 
 
     /// Return int or zero on type mismatch. Floats and doubles are converted.
     /// Return int or zero on type mismatch. Floats and doubles are converted.
     int GetInt() const
     int GetInt() const
@@ -1057,7 +1206,7 @@ public:
     /// Return quaternion or identity on type mismatch.
     /// Return quaternion or identity on type mismatch.
     const Quaternion& GetQuaternion() const
     const Quaternion& GetQuaternion() const
     {
     {
-        return type_ == VAR_QUATERNION ? value_.quaterion_ : Quaternion::IDENTITY;
+        return type_ == VAR_QUATERNION ? value_.quaternion_ : Quaternion::IDENTITY;
     }
     }
 
 
     /// Return color or default on type mismatch. Vector4 is aliased to Color if necessary.
     /// Return color or default on type mismatch. Vector4 is aliased to Color if necessary.
@@ -1158,6 +1307,34 @@ public:
         return type_ == VAR_MATRIX4 ? *value_.matrix4_ : Matrix4::IDENTITY;
         return type_ == VAR_MATRIX4 ? *value_.matrix4_ : Matrix4::IDENTITY;
     }
     }
 
 
+    /// Return pointer to custom variant value.
+    CustomVariantValue* GetCustomVariantValue()
+    {
+        return const_cast<CustomVariantValue*>(const_cast<const Variant*>(this)->GetCustomVariantValue());
+    }
+
+    /// Return const pointer to custom variant value.
+    const CustomVariantValue* GetCustomVariantValue() const
+    {
+        if (type_ == VAR_CUSTOM_HEAP)
+            return value_.customValueHeap_;
+        else if (type_ == VAR_CUSTOM_STACK)
+            return &value_.customValueStack_;
+        else
+            return nullptr;
+    }
+
+    /// Return custom variant value or default-constructed on type mismatch.
+    template <class T> T GetCustom() const
+    {
+        if (const CustomVariantValue* value = GetCustomVariantValue())
+        {
+            if (value->IsType<T>())
+                return *value->GetValuePtr<T>();
+        }
+        return T();
+    }
+
     /// Return value's type.
     /// Return value's type.
     VariantType GetType() const { return type_; }
     VariantType GetType() const { return type_; }
 
 
@@ -1189,6 +1366,17 @@ public:
     /// Return a pointer to a modifiable variant map or null on type mismatch.
     /// Return a pointer to a modifiable variant map or null on type mismatch.
     VariantMap* GetVariantMapPtr() { return type_ == VAR_VARIANTMAP ? &value_.variantMap_ : 0; }
     VariantMap* GetVariantMapPtr() { return type_ == VAR_VARIANTMAP ? &value_.variantMap_ : 0; }
 
 
+    /// Return a pointer to a modifiable custom variant value or null on type mismatch.
+    template <class T> T* GetCustomPtr()
+    {
+        if (const CustomVariantValue* value = GetCustomVariantValue())
+        {
+            if (value->IsType<T>())
+                return value->GetValuePtr<T>();
+        }
+        return nullptr;
+    }
+
     /// Return name for variant type.
     /// Return name for variant type.
     static String GetTypeName(VariantType type);
     static String GetTypeName(VariantType type);
     /// Return variant type from type name.
     /// Return variant type from type name.
@@ -1221,6 +1409,9 @@ private:
     VariantValue value_;
     VariantValue value_;
 };
 };
 
 
+/// Make custom variant value.
+template <typename T> CustomVariantValueImpl<T> MakeCustomValue(const T& value) { return CustomVariantValueImpl<T>(value); }
+
 /// Return variant type from type.
 /// Return variant type from type.
 template <typename T> VariantType GetVariantType();
 template <typename T> VariantType GetVariantType();
 
 
@@ -1366,4 +1557,25 @@ template <> URHO3D_API Matrix3x4 Variant::Get<Matrix3x4>() const;
 
 
 template <> URHO3D_API Matrix4 Variant::Get<Matrix4>() const;
 template <> URHO3D_API Matrix4 Variant::Get<Matrix4>() const;
 
 
+// Implementations
+template <class T> T* CustomVariantValue::GetValuePtr()
+{
+    if (IsType<T>())
+    {
+        auto impl = static_cast<CustomVariantValueImpl<T>*>(this);
+        return &impl.GetValue();
+    }
+    return nullptr;
+}
+
+template <class T> const T* CustomVariantValue::GetValuePtr() const
+{
+    if (IsType<T>())
+    {
+        auto impl = static_cast<const CustomVariantValueImpl<T>*>(this);
+        return &impl.GetValue();
+    }
+    return nullptr;
+}
+
 }
 }

+ 7 - 1
Source/Urho3D/IO/Deserializer.cpp

@@ -386,8 +386,14 @@ Variant Deserializer::ReadVariant(VariantType type)
     case VAR_DOUBLE:
     case VAR_DOUBLE:
         return Variant(ReadDouble());
         return Variant(ReadDouble());
 
 
+        // Deserializing custom values is not supported. Return empty
+    case VAR_CUSTOM_HEAP:
+    case VAR_CUSTOM_STACK:
+        ReadUInt();
+        return Variant::EMPTY;
+
     default:
     default:
-        return Variant();
+        return Variant::EMPTY;
     }
     }
 }
 }
 
 

+ 3 - 1
Source/Urho3D/IO/Serializer.cpp

@@ -286,9 +286,11 @@ bool Serializer::WriteVariantData(const Variant& value)
     case VAR_BUFFER:
     case VAR_BUFFER:
         return WriteBuffer(value.GetBuffer());
         return WriteBuffer(value.GetBuffer());
 
 
-        // Serializing pointers is not supported. Write null
+        // Serializing pointers and custom values is not supported. Write null
     case VAR_VOIDPTR:
     case VAR_VOIDPTR:
     case VAR_PTR:
     case VAR_PTR:
+    case VAR_CUSTOM_HEAP:
+    case VAR_CUSTOM_STACK:
         return WriteUInt(0);
         return WriteUInt(0);
 
 
     case VAR_RESOURCEREF:
     case VAR_RESOURCEREF: