| 1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
| 2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
| 3 | #pragma once |
| 4 | |
| 5 | #include "Prerequisites/BsPrerequisitesUtil.h" |
| 6 | #include "Reflection/BsRTTIField.h" |
| 7 | #include "Error/BsException.h" |
| 8 | |
| 9 | namespace bs |
| 10 | { |
| 11 | /** @addtogroup Internal-Utility |
| 12 | * @{ |
| 13 | */ |
| 14 | |
| 15 | /** @addtogroup RTTI-Internal |
| 16 | * @{ |
| 17 | */ |
| 18 | |
| 19 | /** |
| 20 | * Base class containing common functionality for a plain class field. |
| 21 | * |
| 22 | * @note |
| 23 | * Plain fields are considered those that may be serialized directly by copying their memory. (All built-in types, |
| 24 | * strings, etc.) |
| 25 | */ |
| 26 | struct RTTIPlainFieldBase : public RTTIField |
| 27 | { |
| 28 | virtual ~RTTIPlainFieldBase() = default; |
| 29 | |
| 30 | /** Throws an exception if the current field type and provided template types don't match. */ |
| 31 | template<class DataType> |
| 32 | void checkType() |
| 33 | { |
| 34 | // TODO: Low priority. Because I wanted to get rid of SerializableType I have no way of checking the actual |
| 35 | // type of the field and the type provided to get/set methods matches |
| 36 | |
| 37 | /*if(mType.id != SerializableType<DataType>().id) |
| 38 | { |
| 39 | BS_EXCEPT(InternalErrorException, |
| 40 | "Invalid field type.", |
| 41 | "SerializableSimpleTypeFieldBase::checkType()"); |
| 42 | }*/ |
| 43 | } |
| 44 | |
| 45 | /** Returns the unique identifier for the type owned by the field. */ |
| 46 | virtual UINT32 getTypeId() |
| 47 | { |
| 48 | return 0; |
| 49 | } |
| 50 | |
| 51 | /** @copydoc RTTIField::hasDynamicSize */ |
| 52 | bool hasDynamicSize() override |
| 53 | { |
| 54 | return false; |
| 55 | } |
| 56 | |
| 57 | /** Gets the dynamic size of the object. If object has no dynamic size, static size of the object is returned. */ |
| 58 | virtual UINT32 getDynamicSize(RTTITypeBase* rtti, void* object) |
| 59 | { |
| 60 | return 0; |
| 61 | } |
| 62 | |
| 63 | /** |
| 64 | * Gets the dynamic size of an array element. If the element has no dynamic size, static size of the element |
| 65 | * is returned. |
| 66 | */ |
| 67 | virtual UINT32 getArrayElemDynamicSize(RTTITypeBase* rtti, void* object, int index) |
| 68 | { |
| 69 | return 0; |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * Retrieves the value from the provided field of the provided object, and copies it into the buffer. It does not |
| 74 | * check if buffer is large enough. |
| 75 | */ |
| 76 | virtual void toBuffer(RTTITypeBase* rtti, void* object, void* buffer) = 0; |
| 77 | |
| 78 | /** |
| 79 | * Retrieves the value at the specified array index on the provided field of the provided object, and copies it into |
| 80 | * the buffer. It does not check if buffer is large enough. |
| 81 | */ |
| 82 | virtual void arrayElemToBuffer(RTTITypeBase* rtti, void* object, int index, void* buffer) = 0; |
| 83 | |
| 84 | /** |
| 85 | * Sets the value on the provided field of the provided object. Value is copied from the buffer. It does not check |
| 86 | * the value in the buffer in any way. You must make sure buffer points to the proper location and contains the |
| 87 | * proper type. |
| 88 | */ |
| 89 | virtual void fromBuffer(RTTITypeBase* rtti, void* object, void* buffer) = 0; |
| 90 | |
| 91 | /** |
| 92 | * Sets the value at the specified array index on the provided field of the provided object. Value is copied from |
| 93 | * the buffer. It does not check the value in the buffer in any way. You must make sure buffer points to the proper |
| 94 | * location and contains the proper type. |
| 95 | */ |
| 96 | virtual void arrayElemFromBuffer(RTTITypeBase* rtti, void* object, int index, void* buffer) = 0; |
| 97 | }; |
| 98 | |
| 99 | /** Represents a plain class field containing a specific type. */ |
| 100 | template <class InterfaceType, class DataType, class ObjectType> |
| 101 | struct RTTIPlainField : public RTTIPlainFieldBase |
| 102 | { |
| 103 | typedef DataType& (InterfaceType::*GetterType)(ObjectType*); |
| 104 | typedef void (InterfaceType::*SetterType)(ObjectType*, DataType&); |
| 105 | |
| 106 | typedef DataType& (InterfaceType::*ArrayGetterType)(ObjectType*, UINT32); |
| 107 | typedef void (InterfaceType::*ArraySetterType)(ObjectType*, UINT32, DataType&); |
| 108 | typedef UINT32(InterfaceType::*ArrayGetSizeType)(ObjectType*); |
| 109 | typedef void(InterfaceType::*ArraySetSizeType)(ObjectType*, UINT32); |
| 110 | |
| 111 | /** |
| 112 | * Initializes a plain field containing a single value. |
| 113 | * |
| 114 | * @param[in] name Name of the field. |
| 115 | * @param[in] uniqueId Unique identifier for this field. Although name is also a unique identifier we want a |
| 116 | * small data type that can be used for efficiently serializing data to disk and similar. |
| 117 | * It is primarily used for compatibility between different versions of serialized data. |
| 118 | * @param[in] getter The getter method for the field. |
| 119 | * @param[in] setter The setter method for the field. |
| 120 | * @param[in] info Various optional information about the field. |
| 121 | */ |
| 122 | void initSingle(String name, UINT16 uniqueId, GetterType getter, SetterType setter, const RTTIFieldInfo& info) |
| 123 | { |
| 124 | static_assert(sizeof(RTTIPlainType<DataType>::id) > 0, "Type has no RTTI ID." ); // Just making sure provided type has a type ID |
| 125 | |
| 126 | static_assert((RTTIPlainType<DataType>::hasDynamicSize != 0 || (sizeof(DataType) <= 255)), |
| 127 | "Trying to create a plain RTTI field with size larger than 255. In order to use larger sizes for plain types please specialize " \ |
| 128 | " RTTIPlainType, set hasDynamicSize to true." ); |
| 129 | |
| 130 | this->getter = getter; |
| 131 | this->setter = setter; |
| 132 | |
| 133 | init(std::move(name), uniqueId, false, SerializableFT_Plain, info); |
| 134 | } |
| 135 | |
| 136 | /** |
| 137 | * Initializes a plain field containing multiple values in an array. |
| 138 | * |
| 139 | * @param[in] name Name of the field. |
| 140 | * @param[in] uniqueId Unique identifier for this field. Although name is also a unique identifier we want a |
| 141 | * small data type that can be used for efficiently serializing data to disk and similar. |
| 142 | * It is primarily used for compatibility between different versions of serialized data. |
| 143 | * @param[in] getter The getter method for the field. |
| 144 | * @param[in] getSize Getter method that returns the size of an array. |
| 145 | * @param[in] setter The setter method for the field. |
| 146 | * @param[in] setSize Setter method that allows you to resize an array. Can be null. |
| 147 | * @param[in] info Various optional information about the field. |
| 148 | */ |
| 149 | void initArray(String name, UINT16 uniqueId, ArrayGetterType getter, |
| 150 | ArrayGetSizeType getSize, ArraySetterType setter, ArraySetSizeType setSize, const RTTIFieldInfo& info) |
| 151 | { |
| 152 | static_assert((RTTIPlainType<DataType>::id != 0) || true, "" ); // Just making sure provided type has a type ID |
| 153 | |
| 154 | static_assert((RTTIPlainType<DataType>::hasDynamicSize != 0 || (sizeof(DataType) <= 255)), |
| 155 | "Trying to create a plain RTTI field with size larger than 255. In order to use larger sizes for plain types please specialize " \ |
| 156 | " RTTIPlainType, set hasDynamicSize to true." ); |
| 157 | |
| 158 | arrayGetter = getter; |
| 159 | arraySetter = setter; |
| 160 | arrayGetSize = getSize; |
| 161 | arraySetSize = setSize; |
| 162 | |
| 163 | init(std::move(name), uniqueId, true, SerializableFT_Plain, info); |
| 164 | } |
| 165 | |
| 166 | /** @copydoc RTTIField::getTypeSize */ |
| 167 | UINT32 getTypeSize() override |
| 168 | { |
| 169 | return sizeof(DataType); |
| 170 | } |
| 171 | |
| 172 | /** @copydoc RTTIPlainFieldBase::getTypeId */ |
| 173 | UINT32 getTypeId() override |
| 174 | { |
| 175 | return RTTIPlainType<DataType>::id; |
| 176 | } |
| 177 | |
| 178 | /** @copydoc RTTIPlainFieldBase::hasDynamicSize */ |
| 179 | bool hasDynamicSize() override |
| 180 | { |
| 181 | return RTTIPlainType<DataType>::hasDynamicSize != 0; |
| 182 | } |
| 183 | |
| 184 | /** @copydoc RTTIPlainFieldBase::getDynamicSize */ |
| 185 | UINT32 getDynamicSize(RTTITypeBase* rtti, void* object) override |
| 186 | { |
| 187 | checkIsArray(false); |
| 188 | checkType<DataType>(); |
| 189 | |
| 190 | InterfaceType* rttiObject = static_cast<InterfaceType*>(rtti); |
| 191 | ObjectType* castObject = static_cast<ObjectType*>(object); |
| 192 | DataType value = (rttiObject->*getter)(castObject); |
| 193 | |
| 194 | return RTTIPlainType<DataType>::getDynamicSize(value); |
| 195 | } |
| 196 | |
| 197 | /** @copydoc RTTIPlainFieldBase::getArrayElemDynamicSize */ |
| 198 | UINT32 getArrayElemDynamicSize(RTTITypeBase* rtti, void* object, int index) override |
| 199 | { |
| 200 | checkIsArray(true); |
| 201 | checkType<DataType>(); |
| 202 | |
| 203 | InterfaceType* rttiObject = static_cast<InterfaceType*>(rtti); |
| 204 | ObjectType* castObject = static_cast<ObjectType*>(object); |
| 205 | DataType value = (rttiObject->*arrayGetter)(castObject, index); |
| 206 | |
| 207 | return RTTIPlainType<DataType>::getDynamicSize(value); |
| 208 | } |
| 209 | |
| 210 | /** Returns the size of the array managed by the field. */ |
| 211 | UINT32 getArraySize(RTTITypeBase* rtti, void* object) override |
| 212 | { |
| 213 | checkIsArray(true); |
| 214 | |
| 215 | InterfaceType* rttiObject = static_cast<InterfaceType*>(rtti); |
| 216 | ObjectType* castObject = static_cast<ObjectType*>(object); |
| 217 | return (rttiObject->*arrayGetSize)(castObject); |
| 218 | } |
| 219 | |
| 220 | /** Changes the size of the array managed by the field. Array must be re-populated after. */ |
| 221 | void setArraySize(RTTITypeBase* rtti, void* object, UINT32 size) override |
| 222 | { |
| 223 | checkIsArray(true); |
| 224 | |
| 225 | if(!arraySetSize) |
| 226 | { |
| 227 | BS_EXCEPT(InternalErrorException, "Specified field (" + mName + ") has no array size setter." ); |
| 228 | } |
| 229 | |
| 230 | InterfaceType* rttiObject = static_cast<InterfaceType*>(rtti); |
| 231 | ObjectType* castObject = static_cast<ObjectType*>(object); |
| 232 | (rttiObject->*arraySetSize)(castObject, size); |
| 233 | } |
| 234 | |
| 235 | /** @copydoc RTTIPlainFieldBase::toBuffer */ |
| 236 | void toBuffer(RTTITypeBase* rtti, void* object, void* buffer) override |
| 237 | { |
| 238 | checkIsArray(false); |
| 239 | checkType<DataType>(); |
| 240 | |
| 241 | InterfaceType* rttiObject = static_cast<InterfaceType*>(rtti); |
| 242 | ObjectType* castObject = static_cast<ObjectType*>(object); |
| 243 | DataType value = (rttiObject->*getter)(castObject); |
| 244 | |
| 245 | RTTIPlainType<DataType>::toMemory(value, (char*)buffer); |
| 246 | } |
| 247 | |
| 248 | /** @copydoc RTTIPlainFieldBase::arrayElemToBuffer */ |
| 249 | void arrayElemToBuffer(RTTITypeBase* rtti, void* object, int index, void* buffer) override |
| 250 | { |
| 251 | checkIsArray(true); |
| 252 | checkType<DataType>(); |
| 253 | |
| 254 | InterfaceType* rttiObject = static_cast<InterfaceType*>(rtti); |
| 255 | ObjectType* castObject = static_cast<ObjectType*>(object); |
| 256 | DataType value = (rttiObject->*arrayGetter)(castObject, index); |
| 257 | |
| 258 | RTTIPlainType<DataType>::toMemory(value, (char*)buffer); |
| 259 | } |
| 260 | |
| 261 | /** @copydoc RTTIPlainFieldBase::fromBuffer */ |
| 262 | void fromBuffer(RTTITypeBase* rtti, void* object, void* buffer) override |
| 263 | { |
| 264 | checkIsArray(false); |
| 265 | checkType<DataType>(); |
| 266 | |
| 267 | InterfaceType* rttiObject = static_cast<InterfaceType*>(rtti); |
| 268 | ObjectType* castObject = static_cast<ObjectType*>(object); |
| 269 | |
| 270 | DataType value; |
| 271 | RTTIPlainType<DataType>::fromMemory(value, (char*)buffer); |
| 272 | |
| 273 | if(!setter) |
| 274 | { |
| 275 | BS_EXCEPT(InternalErrorException, |
| 276 | "Specified field (" + mName + ") has no setter." ); |
| 277 | } |
| 278 | |
| 279 | (rttiObject->*setter)(castObject, value); |
| 280 | } |
| 281 | |
| 282 | /** @copydoc RTTIPlainFieldBase::arrayElemFromBuffer */ |
| 283 | void arrayElemFromBuffer(RTTITypeBase* rtti, void* object, int index, void* buffer) override |
| 284 | { |
| 285 | checkIsArray(true); |
| 286 | checkType<DataType>(); |
| 287 | |
| 288 | InterfaceType* rttiObject = static_cast<InterfaceType*>(rtti); |
| 289 | ObjectType* castObject = static_cast<ObjectType*>(object); |
| 290 | |
| 291 | DataType value; |
| 292 | RTTIPlainType<DataType>::fromMemory(value, (char*)buffer); |
| 293 | |
| 294 | if(!arraySetter) |
| 295 | { |
| 296 | BS_EXCEPT(InternalErrorException, |
| 297 | "Specified field (" + mName + ") has no setter." ); |
| 298 | } |
| 299 | |
| 300 | (rttiObject->*arraySetter)(castObject, index, value); |
| 301 | } |
| 302 | |
| 303 | private: |
| 304 | union |
| 305 | { |
| 306 | struct |
| 307 | { |
| 308 | GetterType getter; |
| 309 | SetterType setter; |
| 310 | }; |
| 311 | |
| 312 | struct |
| 313 | { |
| 314 | ArrayGetterType arrayGetter; |
| 315 | ArraySetterType arraySetter; |
| 316 | |
| 317 | ArrayGetSizeType arrayGetSize; |
| 318 | ArraySetSizeType arraySetSize; |
| 319 | }; |
| 320 | }; |
| 321 | }; |
| 322 | |
| 323 | /** @} */ |
| 324 | /** @} */ |
| 325 | } |
| 326 | |