/[svn]/libgig/trunk/src/Serialization.h
ViewVC logotype

Contents of /libgig/trunk/src/Serialization.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3182 - (show annotations) (download) (as text)
Sun May 14 20:40:02 2017 UTC (6 years, 11 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 34742 byte(s)
* Serialization framework: moved methods setVersion() and
  setMinVersion() from class Object to class Archive, and
  hide enum type operation_t from the public API.
* Bumped version (4.0.0.svn23).

1 /***************************************************************************
2 * *
3 * Copyright (C) 2017 Christian Schoenebeck *
4 * <cuse@users.sourceforge.net> *
5 * *
6 * This library is part of libgig. *
7 * *
8 * This library is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this library; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #ifndef LIBGIG_SERIALIZATION_H
25 #define LIBGIG_SERIALIZATION_H
26
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <typeinfo>
34 #include <string>
35 #include <vector>
36 #include <map>
37 #include <time.h>
38
39 #ifndef __has_extension
40 # define __has_extension(x) 0
41 #endif
42
43 #ifndef HAS_BUILTIN_TYPE_TRAITS
44 # if __cplusplus >= 201103L
45 # define HAS_BUILTIN_TYPE_TRAITS 1
46 # elif ( __has_extension(is_class) && __has_extension(is_enum) )
47 # define HAS_BUILTIN_TYPE_TRAITS 1
48 # elif ( __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 3 ) )
49 # define HAS_BUILTIN_TYPE_TRAITS 1
50 # elif _MSC_VER >= 1400 /* MS Visual C++ 8.0 (Visual Studio 2005) */
51 # define HAS_BUILTIN_TYPE_TRAITS 1
52 # elif __INTEL_COMPILER >= 1100
53 # define HAS_BUILTIN_TYPE_TRAITS 1
54 # else
55 # define HAS_BUILTIN_TYPE_TRAITS 0
56 # endif
57 #endif
58
59 #if !HAS_BUILTIN_TYPE_TRAITS
60 # include <tr1/type_traits>
61 # define LIBGIG_IS_CLASS(type) std::tr1::__is_union_or_class<type>::value //NOTE: without compiler support we cannot distinguish union from class
62 #else
63 # define LIBGIG_IS_CLASS(type) __is_class(type)
64 #endif
65
66 /** @brief Serialization / deserialization framework.
67 *
68 * See class Archive as starting point for how to implement serialization and
69 * deserialization with your application.
70 *
71 * The classes in this namespace allow to serialize and deserialize native
72 * C++ objects in a portable, easy and flexible way. Serialization is a
73 * technique that allows to transform the current state and data of native
74 * (in this case C++) objects into a data stream (including all other objects
75 * the "serialized" objects relate to); the data stream may then be sent over
76 * "wire" (for example via network connection to another computer, which might
77 * also have a different OS, CPU architecture, native memory word size and
78 * endian type); and finally the data stream would be "deserialized" on that
79 * receiver side, that is transformed again to modify all objects and data
80 * structures on receiver side to resemble the objects' state and data as it
81 * was originally on sender side.
82 *
83 * In contrast to many other already existing serialization frameworks, this
84 * implementation has a strong focus on robustness regarding long-term changes
85 * to the serialized C++ classes of the serialized objects. So even if sender
86 * and receiver are using different versions of their serialized/deserialized
87 * C++ classes, structures and data types (thus having different data structure
88 * layout to a certain extent), this framework aims trying to automatically
89 * adapt its serialization and deserialization process in that case so that
90 * the deserialized objects on receiver side would still reflect the overall
91 * expected states and overall data as intended by the sender. For being able to
92 * do so, this framework stores all kind of additional information about each
93 * serialized object and each data structure member (for example name of each
94 * data structure member, but also the offset of each member within its
95 * containing data structure, precise data types, and more).
96 *
97 * Like most other serialization frameworks, this frameworks does not require a
98 * tree-structured layout of the serialized data structures. So it automatically
99 * handles also cyclic dependencies between serialized data structures
100 * correctly, without i.e. causing endless recursion or redundancy.
101 *
102 * Additionally this framework also allows partial deserialization. Which means
103 * the receiver side may for example decide that it wants to restrict
104 * deserialization so that it would only modify certain objects or certain
105 * members by the deserialization process, leaving all other ones untouched.
106 * So this partial deserialization technique for example allows to implement
107 * flexible preset features for applications in a powerful and easy way.
108 */
109 namespace Serialization {
110
111 // just symbol prototyping
112 class DataType;
113 class Object;
114 class Member;
115 class Archive;
116 class ObjectPool;
117 class Exception;
118
119 typedef std::string String;
120
121 typedef std::vector<uint8_t> RawData;
122
123 typedef void* ID;
124
125 typedef uint32_t Version;
126
127 enum time_base_t {
128 LOCAL_TIME,
129 UTC_TIME
130 };
131
132 template<typename T>
133 bool IsEnum(const T& data) {
134 #if !HAS_BUILTIN_TYPE_TRAITS
135 return std::tr1::is_enum<T>::value;
136 #else
137 return __is_enum(T);
138 #endif
139 }
140
141 template<typename T>
142 bool IsUnion(const T& data) {
143 #if !HAS_BUILTIN_TYPE_TRAITS
144 return false; // without compiler support we cannot distinguish union from class
145 #else
146 return __is_union(T);
147 #endif
148 }
149
150 template<typename T>
151 bool IsClass(const T& data) {
152 #if !HAS_BUILTIN_TYPE_TRAITS
153 return std::tr1::__is_union_or_class<T>::value; // without compiler support we cannot distinguish union from class
154 #else
155 return __is_class(T);
156 #endif
157 }
158
159 /*template<typename T>
160 bool IsTrivial(T data) {
161 return __is_trivial(T);
162 }*/
163
164 /*template<typename T>
165 bool IsPOD(T data) {
166 return __is_pod(T);
167 }*/
168
169 /** @brief Unique identifier for one specific C++ object, member or fundamental variable.
170 *
171 * Reflects a unique identifier for one specific serialized C++ class
172 * instance, struct instance, member, primitive pointer, or fundamental
173 * variables.
174 */
175 class UID {
176 public:
177 ID id;
178 size_t size;
179
180 bool isValid() const;
181 operator bool() const { return isValid(); }
182 //bool operator()() const { return isValid(); }
183 bool operator==(const UID& other) const { return id == other.id && size == other.size; }
184 bool operator!=(const UID& other) const { return id != other.id || size != other.size; }
185 bool operator<(const UID& other) const { return id < other.id || (id == other.id && size < other.size); }
186 bool operator>(const UID& other) const { return id > other.id || (id == other.id && size > other.size); }
187
188 template<typename T>
189 static UID from(const T& obj) {
190 return Resolver<T>::resolve(obj);
191 }
192
193 protected:
194 // UID resolver for non-pointer types
195 template<typename T>
196 struct Resolver {
197 static UID resolve(const T& obj) {
198 const UID uid = { (ID) &obj, sizeof(obj) };
199 return uid;
200 }
201 };
202
203 // UID resolver for pointer types (of 1st degree)
204 template<typename T>
205 struct Resolver<T*> {
206 static UID resolve(const T* const & obj) {
207 const UID uid = { (ID) obj, sizeof(*obj) };
208 return uid;
209 }
210 };
211 };
212
213 /**
214 * Reflects an invalid UID and behaves similar to NULL as invalid value for
215 * pointer types.
216 */
217 extern const UID NO_UID;
218
219 typedef std::vector<UID> UIDChain;
220
221 // prototyping of private internal friend functions
222 static String _encodePrimitiveValue(const Object& obj);
223 static DataType _popDataTypeBlob(const char*& p, const char* end);
224 static Member _popMemberBlob(const char*& p, const char* end);
225 static Object _popObjectBlob(const char*& p, const char* end);
226 static void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
227 static String _primitiveObjectValueToString(const Object& obj);
228 // |
229 template<typename T>
230 static T _primitiveObjectValueToNumber(const Object& obj);
231
232 /** @brief Abstract reflection of a native C++ data type.
233 *
234 * Provides detailed information about a C++ data type, whether it is a
235 * fundamental C/C++ data type (like int, float, char, etc.) or custom
236 * defined data type like a C++ class, struct, enum, as well as other
237 * features of the data type like its native memory size and more.
238 */
239 class DataType {
240 public:
241 DataType();
242 size_t size() const { return m_size; }
243 bool isValid() const;
244 bool isPointer() const;
245 bool isClass() const;
246 bool isPrimitive() const;
247 bool isInteger() const;
248 bool isReal() const;
249 bool isBool() const;
250 bool isEnum() const;
251 bool isSigned() const;
252 operator bool() const { return isValid(); }
253 //bool operator()() const { return isValid(); }
254 bool operator==(const DataType& other) const;
255 bool operator!=(const DataType& other) const;
256 bool operator<(const DataType& other) const;
257 bool operator>(const DataType& other) const;
258 String asLongDescr() const;
259 String baseTypeName() const { return m_baseTypeName; }
260 String customTypeName(bool demangle = false) const;
261
262 template<typename T>
263 static DataType dataTypeOf(const T& data) {
264 return Resolver<T>::resolve(data);
265 }
266
267 protected:
268 DataType(bool isPointer, int size, String baseType, String customType = "");
269
270 template<typename T, bool T_isPointer>
271 struct ResolverBase {
272 static DataType resolve(const T& data) {
273 const std::type_info& type = typeid(data);
274 const int sz = sizeof(data);
275
276 // for primitive types we are using our own type names instead of
277 // using std:::type_info::name(), because the precise output of the
278 // latter may vary between compilers
279 if (type == typeid(int8_t)) return DataType(T_isPointer, sz, "int8");
280 if (type == typeid(uint8_t)) return DataType(T_isPointer, sz, "uint8");
281 if (type == typeid(int16_t)) return DataType(T_isPointer, sz, "int16");
282 if (type == typeid(uint16_t)) return DataType(T_isPointer, sz, "uint16");
283 if (type == typeid(int32_t)) return DataType(T_isPointer, sz, "int32");
284 if (type == typeid(uint32_t)) return DataType(T_isPointer, sz, "uint32");
285 if (type == typeid(int64_t)) return DataType(T_isPointer, sz, "int64");
286 if (type == typeid(uint64_t)) return DataType(T_isPointer, sz, "uint64");
287 if (type == typeid(bool)) return DataType(T_isPointer, sz, "bool");
288 if (type == typeid(float)) return DataType(T_isPointer, sz, "real32");
289 if (type == typeid(double)) return DataType(T_isPointer, sz, "real64");
290
291 if (IsEnum(data)) return DataType(T_isPointer, sz, "enum", rawCppTypeNameOf(data));
292 if (IsUnion(data)) return DataType(T_isPointer, sz, "union", rawCppTypeNameOf(data));
293 if (IsClass(data)) return DataType(T_isPointer, sz, "class", rawCppTypeNameOf(data));
294
295 return DataType();
296 }
297 };
298
299 // DataType resolver for non-pointer types
300 template<typename T>
301 struct Resolver : ResolverBase<T,false> {
302 static DataType resolve(const T& data) {
303 return ResolverBase<T,false>::resolve(data);
304 }
305 };
306
307 // DataType resolver for pointer types (of 1st degree)
308 template<typename T>
309 struct Resolver<T*> : ResolverBase<T,true> {
310 static DataType resolve(const T*& data) {
311 return ResolverBase<T,true>::resolve(*data);
312 }
313 };
314
315 template<typename T>
316 static String rawCppTypeNameOf(const T& data) {
317 #if defined _MSC_VER // Microsoft compiler ...
318 # warning type_info::raw_name() demangling has not been tested yet with Microsoft compiler! Feedback appreciated!
319 String name = typeid(data).raw_name(); //NOTE: I haven't checked yet what MSC actually outputs here exactly
320 #else // i.e. especially GCC and clang ...
321 String name = typeid(data).name();
322 #endif
323 //while (!name.empty() && name[0] >= 0 && name[0] <= 9)
324 // name = name.substr(1);
325 return name;
326 }
327
328 private:
329 String m_baseTypeName;
330 String m_customTypeName;
331 int m_size;
332 bool m_isPointer;
333
334 friend DataType _popDataTypeBlob(const char*& p, const char* end);
335 friend class Archive;
336 };
337
338 /** @brief Abstract reflection of a native C++ class/struct's member variable.
339 *
340 * Provides detailed information about a specific C++ member variable of
341 * serialized C++ object, like its C++ data type, offset of this member
342 * within its containing data structure/class, its C++ member variable name
343 * and more.
344 */
345 class Member {
346 public:
347 Member();
348 UID uid() const { return m_uid; }
349 String name() const { return m_name; }
350 size_t offset() const { return m_offset; }
351 const DataType& type() const { return m_type; }
352 bool isValid() const;
353 operator bool() const { return isValid(); }
354 //bool operator()() const { return isValid(); }
355 bool operator==(const Member& other) const;
356 bool operator!=(const Member& other) const;
357 bool operator<(const Member& other) const;
358 bool operator>(const Member& other) const;
359
360 protected:
361 Member(String name, UID uid, size_t offset, DataType type);
362 friend class Archive;
363
364 private:
365 UID m_uid;
366 size_t m_offset;
367 String m_name;
368 DataType m_type;
369
370 friend Member _popMemberBlob(const char*& p, const char* end);
371 };
372
373 /** @brief Abstract reflection of a native C++ class/struct instance.
374 *
375 * Provides detailed information about a specific serialized C++ object,
376 * like its C++ member variables, its C++ class/struct name, its native
377 * memory size and more.
378 */
379 class Object {
380 public:
381 Object();
382 Object(UIDChain uidChain, DataType type);
383
384 UID uid(int index = 0) const {
385 return (index < m_uid.size()) ? m_uid[index] : NO_UID;
386 }
387
388 const UIDChain& uidChain() const { return m_uid; }
389 const DataType& type() const { return m_type; }
390 const RawData& rawData() const { return m_data; }
391 Version version() const { return m_version; }
392 Version minVersion() const { return m_minVersion; }
393 bool isVersionCompatibleTo(const Object& other) const;
394 std::vector<Member>& members() { return m_members; }
395 const std::vector<Member>& members() const { return m_members; }
396 Member memberNamed(String name) const;
397 Member memberByUID(const UID& uid) const;
398 std::vector<Member> membersOfType(const DataType& type) const;
399 int sequenceIndexOf(const Member& member) const;
400 bool isValid() const;
401 operator bool() const { return isValid(); }
402 //bool operator()() const { return isValid(); }
403 bool operator==(const Object& other) const;
404 bool operator!=(const Object& other) const;
405 bool operator<(const Object& other) const;
406 bool operator>(const Object& other) const;
407
408 protected:
409 void remove(const Member& member);
410 void setVersion(Version v);
411 void setMinVersion(Version v);
412
413 private:
414 DataType m_type;
415 UIDChain m_uid;
416 Version m_version;
417 Version m_minVersion;
418 RawData m_data;
419 std::vector<Member> m_members;
420
421 friend String _encodePrimitiveValue(const Object& obj);
422 friend Object _popObjectBlob(const char*& p, const char* end);
423 friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
424 friend String _primitiveObjectValueToString(const Object& obj);
425
426 template<typename T>
427 friend T _primitiveObjectValueToNumber(const Object& obj);
428
429 friend class Archive;
430 };
431
432 /** @brief Destination container for serialization, and source container for deserialization.
433 *
434 * This is the main class for implementing serialization and deserialization
435 * with your C++ application. This framework does not require a a tree
436 * structured layout of your C++ objects being serialized/deserialized, it
437 * uses a concept of a "root" object though. So to start serialization
438 * construct an empty Archive object and then instruct it to serialize your
439 * C++ objects by pointing it to your "root" object:
440 * @code
441 * Archive a;
442 * a.serialize(&myRootObject);
443 * @endcode
444 * Or if you prefer the look of operator based code:
445 * @code
446 * Archive a;
447 * a << myRootObject;
448 * @endcode
449 * The Archive object will then serialize all members of the passed C++
450 * object, and will recursively serialize all other C++ objects which it
451 * contains or points to. So the root object is the starting point for the
452 * overall serialization. After the serialize() method returned, you can
453 * then access the serialized data stream by calling rawData() and send that
454 * data stream over "wire", or store it on disk or whatever you may intend
455 * to do with it.
456 *
457 * Then on receiver side likewise, you create a new Archive object, pass the
458 * received data stream i.e. via constructor to the Archive object and call
459 * deserialize() by pointing it to the root object on receiver side:
460 * @code
461 * Archive a(rawDataStream);
462 * a.deserialize(&myRootObject);
463 * @endcode
464 * Or with operator instead:
465 * @code
466 * Archive a(rawDataStream);
467 * a >> myRootObject;
468 * @endcode
469 * Now this framework automatically handles serialization and
470 * deserialization of fundamental data types automatically for you (like
471 * i.e. char, int, long int, float, double, etc.). However for your own
472 * custom C++ classes and structs you must implement one method which
473 * defines which members of your class should actually be serialized and
474 * deserialized. That method to be added must have the following signature:
475 * @code
476 * void serialize(Serialization::Archive* archive);
477 * @endcode
478 * So let's say you have the following simple data structures:
479 * @code
480 * struct Foo {
481 * int a;
482 * bool b;
483 * double c;
484 * };
485 *
486 * struct Bar {
487 * char one;
488 * float two;
489 * Foo foo1;
490 * Foo* pFoo2;
491 * Foo* pFoo3DontTouchMe; // shall not be serialized/deserialized
492 * };
493 * @endcode
494 * So in order to be able to serialize and deserialize objects of those two
495 * structures you would first add the mentioned method to each struct
496 * definition (i.e. in your header file):
497 * @code
498 * struct Foo {
499 * int a;
500 * bool b;
501 * double c;
502 *
503 * void serialize(Serialization::Archive* archive);
504 * };
505 *
506 * struct Bar {
507 * char one;
508 * float two;
509 * Foo foo1;
510 * Foo* pFoo2;
511 * Foo* pFoo3DontTouchMe; // shall not be serialized/deserialized
512 *
513 * void serialize(Serialization::Archive* archive);
514 * };
515 * @endcode
516 * And then you would implement those two new methods like this (i.e. in
517 * your .cpp file):
518 * @code
519 * #define SRLZ(member) \
520 * archive->serializeMember(*this, member, #member);
521 *
522 * void Foo::serialize(Serialization::Archive* archive) {
523 * SRLZ(a);
524 * SRLZ(b);
525 * SRLZ(c);
526 * }
527 *
528 * void Bar::serialize(Serialization::Archive* archive) {
529 * SRLZ(one);
530 * SRLZ(two);
531 * SRLZ(foo1);
532 * SRLZ(pFoo2);
533 * // leaving out pFoo3DontTouchMe here
534 * }
535 * @endcode
536 * Now when you serialize such a Bar object, this framework will also
537 * automatically serialize the respective Foo object(s) accordingly, also
538 * for the pFoo2 pointer for instance (as long as it is not a NULL pointer
539 * that is).
540 *
541 * Note that there is only one method that you need to implement. So the
542 * respective serialize() method implementation of your classes/structs are
543 * both called for serialization, as well as for deserialization!
544 *
545 * In case you need to enforce backward incompatiblity for one of your C++
546 * classes, you can do so by setting a version and minimum version for your
547 * class (see @c setVersion() and @c setMinVersion() for details).
548 */
549 class Archive {
550 public:
551 Archive();
552 Archive(const RawData& data);
553 Archive(const uint8_t* data, size_t size);
554 virtual ~Archive();
555
556 template<typename T>
557 void serialize(const T* obj) {
558 m_operation = OPERATION_SERIALIZE;
559 m_allObjects.clear();
560 m_rawData.clear();
561 m_root = UID::from(obj);
562 const_cast<T*>(obj)->serialize(this);
563 encode();
564 m_operation = OPERATION_NONE;
565 }
566
567 template<typename T>
568 void deserialize(T* obj) {
569 Archive a;
570 m_operation = OPERATION_DESERIALIZE;
571 obj->serialize(&a);
572 a.m_root = UID::from(obj);
573 Syncer s(a, *this);
574 m_operation = OPERATION_NONE;
575 }
576
577 template<typename T>
578 void operator<<(const T& obj) {
579 serialize(&obj);
580 }
581
582 template<typename T>
583 void operator>>(T& obj) {
584 deserialize(&obj);
585 }
586
587 const RawData& rawData();
588 virtual String rawDataFormat() const;
589
590 template<typename T_classType, typename T_memberType>
591 void serializeMember(const T_classType& nativeObject, const T_memberType& nativeMember, const char* memberName) {
592 const size_t offset =
593 ((const uint8_t*)(const void*)&nativeMember) -
594 ((const uint8_t*)(const void*)&nativeObject);
595 const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);
596 const DataType type = DataType::dataTypeOf(nativeMember);
597 const Member member(memberName, uids[0], offset, type);
598 const UID parentUID = UID::from(nativeObject);
599 Object& parent = m_allObjects[parentUID];
600 if (!parent) {
601 const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
602 const DataType type = DataType::dataTypeOf(nativeObject);
603 parent = Object(uids, type);
604 }
605 parent.members().push_back(member);
606 const Object obj(uids, type);
607 const bool bExistsAlready = m_allObjects.count(uids[0]);
608 const bool isValidObject = obj;
609 const bool bExistingObjectIsInvalid = !m_allObjects[uids[0]];
610 if (!bExistsAlready || (bExistingObjectIsInvalid && isValidObject)) {
611 m_allObjects[uids[0]] = obj;
612 // recurse serialization for all members of this member
613 // (only for struct/class types, noop for primitive types)
614 SerializationRecursion<T_memberType>::serializeObject(this, nativeMember);
615 }
616 }
617
618 /** @brief Set version number for your C++ class.
619 *
620 * By calling this method you can store a version number for your
621 * current C++ class (that is a version for its current data structure
622 * layout and method implementations) with serialized archive.
623 *
624 * Along with calling @c setMinVersion() this provides a way for you
625 * to constrain backward compatiblity regarding serialization and
626 * deserialization of your class which the Archive class will obey to.
627 * If required, then typically you might do so in your @c serialize()
628 * method implementation like:
629 * @code
630 * #define SRLZ(member) \
631 * archive->serializeMember(*this, member, #member);
632 *
633 * void Foo::serialize(Serialization::Archive* archive) {
634 * // when serializing: the current version of this class that is
635 * // going to be stored with the serialized archive
636 * archive->setVersion(*this, 6);
637 * // when deserializing: the minimum allowed version of this class
638 * // being serialized in the past
639 * archive->setMinVersion(*this, 3);
640 * // actual data mebers to serialize / deserialize
641 * SRLZ(a);
642 * SRLZ(b);
643 * SRLZ(c);
644 * }
645 * @endcode
646 * In this example above, the C++ clas "Foo" would be serialized along
647 * with the version number @c 6 in the resulting archive (and its raw
648 * data stream respectively).
649 *
650 * When deserializing archives with the example C++ class code above,
651 * the Archive object would check whether your originally serialized
652 * C++ "Foo" object had at least version number @c 3, if not the
653 * deserialization process would automatically be stopped with a
654 * @c Serialization::Exception, claiming that the classes are version
655 * incompatible.
656 *
657 * Since this Serialization / deserialization framework is designed to
658 * be robust on changes to your C++ classes and aims trying to
659 * deserialize all your C++ objects correctly even if your C++ classes
660 * have seen substantial software changes in the meantime; you might
661 * sometimes see it as necessary to constrain backward compatiblity
662 * this way.
663 *
664 * @param nativeObject - your C++ object you want to set a version for
665 * @param v - the version number to set for your C++ class (by default,
666 * that is if you do not explicitly call this method, then
667 * your C++ object will be stored with version number @c 0 ).
668 */
669 template<typename T_classType>
670 void setVersion(const T_classType& nativeObject, Version v) {
671 const UID uid = UID::from(nativeObject);
672 Object& obj = m_allObjects[uid];
673 if (!obj) {
674 const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
675 const DataType type = DataType::dataTypeOf(nativeObject);
676 obj = Object(uids, type);
677 }
678 setVersion(obj, v);
679 }
680
681 /** @brief Set a minimum version number for your C++ class.
682 *
683 * Call this method to define a minimum version that your current C++
684 * class implementation would be compatible with when it comes to
685 * deserialization of an archive containing an object with an older
686 * version of your C++ class.
687 *
688 * @see @c setVersion() for more details about this overall topic.
689 */
690 template<typename T_classType>
691 void setMinVersion(const T_classType& nativeObject, Version v) {
692 const UID uid = UID::from(nativeObject);
693 Object& obj = m_allObjects[uid];
694 if (!obj) {
695 const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
696 const DataType type = DataType::dataTypeOf(nativeObject);
697 obj = Object(uids, type);
698 }
699 setMinVersion(obj, v);
700 }
701
702 virtual void decode(const RawData& data);
703 virtual void decode(const uint8_t* data, size_t size);
704 void clear();
705 bool isModified() const;
706 void removeMember(Object& parent, const Member& member);
707 void remove(const Object& obj);
708 Object& rootObject();
709 Object& objectByUID(const UID& uid);
710 void setAutoValue(Object& object, String value);
711 void setIntValue(Object& object, int64_t value);
712 void setRealValue(Object& object, double value);
713 void setBoolValue(Object& object, bool value);
714 void setEnumValue(Object& object, uint64_t value);
715 String valueAsString(const Object& object);
716 int64_t valueAsInt(const Object& object);
717 double valueAsReal(const Object& object);
718 bool valueAsBool(const Object& object);
719 void setVersion(Object& object, Version v);
720 void setMinVersion(Object& object, Version v);
721 String name() const;
722 void setName(String name);
723 String comment() const;
724 void setComment(String comment);
725 time_t timeStampCreated() const;
726 time_t timeStampModified() const;
727 tm dateTimeCreated(time_base_t base = LOCAL_TIME) const;
728 tm dateTimeModified(time_base_t base = LOCAL_TIME) const;
729
730 protected:
731 // UID resolver for non-pointer types
732 template<typename T>
733 class UIDChainResolver {
734 public:
735 UIDChainResolver(const T& data) {
736 m_uid.push_back(UID::from(data));
737 }
738
739 operator UIDChain() const { return m_uid; }
740 UIDChain operator()() const { return m_uid; }
741 private:
742 UIDChain m_uid;
743 };
744
745 // UID resolver for pointer types (of 1st degree)
746 template<typename T>
747 class UIDChainResolver<T*> {
748 public:
749 UIDChainResolver(const T*& data) {
750 const UID uids[2] = {
751 { &data, sizeof(data) },
752 { data, sizeof(*data) }
753 };
754 m_uid.push_back(uids[0]);
755 m_uid.push_back(uids[1]);
756 }
757
758 operator UIDChain() const { return m_uid; }
759 UIDChain operator()() const { return m_uid; }
760 private:
761 UIDChain m_uid;
762 };
763
764 // SerializationRecursion for non-pointer class/struct types.
765 template<typename T, bool T_isRecursive>
766 struct SerializationRecursionImpl {
767 static void serializeObject(Archive* archive, const T& obj) {
768 const_cast<T&>(obj).serialize(archive);
769 }
770 };
771
772 // SerializationRecursion for pointers (of 1st degree) to class/structs.
773 template<typename T, bool T_isRecursive>
774 struct SerializationRecursionImpl<T*,T_isRecursive> {
775 static void serializeObject(Archive* archive, const T*& obj) {
776 if (!obj) return;
777 const_cast<T*&>(obj)->serialize(archive);
778 }
779 };
780
781 // NOOP SerializationRecursion for primitive types.
782 template<typename T>
783 struct SerializationRecursionImpl<T,false> {
784 static void serializeObject(Archive* archive, const T& obj) {}
785 };
786
787 // NOOP SerializationRecursion for pointers (of 1st degree) to primitive types.
788 template<typename T>
789 struct SerializationRecursionImpl<T*,false> {
790 static void serializeObject(Archive* archive, const T*& obj) {}
791 };
792
793 // Automatically handles recursion for class/struct types, while ignoring all primitive types.
794 template<typename T>
795 struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> {
796 };
797
798 class ObjectPool : public std::map<UID,Object> {
799 public:
800 // prevent passing obvious invalid UID values from creating a new pair entry
801 Object& operator[](const UID& k) {
802 static Object invalid;
803 if (!k.isValid()) {
804 invalid = Object();
805 return invalid;
806 }
807 return std::map<UID,Object>::operator[](k);
808 }
809 };
810
811 friend String _encode(const ObjectPool& objects);
812
813 private:
814 String _encodeRootBlob();
815 void _popRootBlob(const char*& p, const char* end);
816 void _popObjectsBlob(const char*& p, const char* end);
817
818 protected:
819 class Syncer {
820 public:
821 Syncer(Archive& dst, Archive& src);
822 protected:
823 void syncObject(const Object& dst, const Object& src);
824 void syncPrimitive(const Object& dst, const Object& src);
825 void syncPointer(const Object& dst, const Object& src);
826 void syncMember(const Member& dstMember, const Member& srcMember);
827 static Member dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember);
828 private:
829 Archive& m_dst;
830 Archive& m_src;
831 };
832
833 enum operation_t {
834 OPERATION_NONE,
835 OPERATION_SERIALIZE,
836 OPERATION_DESERIALIZE
837 };
838
839 virtual void encode();
840
841 ObjectPool m_allObjects;
842 operation_t m_operation;
843 UID m_root;
844 RawData m_rawData;
845 bool m_isModified;
846 String m_name;
847 String m_comment;
848 time_t m_timeCreated;
849 time_t m_timeModified;
850 };
851
852 /**
853 * Will be thrown whenever an error occurs during an serialization or
854 * deserialization process.
855 */
856 class Exception {
857 public:
858 String Message;
859
860 Exception(String Message) { Exception::Message = Message; }
861 void PrintMessage();
862 virtual ~Exception() {}
863 };
864
865 } // namespace Serialization
866
867 #endif // LIBGIG_SERIALIZATION_H

  ViewVC Help
Powered by ViewVC