2 * Copyright (c) 2020 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) ).
62 struct Property::Value::Impl
64 template<typename... Args>
65 static Impl* New(Property::Value::Storage& buffer, Args... args)
67 return new(&buffer) Impl(std::forward<Args>(args)...);
70 static void Delete(Property::Value::Impl& impl)
77 // Destroy the current object stored in Data union memory.
81 Impl(const Impl&) = delete;
83 Impl(Impl&&) = delete;
85 Impl& operator=(Impl&&) = delete;
89 static_assert(sizeof(Impl) == 16);
90 static_assert(alignof(Impl) == alignof(Impl*));
92 SetType(Property::NONE);
95 Impl(bool booleanValue)
97 SetType(Property::BOOLEAN);
98 mData.mBool.member = booleanValue;
101 Impl(float floatValue)
103 SetType(Property::FLOAT);
104 mData.mFloat.member = floatValue;
107 Impl(int32_t integerValue)
109 SetType(Property::INTEGER);
110 mData.mInt.member = integerValue;
113 Impl(Vector2 vectorValue)
115 SetType(Property::VECTOR2);
116 ConstructInplace(mData.mVector2.member, std::move(vectorValue));
119 Impl(Vector3 vectorValue)
121 SetType(Property::VECTOR3);
122 ConstructInplace(mData.mVector3.member, std::move(vectorValue));
125 Impl(Extents extentsValue)
127 SetType(Property::EXTENTS);
128 ConstructInplace(mData.mExtents.member, std::move(extentsValue));
131 Impl(Property::Map mapValue)
133 SetType(Property::MAP);
134 ConstructInplace(mData.mMap.member, std::move(mapValue));
137 Impl(Property::Array arrayValue)
139 SetType(Property::ARRAY);
140 ConstructInplace(mData.mArray.member, std::move(arrayValue));
143 Impl(std::string stringValue)
145 SetType(Property::STRING);
146 mData.mString.member = new std::string(std::move(stringValue));
149 Impl(Rect<int32_t> rectValue)
151 SetType(Property::RECTANGLE);
152 mData.mRect.member = new Rect<int32_t>(std::move(rectValue));
155 Impl(Vector4 vectorValue)
157 SetType(Property::VECTOR4);
158 mData.mVector4.member = new Vector4(std::move(vectorValue));
161 Impl(Matrix3 matrixValue)
163 SetType(Property::MATRIX3);
164 mData.mMatrix3.member = new Matrix3(std::move(matrixValue));
167 Impl(Matrix matrixValue)
169 SetType(Property::MATRIX);
170 mData.mMatrix.member = new Matrix(std::move(matrixValue));
173 Impl(AngleAxis angleAxisValue)
175 SetType(Property::ROTATION);
176 mData.mAngleAxis.member = new AngleAxis(std::move(angleAxisValue));
181 return mData.mType.type;
186 return mData.mBool.member;
189 int32_t GetInt() const
191 return mData.mInt.member;
194 float GetFloat() const
196 return mData.mFloat.member;
199 const Extents& GetExtents() const
201 return mData.mExtents.member;
204 const Vector2& GetVector2() const
206 return mData.mVector2.member;
209 const Vector3& GetVector3() const
211 return mData.mVector3.member;
214 const Property::Map& GetMap() const
216 return mData.mMap.member;
219 const Property::Array& GetArray() const
221 return mData.mArray.member;
224 const Vector4& GetVector4() const
226 return *(mData.mVector4.member);
229 const Matrix3& GetMatrix3() const
231 return *(mData.mMatrix3.member);
234 const Matrix& GetMatrix() const
236 return *(mData.mMatrix.member);
239 const AngleAxis& GetAngleAxis() const
241 return *(mData.mAngleAxis.member);
244 const std::string& GetString() const
246 return *(mData.mString.member);
249 const Rect<int32_t>& GetRect() const
251 return *(mData.mRect.member);
254 Property::Map* GetMapPtr()
256 return &(mData.mMap.member);
259 const Property::Map* GetMapPtr() const
261 return &(mData.mMap.member);
264 Property::Array* GetArrayPtr()
266 return &(mData.mArray.member);
269 const Property::Array* GetArrayPtr() const
271 return &(mData.mArray.member);
274 Impl& operator=(const Impl& other)
276 const bool isSameType = GetType() == other.GetType();
281 SetType(other.GetType());
290 case Property::BOOLEAN:
292 mData.mBool.member = other.GetBool();
295 case Property::FLOAT:
297 mData.mFloat.member = other.GetFloat();
300 case Property::INTEGER:
302 mData.mInt.member = other.GetInt();
305 case Property::EXTENTS:
307 auto obj = other.GetExtents();
308 ConstructInplace(mData.mExtents.member, std::move(obj));
311 case Property::VECTOR2:
313 auto obj = other.GetVector2();
314 ConstructInplace(mData.mVector2.member, std::move(obj));
317 case Property::VECTOR3:
319 auto obj = other.GetVector3();
320 ConstructInplace(mData.mVector3.member, std::move(obj));
323 case Property::ARRAY:
325 auto obj = other.GetArray();
326 ConstructInplace(mData.mArray.member, std::move(obj));
331 auto obj = other.GetMap();
332 ConstructInplace(mData.mMap.member, std::move(obj));
335 case Property::VECTOR4:
339 *mData.mVector4.member = other.GetVector4();
343 mData.mVector4.member = new Vector4(other.GetVector4());
347 case Property::MATRIX3:
351 *mData.mMatrix3.member = other.GetMatrix3();
355 mData.mMatrix3.member = new Matrix3(other.GetMatrix3());
359 case Property::MATRIX:
363 *mData.mMatrix.member = other.GetMatrix();
367 mData.mMatrix.member = new Matrix(other.GetMatrix());
371 case Property::RECTANGLE:
375 *mData.mRect.member = other.GetRect();
379 mData.mRect.member = new Rect<int32_t>(other.GetRect());
383 case Property::ROTATION:
387 *mData.mAngleAxis.member = other.GetAngleAxis();
391 mData.mAngleAxis.member = new AngleAxis(other.GetAngleAxis());
395 case Property::STRING:
399 *mData.mString.member = other.GetString();
403 mData.mString.member = new std::string(other.GetString());
411 void SetType(Type typeValue)
413 mData.mType.type = typeValue;
418 * This helper function takes a typed(Tp) memory location( member)
419 * and a object of same type( val ) and move constructs a new object of
420 * same type(Tp) in the memory location( member) using placement new.
421 * after this function call member location will have a object of type Tp.
423 template<typename Tp>
424 void ConstructInplace(Tp& member, Tp&& val)
426 new(&member) Tp(std::forward<Tp>(val));
430 * Destroy the object created in the Data union memory by probing the
431 * type and calling the appropriate destructor.
432 * and also reset the type and memory location to reflect that .
439 case Property::BOOLEAN:
440 case Property::FLOAT:
441 case Property::INTEGER:
443 break; // nothing to do
445 case Property::EXTENTS:
447 mData.mExtents.member.~Extents();
450 case Property::VECTOR2:
452 mData.mVector2.member.~Vector2();
455 case Property::VECTOR3:
457 mData.mVector3.member.~Vector3();
460 case Property::ARRAY:
462 using array = Property::Array;
463 mData.mArray.member.~array();
468 using map = Property::Map;
469 mData.mMap.member.~map();
472 case Property::VECTOR4:
474 delete mData.mVector4.member;
477 case Property::MATRIX3:
479 delete mData.mMatrix3.member;
482 case Property::MATRIX:
484 delete mData.mMatrix.member;
487 case Property::RECTANGLE:
489 delete mData.mRect.member;
492 case Property::ROTATION:
494 delete mData.mAngleAxis.member;
497 case Property::STRING:
499 delete mData.mString.member;
506 * This wrapper struct is used for
507 * storing Type in every union member
508 * and can acess it from non active member
509 * of the uninon without invoking UB. this is
510 * possible because of CIS(common initial sequence)
511 * http://eel.is/c++draft/class.mem#general-25
521 * Tagged union implementation.
523 * This Data union contains non trivial data
524 * types Map and Array, the default constructor
525 * and destructors are deleted by the compiler
526 * so we provided empty constructor and destructor
527 * just to pacify the compiler.
528 * The only job of this union to give a typed memory buffer to the
529 * Impl class which can construct the appropriate object
530 * using placement new.
531 * As Impl class explicitly construct the object and keeps track of the
532 * object it creates and then destroys them in the ~Impl() this will not leak
544 UnionMember<bool> mBool;
545 UnionMember<int32_t> mInt;
546 UnionMember<float> mFloat;
547 UnionMember<Extents> mExtents;
548 UnionMember<Vector2> mVector2;
549 UnionMember<Vector3> mVector3;
550 UnionMember<Property::Map> mMap;
551 UnionMember<Property::Array> mArray;
552 UnionMember<Vector4*> mVector4;
553 UnionMember<Matrix3*> mMatrix3;
554 UnionMember<Matrix*> mMatrix;
555 UnionMember<AngleAxis*> mAngleAxis;
556 UnionMember<std::string*> mString;
557 UnionMember<Rect<int32_t>*> mRect;
568 * Safe way to read an already constructed Object from a buffer.
570 const Property::Value::Impl& Property::Value::Read() const
572 return *(std::launder(reinterpret_cast<const Property::Value::Impl*>(mStorage.buffer)));
575 Property::Value::Impl& Property::Value::Write()
577 return *(std::launder(reinterpret_cast<Property::Value::Impl*>(mStorage.buffer)));
580 Property::Value::Value()
584 static_assert(sizeof(Value) == 16);
585 static_assert(alignof(Value) == alignof(Value*));
588 Property::Value::Value(bool booleanValue)
590 Impl::New(mStorage, booleanValue);
593 Property::Value::Value(float floatValue)
595 Impl::New(mStorage, floatValue);
598 Property::Value::Value(int32_t integerValue)
600 Impl::New(mStorage, integerValue);
603 Property::Value::Value(const Vector2& vectorValue)
605 Impl::New(mStorage, vectorValue);
608 Property::Value::Value(const Vector3& vectorValue)
610 Impl::New(mStorage, vectorValue);
613 Property::Value::Value(const Vector4& vectorValue)
615 Impl::New(mStorage, vectorValue);
618 Property::Value::Value(const Matrix3& matrixValue)
620 Impl::New(mStorage, matrixValue);
623 Property::Value::Value(const Matrix& matrixValue)
625 Impl::New(mStorage, matrixValue);
628 Property::Value::Value(const Rect<int32_t>& rectValue)
630 Impl::New(mStorage, rectValue);
633 Property::Value::Value(const Rect<float>& rectValue)
635 Impl::New(mStorage, Vector4(rectValue.x, rectValue.y, rectValue.width, rectValue.height));
638 Property::Value::Value(const AngleAxis& angleAxisValue)
640 Impl::New(mStorage, angleAxisValue);
643 Property::Value::Value(const Quaternion& quaternionValue)
645 AngleAxis angleAxisValue;
646 quaternionValue.ToAxisAngle(angleAxisValue.axis, angleAxisValue.angle);
647 Impl::New(mStorage, std::move(angleAxisValue));
650 Property::Value::Value(std::string stringValue)
652 Impl::New(mStorage, std::move(stringValue));
655 Property::Value::Value(const char* stringValue)
657 if(stringValue) // string constructor is undefined with nullptr
659 Impl::New(mStorage, std::string(stringValue));
663 Impl::New(mStorage, std::string());
667 Property::Value::Value(Property::Array arrayValue)
669 Impl::New(mStorage, std::move(arrayValue));
672 Property::Value::Value(Property::Map mapValue)
674 Impl::New(mStorage, std::move(mapValue));
677 Property::Value::Value(const Extents& extentsValue)
679 Impl::New(mStorage, extentsValue);
682 Property::Value::Value(const std::initializer_list<KeyValuePair>& values)
684 Impl::New(mStorage, Property::Map(values));
687 Property::Value::Value(Type type)
691 case Property::BOOLEAN:
693 Impl::New(mStorage, false);
696 case Property::FLOAT:
698 Impl::New(mStorage, 0.f);
701 case Property::INTEGER:
703 Impl::New(mStorage, 0);
706 case Property::VECTOR2:
708 Impl::New(mStorage, Vector2::ZERO);
711 case Property::VECTOR3:
713 Impl::New(mStorage, Vector3::ZERO);
716 case Property::VECTOR4:
718 Impl::New(mStorage, Vector4::ZERO);
721 case Property::RECTANGLE:
723 Impl::New(mStorage, Rect<int32_t>());
726 case Property::ROTATION:
728 Impl::New(mStorage, AngleAxis());
731 case Property::STRING:
733 Impl::New(mStorage, std::string());
736 case Property::MATRIX:
738 Impl::New(mStorage, Matrix());
741 case Property::MATRIX3:
743 Impl::New(mStorage, Matrix3());
746 case Property::ARRAY:
748 Impl::New(mStorage, Property::Array());
753 Impl::New(mStorage, Property::Map());
756 case Property::EXTENTS:
758 Impl::New(mStorage, Extents());
769 Property::Value::Value(const Property::Value& value)
773 // reuse assignment operator
777 Property::Value::Value(Property::Value&& value) noexcept
779 // steal the Impl object by doing a copy.
780 std::copy(std::begin(value.mStorage.buffer), std::end(value.mStorage.buffer), std::begin(mStorage.buffer));
782 // update the moved from object to NONE state.
783 value.Write().SetType(Property::NONE);
786 Property::Value& Property::Value::operator=(const Property::Value& value)
790 // skip self assignment
794 // this will call the Impl operator=()
795 Write() = value.Read();
800 Property::Value& Property::Value::operator=(Property::Value&& value) noexcept
804 // delete the existing Impl object
805 Impl::Delete(Write());
807 // steal the Impl object by doing a copy.
808 std::copy(std::begin(value.mStorage.buffer), std::end(value.mStorage.buffer), std::begin(mStorage.buffer));
810 // update the moved from object to NONE state.
811 value.Write().SetType(Property::NONE);
817 Property::Value::~Value()
819 Impl::Delete(Write());
822 Property::Type Property::Value::GetType() const
824 return Read().GetType();
827 bool Property::Value::Get(bool& booleanValue) const
829 bool converted = false;
831 const auto& obj = Read();
833 if(obj.GetType() == BOOLEAN)
835 booleanValue = obj.GetBool();
838 else if(obj.GetType() == INTEGER)
840 booleanValue = obj.GetInt();
847 bool Property::Value::Get(float& floatValue) const
849 bool converted = false;
851 const auto& obj = Read();
853 if(obj.GetType() == FLOAT)
855 floatValue = obj.GetFloat();
858 else if(obj.GetType() == BOOLEAN)
860 floatValue = static_cast<float>(obj.GetBool());
863 else if(obj.GetType() == INTEGER)
865 floatValue = static_cast<float>(obj.GetInt());
872 bool Property::Value::Get(int32_t& integerValue) const
874 bool converted = false;
876 const auto& obj = Read();
878 if(obj.GetType() == INTEGER)
880 integerValue = obj.GetInt();
883 else if(obj.GetType() == BOOLEAN)
885 integerValue = obj.GetBool();
888 else if(obj.GetType() == FLOAT)
890 integerValue = static_cast<int32_t>(obj.GetFloat());
897 bool Property::Value::Get(Vector2& vectorValue) const
899 bool converted = false;
901 const auto& obj = Read();
903 if(obj.GetType() == VECTOR4)
905 vectorValue = obj.GetVector4();
908 else if(obj.GetType() == VECTOR2)
910 vectorValue = obj.GetVector2();
913 else if(obj.GetType() == VECTOR3)
915 vectorValue = obj.GetVector3();
922 bool Property::Value::Get(Vector3& vectorValue) const
924 bool converted = false;
926 const auto& obj = Read();
928 if(obj.GetType() == VECTOR4)
930 vectorValue = obj.GetVector4();
933 else if(obj.GetType() == VECTOR2)
935 vectorValue = obj.GetVector2();
938 else if(obj.GetType() == VECTOR3)
940 vectorValue = obj.GetVector3();
947 bool Property::Value::Get(Vector4& vectorValue) const
949 bool converted = false;
951 const auto& obj = Read();
953 if(obj.GetType() == VECTOR4)
955 vectorValue = obj.GetVector4();
958 else if(obj.GetType() == VECTOR2)
960 vectorValue = obj.GetVector2();
963 else if(obj.GetType() == VECTOR3)
965 vectorValue = obj.GetVector3();
972 bool Property::Value::Get(Matrix3& matrixValue) const
974 bool converted = false;
976 const auto& obj = Read();
978 if(obj.GetType() == MATRIX3)
980 matrixValue = obj.GetMatrix3();
986 bool Property::Value::Get(Matrix& matrixValue) const
988 bool converted = false;
990 const auto& obj = Read();
992 if(obj.GetType() == MATRIX)
994 matrixValue = obj.GetMatrix();
1000 bool Property::Value::Get(Rect<int32_t>& rectValue) const
1002 bool converted = false;
1004 const auto& obj = Read();
1006 if(obj.GetType() == RECTANGLE)
1008 rectValue = obj.GetRect();
1014 bool Property::Value::Get(AngleAxis& angleAxisValue) const
1016 bool converted = false;
1018 const auto& obj = Read();
1020 if(obj.GetType() == ROTATION)
1022 angleAxisValue = obj.GetAngleAxis();
1028 bool Property::Value::Get(Quaternion& quaternionValue) const
1030 bool converted = false;
1032 const auto& obj = Read();
1034 if(obj.GetType() == ROTATION)
1036 auto& angleAxis = obj.GetAngleAxis();
1037 quaternionValue = Quaternion(angleAxis.angle, angleAxis.axis);
1043 bool Property::Value::Get(std::string& stringValue) const
1045 bool converted = false;
1047 const auto& obj = Read();
1049 if(obj.GetType() == STRING)
1051 stringValue.assign(obj.GetString());
1057 bool Property::Value::Get(Property::Array& arrayValue) const
1059 bool converted = false;
1061 const auto& obj = Read();
1063 if(obj.GetType() == ARRAY)
1065 arrayValue = obj.GetArray();
1071 bool Property::Value::Get(Property::Map& mapValue) const
1073 bool converted = false;
1075 const auto& obj = Read();
1077 if(obj.GetType() == MAP)
1079 mapValue = obj.GetMap();
1085 Property::Array const* Property::Value::GetArray() const
1087 const auto& obj = Read();
1089 if(obj.GetType() == ARRAY)
1091 return obj.GetArrayPtr();
1096 Property::Array* Property::Value::GetArray()
1098 auto& obj = Write();
1100 if(obj.GetType() == ARRAY)
1102 return obj.GetArrayPtr();
1107 Property::Map const* Property::Value::GetMap() const
1109 const auto& obj = Read();
1111 if(obj.GetType() == MAP)
1113 return obj.GetMapPtr();
1118 Property::Map* Property::Value::GetMap()
1120 auto& obj = Write();
1122 if(obj.GetType() == MAP)
1124 return obj.GetMapPtr();
1129 bool Property::Value::Get(Extents& extentsValue) const
1131 bool converted = false;
1133 const auto& obj = Read();
1135 if(obj.GetType() == EXTENTS)
1137 extentsValue = obj.GetExtents();
1140 else if(obj.GetType() == VECTOR4)
1142 auto& vec4 = obj.GetVector4();
1143 extentsValue.start = static_cast<uint16_t>(vec4.x);
1144 extentsValue.end = static_cast<uint16_t>(vec4.y);
1145 extentsValue.top = static_cast<uint16_t>(vec4.z);
1146 extentsValue.bottom = static_cast<uint16_t>(vec4.w);
1153 std::ostream& operator<<(std::ostream& stream, const Property::Value& value)
1155 const auto& obj = value.Read();
1157 switch(obj.GetType())
1159 case Dali::Property::BOOLEAN:
1161 stream << obj.GetBool();
1164 case Dali::Property::FLOAT:
1166 stream << obj.GetFloat();
1169 case Dali::Property::INTEGER:
1171 stream << obj.GetInt();
1174 case Dali::Property::VECTOR2:
1176 stream << obj.GetVector2();
1179 case Dali::Property::VECTOR3:
1181 stream << obj.GetVector3();
1184 case Dali::Property::VECTOR4:
1186 stream << obj.GetVector4();
1189 case Dali::Property::MATRIX3:
1191 stream << obj.GetMatrix3();
1194 case Dali::Property::MATRIX:
1196 stream << obj.GetMatrix();
1199 case Dali::Property::RECTANGLE:
1201 stream << obj.GetRect();
1204 case Dali::Property::ROTATION:
1206 stream << obj.GetAngleAxis();
1209 case Dali::Property::STRING:
1211 stream << obj.GetString();
1214 case Dali::Property::ARRAY:
1216 stream << obj.GetArray();
1219 case Dali::Property::MAP:
1221 stream << obj.GetMap();
1224 case Dali::Property::EXTENTS:
1226 stream << obj.GetExtents();
1229 case Dali::Property::NONE:
1231 stream << "undefined type";