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 | |