2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/public-api/object/property-value.h>
27 #include <dali/integration-api/debug.h>
28 #include <dali/public-api/common/extents.h>
29 #include <dali/public-api/math/angle-axis.h>
30 #include <dali/public-api/math/matrix.h>
31 #include <dali/public-api/math/matrix3.h>
32 #include <dali/public-api/math/quaternion.h>
33 #include <dali/public-api/math/radian.h>
34 #include <dali/public-api/math/rect.h>
35 #include <dali/public-api/math/vector2.h>
36 #include <dali/public-api/math/vector3.h>
37 #include <dali/public-api/math/vector4.h>
38 #include <dali/public-api/object/property-array.h>
39 #include <dali/public-api/object/property-map.h>
40 #include <dali/public-api/object/property-types.h>
43 * As the Implementation is bit complex because of Small Buffer Optimization (SBO)
44 * In the Property::Value class and Tagged Union implementation in the Impl class.
45 * Here is the brief description of the motivation and internal of this implementation.
47 * The main motivation is to keep the value semantic without doing any heap allocation.
48 * 1. In the public class we keep a aligned buffer of 16byte instead of keeping the Pimpl pointer.
49 * 2. we create the Impl object inplace in that buffer to avoid the memory allocation.
50 * 3. we don't store the Impl* along with the SBO storage to save 8byte from the Property::Value object.
51 * 4. Every time we need to access Impl object we access it through Read() and Write() which retrieves
52 * the already constructed Impl object from the SBO storage.
53 * 5. with SBO , the move and assignment operator are tricky , so we use std::copy to move the object
54 * and update the moved from object to NONE to keep it in a safe state.
55 * 6. In the Impl class, we keep a Uninitialized Union and manage lifecycle of objects by doing construct/destroy manually.
56 * 7. We keep the Type information along with the Object to keep the size as 16byte (using common initial sequence(CIS) ).
61 struct Property::Value::Impl
63 template<typename... Args>
64 static Impl* New(Property::Value::Storage& buffer, Args... args)
66 return new(&buffer) Impl(std::forward<Args>(args)...);
69 static void Delete(Property::Value::Impl& impl)
76 // Destroy the current object stored in Data union memory.
80 Impl(const Impl&) = delete;
82 Impl(Impl&&) = delete;
84 Impl& operator=(Impl&&) = delete;
88 static_assert(sizeof(Impl) == 16);
89 static_assert(alignof(Impl) == alignof(Impl*));
91 SetType(Property::NONE);
94 Impl(bool booleanValue)
96 SetType(Property::BOOLEAN);
97 mData.mBool.member = booleanValue;
100 Impl(float floatValue)
102 SetType(Property::FLOAT);
103 mData.mFloat.member = floatValue;
106 Impl(int32_t integerValue)
108 SetType(Property::INTEGER);
109 mData.mInt.member = integerValue;
112 Impl(Vector2 vectorValue)
114 SetType(Property::VECTOR2);
115 ConstructInplace(mData.mVector2.member, std::move(vectorValue));
118 Impl(Vector3 vectorValue)
120 SetType(Property::VECTOR3);
121 ConstructInplace(mData.mVector3.member, std::move(vectorValue));
124 Impl(Extents extentsValue)
126 SetType(Property::EXTENTS);
127 ConstructInplace(mData.mExtents.member, std::move(extentsValue));
130 Impl(Property::Map mapValue)
132 SetType(Property::MAP);
133 ConstructInplace(mData.mMap.member, std::move(mapValue));
136 Impl(Property::Array arrayValue)
138 SetType(Property::ARRAY);
139 ConstructInplace(mData.mArray.member, std::move(arrayValue));
142 Impl(std::string stringValue)
144 SetType(Property::STRING);
145 mData.mString.member = new std::string(std::move(stringValue));
148 Impl(Rect<int32_t> rectValue)
150 SetType(Property::RECTANGLE);
151 mData.mRect.member = new Rect<int32_t>(std::move(rectValue));
154 Impl(Vector4 vectorValue)
156 SetType(Property::VECTOR4);
157 mData.mVector4.member = new Vector4(std::move(vectorValue));
160 Impl(Matrix3 matrixValue)
162 SetType(Property::MATRIX3);
163 mData.mMatrix3.member = new Matrix3(std::move(matrixValue));
166 Impl(Matrix matrixValue)
168 SetType(Property::MATRIX);
169 mData.mMatrix.member = new Matrix(std::move(matrixValue));
172 Impl(AngleAxis angleAxisValue)
174 SetType(Property::ROTATION);
175 mData.mAngleAxis.member = new AngleAxis(std::move(angleAxisValue));
180 return mData.mType.type;
185 return mData.mBool.member;
188 int32_t GetInt() const
190 return mData.mInt.member;
193 float GetFloat() const
195 return mData.mFloat.member;
198 const Extents& GetExtents() const
200 return mData.mExtents.member;
203 const Vector2& GetVector2() const
205 return mData.mVector2.member;
208 const Vector3& GetVector3() const
210 return mData.mVector3.member;
213 const Property::Map& GetMap() const
215 return mData.mMap.member;
218 const Property::Array& GetArray() const
220 return mData.mArray.member;
223 const Vector4& GetVector4() const
225 return *(mData.mVector4.member);
228 const Matrix3& GetMatrix3() const
230 return *(mData.mMatrix3.member);
233 const Matrix& GetMatrix() const
235 return *(mData.mMatrix.member);
238 const AngleAxis& GetAngleAxis() const
240 return *(mData.mAngleAxis.member);
243 const std::string& GetString() const
245 return *(mData.mString.member);
248 const Rect<int32_t>& GetRect() const
250 return *(mData.mRect.member);
253 Property::Map* GetMapPtr()
255 return &(mData.mMap.member);
258 const Property::Map* GetMapPtr() const
260 return &(mData.mMap.member);
263 Property::Array* GetArrayPtr()
265 return &(mData.mArray.member);
268 const Property::Array* GetArrayPtr() const
270 return &(mData.mArray.member);
273 Impl& operator=(const Impl& other)
275 const bool isSameType = GetType() == other.GetType();
280 SetType(other.GetType());
289 case Property::BOOLEAN:
291 mData.mBool.member = other.GetBool();
294 case Property::FLOAT:
296 mData.mFloat.member = other.GetFloat();
299 case Property::INTEGER:
301 mData.mInt.member = other.GetInt();
304 case Property::EXTENTS:
306 auto obj = other.GetExtents();
307 ConstructInplace(mData.mExtents.member, std::move(obj));
310 case Property::VECTOR2:
312 auto obj = other.GetVector2();
313 ConstructInplace(mData.mVector2.member, std::move(obj));
316 case Property::VECTOR3:
318 auto obj = other.GetVector3();
319 ConstructInplace(mData.mVector3.member, std::move(obj));
322 case Property::ARRAY:
326 mData.mArray.member = other.GetArray();
330 auto obj = other.GetArray();
331 ConstructInplace(mData.mArray.member, std::move(obj));
339 mData.mMap.member = other.GetMap();
343 auto obj = other.GetMap();
344 ConstructInplace(mData.mMap.member, std::move(obj));
348 case Property::VECTOR4:
352 *mData.mVector4.member = other.GetVector4();
356 mData.mVector4.member = new Vector4(other.GetVector4());
360 case Property::MATRIX3:
364 *mData.mMatrix3.member = other.GetMatrix3();
368 mData.mMatrix3.member = new Matrix3(other.GetMatrix3());
372 case Property::MATRIX:
376 *mData.mMatrix.member = other.GetMatrix();
380 mData.mMatrix.member = new Matrix(other.GetMatrix());
384 case Property::RECTANGLE:
388 *mData.mRect.member = other.GetRect();
392 mData.mRect.member = new Rect<int32_t>(other.GetRect());
396 case Property::ROTATION:
400 *mData.mAngleAxis.member = other.GetAngleAxis();
404 mData.mAngleAxis.member = new AngleAxis(other.GetAngleAxis());
408 case Property::STRING:
412 *mData.mString.member = other.GetString();
416 mData.mString.member = new std::string(other.GetString());
424 void SetType(Type typeValue)
426 mData.mType.type = typeValue;
431 * This helper function takes a typed(Tp) memory location( member)
432 * and a object of same type( val ) and move constructs a new object of
433 * same type(Tp) in the memory location( member) using placement new.
434 * after this function call member location will have a object of type Tp.
436 template<typename Tp>
437 void ConstructInplace(Tp& member, Tp&& val)
439 new(&member) Tp(std::forward<Tp>(val));
443 * Destroy the object created in the Data union memory by probing the
444 * type and calling the appropriate destructor.
445 * and also reset the type and memory location to reflect that .
452 case Property::BOOLEAN:
453 case Property::FLOAT:
454 case Property::INTEGER:
456 break; // nothing to do
458 case Property::EXTENTS:
460 mData.mExtents.member.~Extents();
463 case Property::VECTOR2:
465 mData.mVector2.member.~Vector2();
468 case Property::VECTOR3:
470 mData.mVector3.member.~Vector3();
473 case Property::ARRAY:
475 using array = Property::Array;
476 mData.mArray.member.~array();
481 using map = Property::Map;
482 mData.mMap.member.~map();
485 case Property::VECTOR4:
487 delete mData.mVector4.member;
490 case Property::MATRIX3:
492 delete mData.mMatrix3.member;
495 case Property::MATRIX:
497 delete mData.mMatrix.member;
500 case Property::RECTANGLE:
502 delete mData.mRect.member;
505 case Property::ROTATION:
507 delete mData.mAngleAxis.member;
510 case Property::STRING:
512 delete mData.mString.member;
519 * This wrapper struct is used for
520 * storing Type in every union member
521 * and can acess it from non active member
522 * of the uninon without invoking UB. this is
523 * possible because of CIS(common initial sequence)
524 * http://eel.is/c++draft/class.mem#general-25
534 * Tagged union implementation.
536 * This Data union contains non trivial data
537 * types Map and Array, the default constructor
538 * and destructors are deleted by the compiler
539 * so we provided empty constructor and destructor
540 * just to pacify the compiler.
541 * The only job of this union to give a typed memory buffer to the
542 * Impl class which can construct the appropriate object
543 * using placement new.
544 * As Impl class explicitly construct the object and keeps track of the
545 * object it creates and then destroys them in the ~Impl() this will not leak
557 UnionMember<bool> mBool;
558 UnionMember<int32_t> mInt;
559 UnionMember<float> mFloat;
560 UnionMember<Extents> mExtents;
561 UnionMember<Vector2> mVector2;
562 UnionMember<Vector3> mVector3;
563 UnionMember<Property::Map> mMap;
564 UnionMember<Property::Array> mArray;
565 UnionMember<Vector4*> mVector4;
566 UnionMember<Matrix3*> mMatrix3;
567 UnionMember<Matrix*> mMatrix;
568 UnionMember<AngleAxis*> mAngleAxis;
569 UnionMember<std::string*> mString;
570 UnionMember<Rect<int32_t>*> mRect;
581 * Safe way to read an already constructed Object from a buffer.
583 const Property::Value::Impl& Property::Value::Read() const
585 return *(std::launder(reinterpret_cast<const Property::Value::Impl*>(mStorage.buffer)));
588 Property::Value::Impl& Property::Value::Write()
590 return *(std::launder(reinterpret_cast<Property::Value::Impl*>(mStorage.buffer)));
593 Property::Value::Value()
597 static_assert(sizeof(Value) == 16);
598 static_assert(alignof(Value) == alignof(Value*));
601 Property::Value::Value(bool booleanValue)
603 Impl::New(mStorage, booleanValue);
606 Property::Value::Value(float floatValue)
608 Impl::New(mStorage, floatValue);
611 Property::Value::Value(int32_t integerValue)
613 Impl::New(mStorage, integerValue);
616 Property::Value::Value(const Vector2& vectorValue)
618 Impl::New(mStorage, vectorValue);
621 Property::Value::Value(const Vector3& vectorValue)
623 Impl::New(mStorage, vectorValue);
626 Property::Value::Value(const Vector4& vectorValue)
628 Impl::New(mStorage, vectorValue);
631 Property::Value::Value(const Matrix3& matrixValue)
633 Impl::New(mStorage, matrixValue);
636 Property::Value::Value(const Matrix& matrixValue)
638 Impl::New(mStorage, matrixValue);
641 Property::Value::Value(const Rect<int32_t>& rectValue)
643 Impl::New(mStorage, rectValue);
646 Property::Value::Value(const Rect<float>& rectValue)
648 Impl::New(mStorage, Vector4(rectValue.x, rectValue.y, rectValue.width, rectValue.height));
651 Property::Value::Value(const AngleAxis& angleAxisValue)
653 Impl::New(mStorage, angleAxisValue);
656 Property::Value::Value(const Quaternion& quaternionValue)
658 AngleAxis angleAxisValue;
659 quaternionValue.ToAxisAngle(angleAxisValue.axis, angleAxisValue.angle);
660 Impl::New(mStorage, std::move(angleAxisValue));
663 Property::Value::Value(std::string stringValue)
665 Impl::New(mStorage, std::move(stringValue));
668 Property::Value::Value(const char* stringValue)
670 if(stringValue) // string constructor is undefined with nullptr
672 Impl::New(mStorage, std::string(stringValue));
676 Impl::New(mStorage, std::string());
680 Property::Value::Value(Property::Array arrayValue)
682 Impl::New(mStorage, std::move(arrayValue));
685 Property::Value::Value(Property::Map mapValue)
687 Impl::New(mStorage, std::move(mapValue));
690 Property::Value::Value(const Extents& extentsValue)
692 Impl::New(mStorage, extentsValue);
695 Property::Value::Value(const std::initializer_list<KeyValuePair>& values)
697 Impl::New(mStorage, Property::Map(values));
700 Property::Value::Value(Type type)
704 case Property::BOOLEAN:
706 Impl::New(mStorage, false);
709 case Property::FLOAT:
711 Impl::New(mStorage, 0.f);
714 case Property::INTEGER:
716 Impl::New(mStorage, 0);
719 case Property::VECTOR2:
721 Impl::New(mStorage, Vector2::ZERO);
724 case Property::VECTOR3:
726 Impl::New(mStorage, Vector3::ZERO);
729 case Property::VECTOR4:
731 Impl::New(mStorage, Vector4::ZERO);
734 case Property::RECTANGLE:
736 Impl::New(mStorage, Rect<int32_t>());
739 case Property::ROTATION:
741 Impl::New(mStorage, AngleAxis());
744 case Property::STRING:
746 Impl::New(mStorage, std::string());
749 case Property::MATRIX:
751 Impl::New(mStorage, Matrix());
754 case Property::MATRIX3:
756 Impl::New(mStorage, Matrix3());
759 case Property::ARRAY:
761 Impl::New(mStorage, Property::Array());
766 Impl::New(mStorage, Property::Map());
769 case Property::EXTENTS:
771 Impl::New(mStorage, Extents());
782 Property::Value::Value(const Property::Value& value)
786 // reuse assignment operator
790 Property::Value::Value(Property::Value&& value) noexcept
792 // steal the Impl object by doing a copy.
793 std::copy(std::begin(value.mStorage.buffer), std::end(value.mStorage.buffer), std::begin(mStorage.buffer));
795 // update the moved from object to NONE state.
796 value.Write().SetType(Property::NONE);
799 Property::Value& Property::Value::operator=(const Property::Value& value)
803 // skip self assignment
807 // this will call the Impl operator=()
808 Write() = value.Read();
813 Property::Value& Property::Value::operator=(Property::Value&& value) noexcept
817 // delete the existing Impl object
818 Impl::Delete(Write());
820 // steal the Impl object by doing a copy.
821 std::copy(std::begin(value.mStorage.buffer), std::end(value.mStorage.buffer), std::begin(mStorage.buffer));
823 // update the moved from object to NONE state.
824 value.Write().SetType(Property::NONE);
830 Property::Value::~Value()
832 Impl::Delete(Write());
835 Property::Type Property::Value::GetType() const
837 return Read().GetType();
840 bool Property::Value::Get(bool& booleanValue) const
842 bool converted = false;
844 const auto& obj = Read();
846 if(obj.GetType() == BOOLEAN)
848 booleanValue = obj.GetBool();
851 else if(obj.GetType() == INTEGER)
853 booleanValue = obj.GetInt();
860 bool Property::Value::Get(float& floatValue) const
862 bool converted = false;
864 const auto& obj = Read();
866 if(obj.GetType() == FLOAT)
868 floatValue = obj.GetFloat();
871 else if(obj.GetType() == BOOLEAN)
873 floatValue = static_cast<float>(obj.GetBool());
876 else if(obj.GetType() == INTEGER)
878 floatValue = static_cast<float>(obj.GetInt());
885 bool Property::Value::Get(int32_t& integerValue) const
887 bool converted = false;
889 const auto& obj = Read();
891 if(obj.GetType() == INTEGER)
893 integerValue = obj.GetInt();
896 else if(obj.GetType() == BOOLEAN)
898 integerValue = obj.GetBool();
901 else if(obj.GetType() == FLOAT)
903 integerValue = static_cast<int32_t>(obj.GetFloat());
910 bool Property::Value::Get(Vector2& vectorValue) const
912 bool converted = false;
914 const auto& obj = Read();
916 if(obj.GetType() == VECTOR4)
918 vectorValue = obj.GetVector4();
921 else if(obj.GetType() == VECTOR2)
923 vectorValue = obj.GetVector2();
926 else if(obj.GetType() == VECTOR3)
928 vectorValue = obj.GetVector3();
935 bool Property::Value::Get(Vector3& vectorValue) const
937 bool converted = false;
939 const auto& obj = Read();
941 if(obj.GetType() == VECTOR4)
943 vectorValue = obj.GetVector4();
946 else if(obj.GetType() == VECTOR2)
948 vectorValue = obj.GetVector2();
951 else if(obj.GetType() == VECTOR3)
953 vectorValue = obj.GetVector3();
960 bool Property::Value::Get(Vector4& vectorValue) const
962 bool converted = false;
964 const auto& obj = Read();
966 if(obj.GetType() == VECTOR4)
968 vectorValue = obj.GetVector4();
971 else if(obj.GetType() == VECTOR2)
973 vectorValue = obj.GetVector2();
976 else if(obj.GetType() == VECTOR3)
978 vectorValue = obj.GetVector3();
985 bool Property::Value::Get(Matrix3& matrixValue) const
987 bool converted = false;
989 const auto& obj = Read();
991 if(obj.GetType() == MATRIX3)
993 matrixValue = obj.GetMatrix3();
999 bool Property::Value::Get(Matrix& matrixValue) const
1001 bool converted = false;
1003 const auto& obj = Read();
1005 if(obj.GetType() == MATRIX)
1007 matrixValue = obj.GetMatrix();
1013 bool Property::Value::Get(Rect<int32_t>& rectValue) const
1015 bool converted = false;
1017 const auto& obj = Read();
1019 if(obj.GetType() == RECTANGLE)
1021 rectValue = obj.GetRect();
1027 bool Property::Value::Get(AngleAxis& angleAxisValue) const
1029 bool converted = false;
1031 const auto& obj = Read();
1033 if(obj.GetType() == ROTATION)
1035 angleAxisValue = obj.GetAngleAxis();
1041 bool Property::Value::Get(Quaternion& quaternionValue) const
1043 bool converted = false;
1045 const auto& obj = Read();
1047 if(obj.GetType() == ROTATION)
1049 auto& angleAxis = obj.GetAngleAxis();
1050 quaternionValue = Quaternion(angleAxis.angle, angleAxis.axis);
1056 bool Property::Value::Get(std::string& stringValue) const
1058 bool converted = false;
1060 const auto& obj = Read();
1062 if(obj.GetType() == STRING)
1064 stringValue.assign(obj.GetString());
1070 bool Property::Value::Get(Property::Array& arrayValue) const
1072 bool converted = false;
1074 const auto& obj = Read();
1076 if(obj.GetType() == ARRAY)
1078 arrayValue = obj.GetArray();
1084 bool Property::Value::Get(Property::Map& mapValue) const
1086 bool converted = false;
1088 const auto& obj = Read();
1090 if(obj.GetType() == MAP)
1092 mapValue = obj.GetMap();
1098 Property::Array const* Property::Value::GetArray() const
1100 const auto& obj = Read();
1102 if(obj.GetType() == ARRAY)
1104 return obj.GetArrayPtr();
1109 Property::Array* Property::Value::GetArray()
1111 auto& obj = Write();
1113 if(obj.GetType() == ARRAY)
1115 return obj.GetArrayPtr();
1120 Property::Map const* Property::Value::GetMap() const
1122 const auto& obj = Read();
1124 if(obj.GetType() == MAP)
1126 return obj.GetMapPtr();
1131 Property::Map* Property::Value::GetMap()
1133 auto& obj = Write();
1135 if(obj.GetType() == MAP)
1137 return obj.GetMapPtr();
1142 bool Property::Value::Get(Extents& extentsValue) const
1144 bool converted = false;
1146 const auto& obj = Read();
1148 if(obj.GetType() == EXTENTS)
1150 extentsValue = obj.GetExtents();
1153 else if(obj.GetType() == VECTOR4)
1155 auto& vec4 = obj.GetVector4();
1156 extentsValue.start = static_cast<uint16_t>(vec4.x);
1157 extentsValue.end = static_cast<uint16_t>(vec4.y);
1158 extentsValue.top = static_cast<uint16_t>(vec4.z);
1159 extentsValue.bottom = static_cast<uint16_t>(vec4.w);
1166 std::ostream& operator<<(std::ostream& stream, const Property::Value& value)
1168 const auto& obj = value.Read();
1170 switch(obj.GetType())
1172 case Dali::Property::BOOLEAN:
1174 stream << obj.GetBool();
1177 case Dali::Property::FLOAT:
1179 stream << obj.GetFloat();
1182 case Dali::Property::INTEGER:
1184 stream << obj.GetInt();
1187 case Dali::Property::VECTOR2:
1189 stream << obj.GetVector2();
1192 case Dali::Property::VECTOR3:
1194 stream << obj.GetVector3();
1197 case Dali::Property::VECTOR4:
1199 stream << obj.GetVector4();
1202 case Dali::Property::MATRIX3:
1204 stream << obj.GetMatrix3();
1207 case Dali::Property::MATRIX:
1209 stream << obj.GetMatrix();
1212 case Dali::Property::RECTANGLE:
1214 stream << obj.GetRect();
1217 case Dali::Property::ROTATION:
1219 stream << obj.GetAngleAxis();
1222 case Dali::Property::STRING:
1224 stream << obj.GetString();
1227 case Dali::Property::ARRAY:
1229 stream << obj.GetArray();
1232 case Dali::Property::MAP:
1234 stream << obj.GetMap();
1237 case Dali::Property::EXTENTS:
1239 stream << obj.GetExtents();
1242 case Dali::Property::NONE:
1244 stream << "undefined type";