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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3182 - (hide 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 schoenebeck 3138 /***************************************************************************
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 schoenebeck 3156 #include <time.h>
38 schoenebeck 3178
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 schoenebeck 3163 # include <tr1/type_traits>
61 schoenebeck 3167 # 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 schoenebeck 3163 #endif
65 schoenebeck 3138
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 schoenebeck 3146 // just symbol prototyping
112     class DataType;
113     class Object;
114     class Member;
115 schoenebeck 3138 class Archive;
116 schoenebeck 3146 class ObjectPool;
117 schoenebeck 3138 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 schoenebeck 3156 enum time_base_t {
128     LOCAL_TIME,
129     UTC_TIME
130     };
131    
132 schoenebeck 3138 template<typename T>
133     bool IsEnum(const T& data) {
134 schoenebeck 3178 #if !HAS_BUILTIN_TYPE_TRAITS
135 schoenebeck 3165 return std::tr1::is_enum<T>::value;
136 schoenebeck 3164 #else
137 schoenebeck 3138 return __is_enum(T);
138 schoenebeck 3164 #endif
139 schoenebeck 3138 }
140    
141     template<typename T>
142     bool IsUnion(const T& data) {
143 schoenebeck 3178 #if !HAS_BUILTIN_TYPE_TRAITS
144 schoenebeck 3166 return false; // without compiler support we cannot distinguish union from class
145 schoenebeck 3164 #else
146 schoenebeck 3138 return __is_union(T);
147 schoenebeck 3164 #endif
148 schoenebeck 3138 }
149    
150     template<typename T>
151     bool IsClass(const T& data) {
152 schoenebeck 3178 #if !HAS_BUILTIN_TYPE_TRAITS
153 schoenebeck 3166 return std::tr1::__is_union_or_class<T>::value; // without compiler support we cannot distinguish union from class
154 schoenebeck 3164 #else
155 schoenebeck 3138 return __is_class(T);
156 schoenebeck 3164 #endif
157 schoenebeck 3138 }
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 schoenebeck 3168 const UID uid = { (ID) &obj, sizeof(obj) };
199     return uid;
200 schoenebeck 3138 }
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 schoenebeck 3168 const UID uid = { (ID) obj, sizeof(*obj) };
208     return uid;
209 schoenebeck 3138 }
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 schoenebeck 3146 // prototyping of private internal friend functions
222 schoenebeck 3150 static String _encodePrimitiveValue(const Object& obj);
223 schoenebeck 3146 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 schoenebeck 3150 static String _primitiveObjectValueToString(const Object& obj);
228 schoenebeck 3169 // |
229     template<typename T>
230     static T _primitiveObjectValueToNumber(const Object& obj);
231 schoenebeck 3146
232 schoenebeck 3138 /** @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 schoenebeck 3173 String customTypeName(bool demangle = false) const;
261 schoenebeck 3138
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 schoenebeck 3150 friend class Archive;
336 schoenebeck 3138 };
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 schoenebeck 3153 Member memberByUID(const UID& uid) const;
398 schoenebeck 3138 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 schoenebeck 3153 protected:
409     void remove(const Member& member);
410 schoenebeck 3182 void setVersion(Version v);
411     void setMinVersion(Version v);
412 schoenebeck 3153
413 schoenebeck 3138 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 schoenebeck 3150 friend String _encodePrimitiveValue(const Object& obj);
422 schoenebeck 3138 friend Object _popObjectBlob(const char*& p, const char* end);
423     friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
424 schoenebeck 3150 friend String _primitiveObjectValueToString(const Object& obj);
425 schoenebeck 3169
426     template<typename T>
427     friend T _primitiveObjectValueToNumber(const Object& obj);
428    
429 schoenebeck 3150 friend class Archive;
430 schoenebeck 3138 };
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 schoenebeck 3142 * @endcode
444 schoenebeck 3138 * Or if you prefer the look of operator based code:
445     * @code
446     * Archive a;
447     * a << myRootObject;
448 schoenebeck 3142 * @endcode
449 schoenebeck 3138 * 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 schoenebeck 3142 * @endcode
464 schoenebeck 3138 * Or with operator instead:
465     * @code
466     * Archive a(rawDataStream);
467     * a >> myRootObject;
468 schoenebeck 3142 * @endcode
469 schoenebeck 3138 * 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 schoenebeck 3182 *
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 schoenebeck 3138 */
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 schoenebeck 3150 const RawData& rawData();
588 schoenebeck 3138 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 schoenebeck 3182 ((const uint8_t*)(const void*)&nativeMember) -
594     ((const uint8_t*)(const void*)&nativeObject);
595 schoenebeck 3138 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 schoenebeck 3182 /** @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 schoenebeck 3138 virtual void decode(const RawData& data);
703     virtual void decode(const uint8_t* data, size_t size);
704     void clear();
705 schoenebeck 3150 bool isModified() const;
706 schoenebeck 3153 void removeMember(Object& parent, const Member& member);
707 schoenebeck 3138 void remove(const Object& obj);
708     Object& rootObject();
709     Object& objectByUID(const UID& uid);
710 schoenebeck 3150 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 schoenebeck 3169 int64_t valueAsInt(const Object& object);
717     double valueAsReal(const Object& object);
718     bool valueAsBool(const Object& object);
719 schoenebeck 3182 void setVersion(Object& object, Version v);
720     void setMinVersion(Object& object, Version v);
721 schoenebeck 3156 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 schoenebeck 3138
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 schoenebeck 3168 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 schoenebeck 3138 }
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 schoenebeck 3167 struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> {
796 schoenebeck 3138 };
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 schoenebeck 3182 enum operation_t {
834     OPERATION_NONE,
835     OPERATION_SERIALIZE,
836     OPERATION_DESERIALIZE
837     };
838    
839 schoenebeck 3138 virtual void encode();
840    
841     ObjectPool m_allObjects;
842     operation_t m_operation;
843     UID m_root;
844     RawData m_rawData;
845 schoenebeck 3150 bool m_isModified;
846 schoenebeck 3156 String m_name;
847     String m_comment;
848     time_t m_timeCreated;
849     time_t m_timeModified;
850 schoenebeck 3138 };
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