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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3774 - (hide annotations) (download) (as text)
Tue May 19 14:55:48 2020 UTC (3 years, 11 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 53881 byte(s)
* Serialization.cpp/.h: Added new method Archive::operation() which
  allows applications to distinguish between serialization vs.
  deserialization in their serialize() method implementations.

* Bumped version (4.2.0.svn12).

1 schoenebeck 3138 /***************************************************************************
2     * *
3 schoenebeck 3771 * Copyright (C) 2017-2020 Christian Schoenebeck *
4 schoenebeck 3476 * <cuse@users.sourceforge.net> *
5 schoenebeck 3138 * *
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 3198 #include <stdarg.h>
39 schoenebeck 3773 #include <assert.h>
40 schoenebeck 3178
41     #ifndef __has_extension
42     # define __has_extension(x) 0
43     #endif
44    
45     #ifndef HAS_BUILTIN_TYPE_TRAITS
46     # if __cplusplus >= 201103L
47     # define HAS_BUILTIN_TYPE_TRAITS 1
48     # elif ( __has_extension(is_class) && __has_extension(is_enum) )
49     # define HAS_BUILTIN_TYPE_TRAITS 1
50     # elif ( __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 3 ) )
51     # define HAS_BUILTIN_TYPE_TRAITS 1
52     # elif _MSC_VER >= 1400 /* MS Visual C++ 8.0 (Visual Studio 2005) */
53     # define HAS_BUILTIN_TYPE_TRAITS 1
54     # elif __INTEL_COMPILER >= 1100
55     # define HAS_BUILTIN_TYPE_TRAITS 1
56     # else
57     # define HAS_BUILTIN_TYPE_TRAITS 0
58     # endif
59     #endif
60    
61     #if !HAS_BUILTIN_TYPE_TRAITS
62 schoenebeck 3163 # include <tr1/type_traits>
63 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
64     #else
65     # define LIBGIG_IS_CLASS(type) __is_class(type)
66 schoenebeck 3163 #endif
67 schoenebeck 3138
68     /** @brief Serialization / deserialization framework.
69     *
70     * See class Archive as starting point for how to implement serialization and
71     * deserialization with your application.
72     *
73     * The classes in this namespace allow to serialize and deserialize native
74     * C++ objects in a portable, easy and flexible way. Serialization is a
75     * technique that allows to transform the current state and data of native
76     * (in this case C++) objects into a data stream (including all other objects
77     * the "serialized" objects relate to); the data stream may then be sent over
78     * "wire" (for example via network connection to another computer, which might
79     * also have a different OS, CPU architecture, native memory word size and
80     * endian type); and finally the data stream would be "deserialized" on that
81     * receiver side, that is transformed again to modify all objects and data
82     * structures on receiver side to resemble the objects' state and data as it
83     * was originally on sender side.
84     *
85     * In contrast to many other already existing serialization frameworks, this
86     * implementation has a strong focus on robustness regarding long-term changes
87     * to the serialized C++ classes of the serialized objects. So even if sender
88     * and receiver are using different versions of their serialized/deserialized
89     * C++ classes, structures and data types (thus having different data structure
90     * layout to a certain extent), this framework aims trying to automatically
91     * adapt its serialization and deserialization process in that case so that
92     * the deserialized objects on receiver side would still reflect the overall
93     * expected states and overall data as intended by the sender. For being able to
94     * do so, this framework stores all kind of additional information about each
95     * serialized object and each data structure member (for example name of each
96     * data structure member, but also the offset of each member within its
97     * containing data structure, precise data types, and more).
98     *
99     * Like most other serialization frameworks, this frameworks does not require a
100     * tree-structured layout of the serialized data structures. So it automatically
101     * handles also cyclic dependencies between serialized data structures
102     * correctly, without i.e. causing endless recursion or redundancy.
103     *
104     * Additionally this framework also allows partial deserialization. Which means
105     * the receiver side may for example decide that it wants to restrict
106     * deserialization so that it would only modify certain objects or certain
107     * members by the deserialization process, leaving all other ones untouched.
108     * So this partial deserialization technique for example allows to implement
109     * flexible preset features for applications in a powerful and easy way.
110     */
111     namespace Serialization {
112    
113 schoenebeck 3146 // just symbol prototyping
114     class DataType;
115     class Object;
116     class Member;
117 schoenebeck 3138 class Archive;
118 schoenebeck 3146 class ObjectPool;
119 schoenebeck 3138 class Exception;
120    
121     typedef std::string String;
122    
123 schoenebeck 3183 /** @brief Raw data stream of serialized C++ objects.
124     *
125     * This data type is used for the data stream as a result of serializing
126     * your C++ objects with Archive::serialize(), and for native raw data
127     * representation of individual serialized C/C++ objects, members and variables.
128     *
129     * @see Archive::rawData(), Object::rawData()
130     */
131 schoenebeck 3138 typedef std::vector<uint8_t> RawData;
132    
133 schoenebeck 3183 /** @brief Abstract identifier for serialized C++ objects.
134     *
135     * This data type is used for identifying serialized C++ objects and members
136     * of your C++ objects. It is important to know that such an ID might not
137     * necessarily be unique. For example the ID of one C++ object might often
138     * be identical to the ID of the first member of that particular C++ object.
139     * That's why there is additionally the concept of an UID in this framework.
140     *
141     * @see UID
142     */
143 schoenebeck 3138 typedef void* ID;
144    
145 schoenebeck 3183 /** @brief Version number data type.
146     *
147     * This data type is used for maintaining version number information of
148     * your C++ class implementations.
149     *
150     * @see Archive::setVersion() and Archive::setMinVersion()
151     */
152 schoenebeck 3138 typedef uint32_t Version;
153    
154 schoenebeck 3183 /** @brief To which time zone a certain timing information relates to.
155     *
156     * The constants in this enum type are used to define to which precise time
157     * zone a time stamp relates to.
158     */
159 schoenebeck 3156 enum time_base_t {
160 schoenebeck 3183 LOCAL_TIME, ///< The time stamp relates to the machine's local time zone. Request a time stamp in local time if you want to present that time stamp to the end user.
161     UTC_TIME ///< The time stamp relates to "Greenwhich Mean Time" zone, also known as "Coordinated Universal Time". Request time stamp with UTC if you want to compare that time stamp with other time stamps.
162 schoenebeck 3156 };
163    
164 schoenebeck 3183 /** @brief Check whether data is a C/C++ @c enum type.
165     *
166     * Returns true if the supplied C++ variable or object is of a C/C++ @c enum
167     * type.
168     *
169     * @param data - the variable or object whose data type shall be checked
170     */
171 schoenebeck 3138 template<typename T>
172     bool IsEnum(const T& data) {
173 schoenebeck 3178 #if !HAS_BUILTIN_TYPE_TRAITS
174 schoenebeck 3165 return std::tr1::is_enum<T>::value;
175 schoenebeck 3164 #else
176 schoenebeck 3138 return __is_enum(T);
177 schoenebeck 3164 #endif
178 schoenebeck 3138 }
179    
180 schoenebeck 3183 /** @brief Check whether data is a C++ @c union type.
181     *
182     * Returns true if the supplied C++ variable or object is of a C/C++ @c union
183     * type. Note that the result of this function is only reliable if the C++
184     * compiler you are using has support for built-in type traits. If your C++
185     * compiler does not have built-in type traits support, then this function
186     * will simply return @c false on all your calls.
187     *
188     * @param data - the variable or object whose data type shall be checked
189     */
190 schoenebeck 3138 template<typename T>
191     bool IsUnion(const T& data) {
192 schoenebeck 3178 #if !HAS_BUILTIN_TYPE_TRAITS
193 schoenebeck 3166 return false; // without compiler support we cannot distinguish union from class
194 schoenebeck 3164 #else
195 schoenebeck 3138 return __is_union(T);
196 schoenebeck 3164 #endif
197 schoenebeck 3138 }
198    
199 schoenebeck 3183 /** @brief Check whether data is a C/C++ @c struct or C++ @c class type.
200     *
201     * Returns true if the supplied C++ variable or object is of C/C++ @c struct
202     * or C++ @c class type. Note that if you are using a C++ compiler which
203     * does have built-in type traits support, then this function will also
204     * return @c true on C/C++ @c union types.
205     *
206     * @param data - the variable or object whose data type shall be checked
207     */
208 schoenebeck 3138 template<typename T>
209     bool IsClass(const T& data) {
210 schoenebeck 3178 #if !HAS_BUILTIN_TYPE_TRAITS
211 schoenebeck 3166 return std::tr1::__is_union_or_class<T>::value; // without compiler support we cannot distinguish union from class
212 schoenebeck 3164 #else
213 schoenebeck 3138 return __is_class(T);
214 schoenebeck 3164 #endif
215 schoenebeck 3138 }
216    
217     /*template<typename T>
218     bool IsTrivial(T data) {
219     return __is_trivial(T);
220     }*/
221    
222     /*template<typename T>
223     bool IsPOD(T data) {
224     return __is_pod(T);
225     }*/
226    
227 schoenebeck 3185 /** @brief Unique identifier referring to one specific native C++ object, member, fundamental variable, or any other native C++ data.
228 schoenebeck 3138 *
229 schoenebeck 3185 * Reflects a unique identifier for one specific serialized C++ data, i.e.
230     * C++ class instance, C/C++ struct instance, member, primitive pointer,
231     * fundamental variables, or any other native C/C++ data originally being
232     * serialized.
233 schoenebeck 3183 *
234     * A unique identifier is composed of an id (an identifier which is not
235     * necessarily unique) and a size. Since the underlying ID is derived from
236     * the original C++ object's memory location, such an ID is not sufficient
237     * to distinguish a particular C++ object from the first member of that C++
238     * object, since both typically share the same memory address. So
239     * additionally the memory size of the respective object or member is
240     * bundled with UID objects to make them unique and distinguishable.
241 schoenebeck 3138 */
242     class UID {
243     public:
244 schoenebeck 3183 ID id; ///< Abstract non-unique ID of the object or member in question.
245     size_t size; ///< Memory size of the object or member in question.
246 schoenebeck 3138
247     bool isValid() const;
248 schoenebeck 3183 operator bool() const { return isValid(); } ///< Same as calling isValid().
249 schoenebeck 3138 //bool operator()() const { return isValid(); }
250     bool operator==(const UID& other) const { return id == other.id && size == other.size; }
251     bool operator!=(const UID& other) const { return id != other.id || size != other.size; }
252     bool operator<(const UID& other) const { return id < other.id || (id == other.id && size < other.size); }
253     bool operator>(const UID& other) const { return id > other.id || (id == other.id && size > other.size); }
254    
255 schoenebeck 3183 /** @brief Create an unique indentifier for a native C++ object/member/variable.
256     *
257     * Creates and returns an unique identifier for the passed native C++
258     * object, object member or variable. For the same C++ object/member/variable
259     * this function will always return the same UID. For all other ones,
260     * this function is guaranteed to return a different UID.
261     */
262 schoenebeck 3138 template<typename T>
263     static UID from(const T& obj) {
264     return Resolver<T>::resolve(obj);
265     }
266    
267     protected:
268     // UID resolver for non-pointer types
269     template<typename T>
270     struct Resolver {
271     static UID resolve(const T& obj) {
272 schoenebeck 3168 const UID uid = { (ID) &obj, sizeof(obj) };
273     return uid;
274 schoenebeck 3138 }
275     };
276    
277     // UID resolver for pointer types (of 1st degree)
278     template<typename T>
279     struct Resolver<T*> {
280     static UID resolve(const T* const & obj) {
281 schoenebeck 3168 const UID uid = { (ID) obj, sizeof(*obj) };
282     return uid;
283 schoenebeck 3138 }
284     };
285     };
286    
287     /**
288     * Reflects an invalid UID and behaves similar to NULL as invalid value for
289 schoenebeck 3183 * pointer types. All UID objects are first initialized with this value,
290     * and it essentially an all zero object.
291 schoenebeck 3138 */
292     extern const UID NO_UID;
293    
294 schoenebeck 3183 /** @brief Chain of UIDs.
295     *
296     * This data type is used for native C++ pointers. The first member of the
297     * UID chain is the unique identifier of the C++ pointer itself, then the
298     * following UIDs are the respective objects or variables the pointer is
299     * pointing to. The size (the amount of elements) of the UIDChain depends
300     * solely on the degree of the pointer type. For example the following C/C++
301     * pointer:
302     * @code
303     * int* pNumber;
304     * @endcode
305     * is an integer pointer of first degree. Such a pointer would have a
306     * UIDChain with 2 members: the first element would be the UID of the
307     * pointer itself, the second element of the chain would be the integer data
308     * that pointer is pointing to. In the following example:
309     * @code
310     * bool*** pppSomeFlag;
311     * @endcode
312     * That boolean pointer would be of third degree, and thus its UIDChain
313     * would have a size of 4 (elements).
314     *
315     * Accordingly a non pointer type like:
316     * @code
317     * float f;
318     * @endcode
319     * would yield in a UIDChain of size 1.
320     *
321     * Since however this serialization framework currently only supports
322     * pointers of first degree yet, all UIDChains are currently either of
323     * size 1 or 2, which might change in future though.
324     */
325 schoenebeck 3138 typedef std::vector<UID> UIDChain;
326    
327 schoenebeck 3392 #if LIBGIG_SERIALIZATION_INTERNAL
328 schoenebeck 3146 // prototyping of private internal friend functions
329 schoenebeck 3150 static String _encodePrimitiveValue(const Object& obj);
330 schoenebeck 3146 static DataType _popDataTypeBlob(const char*& p, const char* end);
331     static Member _popMemberBlob(const char*& p, const char* end);
332     static Object _popObjectBlob(const char*& p, const char* end);
333     static void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
334 schoenebeck 3150 static String _primitiveObjectValueToString(const Object& obj);
335 schoenebeck 3169 // |
336     template<typename T>
337     static T _primitiveObjectValueToNumber(const Object& obj);
338 schoenebeck 3392 #endif // LIBGIG_SERIALIZATION_INTERNAL
339 schoenebeck 3146
340 schoenebeck 3138 /** @brief Abstract reflection of a native C++ data type.
341     *
342 schoenebeck 3183 * Provides detailed information about a serialized C++ data type, whether
343     * it is a fundamental C/C++ data type (like @c int, @c float, @c char,
344     * etc.) or custom defined data types like a C++ @c class, C/C++ @c struct,
345     * @c enum, as well as other features of the respective data type like its
346     * native memory size and more.
347     *
348     * All informations provided by this class are retrieved from the
349     * respective individual C++ objects, their members and other data when
350     * they are serialized, and all those information are stored with the
351     * serialized archive and its resulting data stream. Due to the availability
352     * of these extensive data type information within serialized archives, this
353     * framework is capable to use them in order to adapt its deserialization
354     * process upon subsequent changes to your individual C++ classes.
355 schoenebeck 3138 */
356     class DataType {
357     public:
358     DataType();
359 schoenebeck 3183 size_t size() const { return m_size; } ///< Returns native memory size of the respective C++ object or variable.
360 schoenebeck 3138 bool isValid() const;
361     bool isPointer() const;
362     bool isClass() const;
363     bool isPrimitive() const;
364 schoenebeck 3771 bool isString() const;
365 schoenebeck 3138 bool isInteger() const;
366     bool isReal() const;
367     bool isBool() const;
368     bool isEnum() const;
369     bool isSigned() const;
370 schoenebeck 3183 operator bool() const { return isValid(); } ///< Same as calling isValid().
371 schoenebeck 3138 //bool operator()() const { return isValid(); }
372     bool operator==(const DataType& other) const;
373     bool operator!=(const DataType& other) const;
374     bool operator<(const DataType& other) const;
375     bool operator>(const DataType& other) const;
376     String asLongDescr() const;
377 schoenebeck 3183 String baseTypeName() const;
378 schoenebeck 3173 String customTypeName(bool demangle = false) const;
379 schoenebeck 3138
380 schoenebeck 3183 /** @brief Construct a DataType object for the given native C++ data.
381     *
382     * Use this function to create corresponding DataType objects for
383     * native C/C++ objects, members and variables.
384     *
385     * @param data - native C/C++ object/member/variable a DataType object
386     * shall be created for
387     * @returns corresponding DataType object for the supplied native C/C++
388     * object/member/variable
389     */
390 schoenebeck 3138 template<typename T>
391     static DataType dataTypeOf(const T& data) {
392     return Resolver<T>::resolve(data);
393     }
394    
395     protected:
396     DataType(bool isPointer, int size, String baseType, String customType = "");
397    
398     template<typename T, bool T_isPointer>
399     struct ResolverBase {
400     static DataType resolve(const T& data) {
401     const std::type_info& type = typeid(data);
402     const int sz = sizeof(data);
403    
404     // for primitive types we are using our own type names instead of
405     // using std:::type_info::name(), because the precise output of the
406     // latter may vary between compilers
407     if (type == typeid(int8_t)) return DataType(T_isPointer, sz, "int8");
408     if (type == typeid(uint8_t)) return DataType(T_isPointer, sz, "uint8");
409     if (type == typeid(int16_t)) return DataType(T_isPointer, sz, "int16");
410     if (type == typeid(uint16_t)) return DataType(T_isPointer, sz, "uint16");
411     if (type == typeid(int32_t)) return DataType(T_isPointer, sz, "int32");
412     if (type == typeid(uint32_t)) return DataType(T_isPointer, sz, "uint32");
413     if (type == typeid(int64_t)) return DataType(T_isPointer, sz, "int64");
414     if (type == typeid(uint64_t)) return DataType(T_isPointer, sz, "uint64");
415 schoenebeck 3773 if (type == typeid(size_t)) {
416     if (sz == 1) return DataType(T_isPointer, sz, "uint8");
417     if (sz == 2) return DataType(T_isPointer, sz, "uint16");
418     if (sz == 4) return DataType(T_isPointer, sz, "uint32");
419     if (sz == 8) return DataType(T_isPointer, sz, "uint64");
420     else assert(false /* unknown size_t size */);
421     }
422     if (type == typeid(ssize_t)) {
423     if (sz == 1) return DataType(T_isPointer, sz, "int8");
424     if (sz == 2) return DataType(T_isPointer, sz, "int16");
425     if (sz == 4) return DataType(T_isPointer, sz, "int32");
426     if (sz == 8) return DataType(T_isPointer, sz, "int64");
427     else assert(false /* unknown ssize_t size */);
428     }
429 schoenebeck 3138 if (type == typeid(bool)) return DataType(T_isPointer, sz, "bool");
430     if (type == typeid(float)) return DataType(T_isPointer, sz, "real32");
431     if (type == typeid(double)) return DataType(T_isPointer, sz, "real64");
432 schoenebeck 3771 if (type == typeid(String)) return DataType(T_isPointer, sz, "String");
433 schoenebeck 3138
434     if (IsEnum(data)) return DataType(T_isPointer, sz, "enum", rawCppTypeNameOf(data));
435     if (IsUnion(data)) return DataType(T_isPointer, sz, "union", rawCppTypeNameOf(data));
436     if (IsClass(data)) return DataType(T_isPointer, sz, "class", rawCppTypeNameOf(data));
437    
438     return DataType();
439     }
440     };
441    
442     // DataType resolver for non-pointer types
443     template<typename T>
444     struct Resolver : ResolverBase<T,false> {
445     static DataType resolve(const T& data) {
446     return ResolverBase<T,false>::resolve(data);
447     }
448     };
449    
450     // DataType resolver for pointer types (of 1st degree)
451     template<typename T>
452     struct Resolver<T*> : ResolverBase<T,true> {
453     static DataType resolve(const T*& data) {
454     return ResolverBase<T,true>::resolve(*data);
455     }
456     };
457    
458     template<typename T>
459     static String rawCppTypeNameOf(const T& data) {
460     #if defined _MSC_VER // Microsoft compiler ...
461 schoenebeck 3476 String name = typeid(data).raw_name();
462 schoenebeck 3138 #else // i.e. especially GCC and clang ...
463     String name = typeid(data).name();
464     #endif
465     //while (!name.empty() && name[0] >= 0 && name[0] <= 9)
466     // name = name.substr(1);
467     return name;
468     }
469    
470     private:
471     String m_baseTypeName;
472     String m_customTypeName;
473     int m_size;
474     bool m_isPointer;
475    
476 schoenebeck 3392 #if LIBGIG_SERIALIZATION_INTERNAL
477 schoenebeck 3138 friend DataType _popDataTypeBlob(const char*& p, const char* end);
478 schoenebeck 3392 #endif
479 schoenebeck 3150 friend class Archive;
480 schoenebeck 3138 };
481    
482     /** @brief Abstract reflection of a native C++ class/struct's member variable.
483     *
484     * Provides detailed information about a specific C++ member variable of
485     * serialized C++ object, like its C++ data type, offset of this member
486     * within its containing data structure/class, its C++ member variable name
487     * and more.
488 schoenebeck 3183 *
489     * Consider you defined the following user defined C/C++ @c struct type in
490     * your application:
491     * @code
492     * struct Foo {
493     * int a;
494     * bool b;
495     * double someValue;
496     * };
497     * @endcode
498     * Then @c a, @c b and @c someValue are "members" of @c struct @c Foo for
499     * instance. So that @c struct would have 3 members in the latter example.
500     *
501     * @see Object::members()
502 schoenebeck 3138 */
503     class Member {
504     public:
505     Member();
506 schoenebeck 3183 UID uid() const;
507     String name() const;
508     size_t offset() const;
509     const DataType& type() const;
510 schoenebeck 3138 bool isValid() const;
511 schoenebeck 3183 operator bool() const { return isValid(); } ///< Same as calling isValid().
512 schoenebeck 3138 //bool operator()() const { return isValid(); }
513     bool operator==(const Member& other) const;
514     bool operator!=(const Member& other) const;
515     bool operator<(const Member& other) const;
516     bool operator>(const Member& other) const;
517    
518     protected:
519     Member(String name, UID uid, size_t offset, DataType type);
520     friend class Archive;
521    
522     private:
523     UID m_uid;
524     size_t m_offset;
525     String m_name;
526     DataType m_type;
527    
528 schoenebeck 3392 #if LIBGIG_SERIALIZATION_INTERNAL
529 schoenebeck 3138 friend Member _popMemberBlob(const char*& p, const char* end);
530 schoenebeck 3392 #endif
531 schoenebeck 3138 };
532    
533 schoenebeck 3185 /** @brief Abstract reflection of some native serialized C/C++ data.
534 schoenebeck 3138 *
535 schoenebeck 3185 * When your native C++ objects are serialized, all native data is
536     * translated and reflected by such an Object reflection. So each instance
537     * of your serialized native C++ class objects become available as an
538     * Object, but also each member variable of your C++ objects is translated
539     * into an Object, and any other native C/C++ data. So essentially every
540     * native data is turned into its own Object and accessible by this API.
541 schoenebeck 3183 *
542 schoenebeck 3185 * For each one of those Object reflections, this class provides detailed
543     * information about their native origin. For example if an Object
544     * represents a native C++ class instante, then it provides access to its
545     * C++ class/struct name, to its C++ member variables, its native memory
546     * size and much more.
547     *
548 schoenebeck 3183 * Even though this framework allows you to adjust abstract Object instances
549     * to a certain extent, most of the methods of this Object class are
550     * read-only though and the actual modifyable methods are made available
551     * not as part of this Object class, but as part of the Archive class
552 schoenebeck 3185 * instead. This design decision was made for performance and safety
553     * reasons.
554 schoenebeck 3183 *
555     * @see Archive::setIntValue() as an example for modifying Object instances.
556 schoenebeck 3138 */
557     class Object {
558     public:
559     Object();
560     Object(UIDChain uidChain, DataType type);
561    
562 schoenebeck 3183 UID uid(int index = 0) const;
563     const UIDChain& uidChain() const;
564     const DataType& type() const;
565     const RawData& rawData() const;
566     Version version() const;
567     Version minVersion() const;
568 schoenebeck 3138 bool isVersionCompatibleTo(const Object& other) const;
569 schoenebeck 3183 std::vector<Member>& members();
570     const std::vector<Member>& members() const;
571 schoenebeck 3138 Member memberNamed(String name) const;
572 schoenebeck 3153 Member memberByUID(const UID& uid) const;
573 schoenebeck 3138 std::vector<Member> membersOfType(const DataType& type) const;
574     int sequenceIndexOf(const Member& member) const;
575     bool isValid() const;
576 schoenebeck 3183 operator bool() const { return isValid(); } ///< Same as calling isValid().
577 schoenebeck 3138 //bool operator()() const { return isValid(); }
578     bool operator==(const Object& other) const;
579     bool operator!=(const Object& other) const;
580     bool operator<(const Object& other) const;
581     bool operator>(const Object& other) const;
582    
583 schoenebeck 3153 protected:
584     void remove(const Member& member);
585 schoenebeck 3182 void setVersion(Version v);
586     void setMinVersion(Version v);
587 schoenebeck 3153
588 schoenebeck 3138 private:
589     DataType m_type;
590     UIDChain m_uid;
591     Version m_version;
592     Version m_minVersion;
593     RawData m_data;
594     std::vector<Member> m_members;
595    
596 schoenebeck 3392 #if LIBGIG_SERIALIZATION_INTERNAL
597 schoenebeck 3150 friend String _encodePrimitiveValue(const Object& obj);
598 schoenebeck 3138 friend Object _popObjectBlob(const char*& p, const char* end);
599     friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
600 schoenebeck 3150 friend String _primitiveObjectValueToString(const Object& obj);
601 schoenebeck 3392 // |
602 schoenebeck 3169 template<typename T>
603     friend T _primitiveObjectValueToNumber(const Object& obj);
604 schoenebeck 3392 #endif // LIBGIG_SERIALIZATION_INTERNAL
605 schoenebeck 3169
606 schoenebeck 3150 friend class Archive;
607 schoenebeck 3138 };
608    
609     /** @brief Destination container for serialization, and source container for deserialization.
610     *
611     * This is the main class for implementing serialization and deserialization
612     * with your C++ application. This framework does not require a a tree
613     * structured layout of your C++ objects being serialized/deserialized, it
614     * uses a concept of a "root" object though. So to start serialization
615     * construct an empty Archive object and then instruct it to serialize your
616     * C++ objects by pointing it to your "root" object:
617     * @code
618     * Archive a;
619     * a.serialize(&myRootObject);
620 schoenebeck 3142 * @endcode
621 schoenebeck 3138 * Or if you prefer the look of operator based code:
622     * @code
623     * Archive a;
624     * a << myRootObject;
625 schoenebeck 3142 * @endcode
626 schoenebeck 3138 * The Archive object will then serialize all members of the passed C++
627     * object, and will recursively serialize all other C++ objects which it
628     * contains or points to. So the root object is the starting point for the
629     * overall serialization. After the serialize() method returned, you can
630     * then access the serialized data stream by calling rawData() and send that
631     * data stream over "wire", or store it on disk or whatever you may intend
632     * to do with it.
633     *
634     * Then on receiver side likewise, you create a new Archive object, pass the
635     * received data stream i.e. via constructor to the Archive object and call
636     * deserialize() by pointing it to the root object on receiver side:
637     * @code
638     * Archive a(rawDataStream);
639     * a.deserialize(&myRootObject);
640 schoenebeck 3142 * @endcode
641 schoenebeck 3138 * Or with operator instead:
642     * @code
643     * Archive a(rawDataStream);
644     * a >> myRootObject;
645 schoenebeck 3142 * @endcode
646 schoenebeck 3138 * Now this framework automatically handles serialization and
647     * deserialization of fundamental data types automatically for you (like
648     * i.e. char, int, long int, float, double, etc.). However for your own
649     * custom C++ classes and structs you must implement one method which
650     * defines which members of your class should actually be serialized and
651     * deserialized. That method to be added must have the following signature:
652     * @code
653     * void serialize(Serialization::Archive* archive);
654     * @endcode
655     * So let's say you have the following simple data structures:
656     * @code
657     * struct Foo {
658     * int a;
659     * bool b;
660     * double c;
661     * };
662     *
663     * struct Bar {
664     * char one;
665     * float two;
666     * Foo foo1;
667     * Foo* pFoo2;
668     * Foo* pFoo3DontTouchMe; // shall not be serialized/deserialized
669     * };
670     * @endcode
671     * So in order to be able to serialize and deserialize objects of those two
672     * structures you would first add the mentioned method to each struct
673     * definition (i.e. in your header file):
674     * @code
675     * struct Foo {
676     * int a;
677     * bool b;
678     * double c;
679     *
680     * void serialize(Serialization::Archive* archive);
681     * };
682     *
683     * struct Bar {
684     * char one;
685     * float two;
686     * Foo foo1;
687     * Foo* pFoo2;
688     * Foo* pFoo3DontTouchMe; // shall not be serialized/deserialized
689     *
690     * void serialize(Serialization::Archive* archive);
691     * };
692     * @endcode
693     * And then you would implement those two new methods like this (i.e. in
694     * your .cpp file):
695     * @code
696     * #define SRLZ(member) \
697     * archive->serializeMember(*this, member, #member);
698     *
699     * void Foo::serialize(Serialization::Archive* archive) {
700     * SRLZ(a);
701     * SRLZ(b);
702     * SRLZ(c);
703     * }
704     *
705     * void Bar::serialize(Serialization::Archive* archive) {
706     * SRLZ(one);
707     * SRLZ(two);
708     * SRLZ(foo1);
709     * SRLZ(pFoo2);
710     * // leaving out pFoo3DontTouchMe here
711     * }
712     * @endcode
713     * Now when you serialize such a Bar object, this framework will also
714     * automatically serialize the respective Foo object(s) accordingly, also
715     * for the pFoo2 pointer for instance (as long as it is not a NULL pointer
716     * that is).
717     *
718     * Note that there is only one method that you need to implement. So the
719     * respective serialize() method implementation of your classes/structs are
720     * both called for serialization, as well as for deserialization!
721 schoenebeck 3182 *
722     * In case you need to enforce backward incompatiblity for one of your C++
723     * classes, you can do so by setting a version and minimum version for your
724     * class (see @c setVersion() and @c setMinVersion() for details).
725 schoenebeck 3138 */
726     class Archive {
727     public:
728 schoenebeck 3774 /** @brief Current activity of @c Archive object.
729     */
730     enum operation_t {
731     OPERATION_NONE, ///< Archive is currently neither serializing, nor deserializing.
732     OPERATION_SERIALIZE, ///< Archive is currently serializing.
733     OPERATION_DESERIALIZE ///< Archive is currently deserializing.
734     };
735    
736 schoenebeck 3138 Archive();
737     Archive(const RawData& data);
738     Archive(const uint8_t* data, size_t size);
739     virtual ~Archive();
740    
741 schoenebeck 3183 /** @brief Initiate serialization.
742     *
743     * Initiates serialization of all native C++ objects, which means
744     * capturing and storing the current data of all your C++ objects as
745     * content of this Archive.
746     *
747     * This framework has a concept of a "root" object which you must pass
748     * to this method. The root object is the starting point for
749     * serialization of your C++ objects. The framework will then
750     * recursively serialize all members of that C++ object an continue to
751     * serialize all other C++ objects that it might contain or point to.
752     *
753     * After this method returned, you might traverse all serialized objects
754     * by walking them starting from the rootObject(). You might then modify
755     * that abstract reflection of your C++ objects and finally you might
756     * call rawData() to get an encoded raw data stream which you might use
757     * for sending it "over wire" to somewhere where it is going to be
758     * deserialized later on.
759     *
760     * Note that whenever you call this method, the previous content of this
761     * Archive will first be cleared.
762     *
763     * @param obj - native C++ root object where serialization shall start
764     * @see Archive::operator<<()
765     */
766 schoenebeck 3138 template<typename T>
767     void serialize(const T* obj) {
768     m_operation = OPERATION_SERIALIZE;
769     m_allObjects.clear();
770     m_rawData.clear();
771     m_root = UID::from(obj);
772     const_cast<T*>(obj)->serialize(this);
773     encode();
774     m_operation = OPERATION_NONE;
775     }
776    
777 schoenebeck 3183 /** @brief Initiate deserialization.
778     *
779     * Initiates deserialization of all native C++ objects, which means all
780     * your C++ objects will be restored with the values contained in this
781     * Archive. So that also means calling deserialize() only makes sense if
782     * this a non-empty Archive, which i.e. is the case if you either called
783     * serialize() with this Archive object before or if you passed a
784     * previously serialized raw data stream to the constructor of this
785     * Archive object.
786     *
787     * This framework has a concept of a "root" object which you must pass
788     * to this method. The root object is the starting point for
789     * deserialization of your C++ objects. The framework will then
790     * recursively deserialize all members of that C++ object an continue to
791     * deserialize all other C++ objects that it might contain or point to,
792     * according to the values stored in this Archive.
793     *
794     * @param obj - native C++ root object where deserialization shall start
795     * @see Archive::operator>>()
796     *
797     * @throws Exception if the data stored in this Archive cannot be
798     * restored to the C++ objects passed to this method, i.e.
799     * because of version or type incompatibilities.
800     */
801 schoenebeck 3138 template<typename T>
802     void deserialize(T* obj) {
803     Archive a;
804 schoenebeck 3774 a.m_operation = m_operation = OPERATION_DESERIALIZE;
805 schoenebeck 3138 obj->serialize(&a);
806     a.m_root = UID::from(obj);
807     Syncer s(a, *this);
808 schoenebeck 3774 a.m_operation = m_operation = OPERATION_NONE;
809 schoenebeck 3138 }
810    
811 schoenebeck 3183 /** @brief Initiate serialization of your C++ objects.
812     *
813     * Same as calling @c serialize(), this is just meant if you prefer
814     * to use operator based code instead, which you might find to be more
815     * intuitive.
816     *
817     * Example:
818     * @code
819     * Archive a;
820     * a << myRootObject;
821     * @endcode
822     *
823     * @see Archive::serialize() for more details.
824     */
825 schoenebeck 3138 template<typename T>
826     void operator<<(const T& obj) {
827     serialize(&obj);
828     }
829    
830 schoenebeck 3183 /** @brief Initiate deserialization of your C++ objects.
831     *
832     * Same as calling @c deserialize(), this is just meant if you prefer
833     * to use operator based code instead, which you might find to be more
834     * intuitive.
835     *
836     * Example:
837     * @code
838     * Archive a(rawDataStream);
839     * a >> myRootObject;
840     * @endcode
841     *
842     * @throws Exception if the data stored in this Archive cannot be
843     * restored to the C++ objects passed to this method, i.e.
844     * because of version or type incompatibilities.
845     *
846     * @see Archive::deserialize() for more details.
847     */
848 schoenebeck 3138 template<typename T>
849     void operator>>(T& obj) {
850     deserialize(&obj);
851     }
852    
853 schoenebeck 3150 const RawData& rawData();
854 schoenebeck 3138 virtual String rawDataFormat() const;
855    
856 schoenebeck 3183 /** @brief Serialize a native C/C++ member variable.
857     *
858     * This method is usually called by the serialize() method
859     * implementation of your C/C++ structs and classes, for each of the
860     * member variables that shall be serialized and deserialized
861     * automatically with this framework. It is recommend that you are not
862     * using this method name directly, but rather define a short hand C
863     * macro in your .cpp file like:
864     * @code
865     * #define SRLZ(member) \
866     * archive->serializeMember(*this, member, #member);
867     *
868     * void Foo::serialize(Serialization::Archive* archive) {
869     * SRLZ(a);
870     * SRLZ(b);
871     * SRLZ(c);
872     * }
873     * @endcode
874     * As you can see, using such a macro makes your code more readable and
875     * less error prone.
876     *
877     * It is completely up to you to decide which ones of your member
878     * variables shall automatically be serialized and deserialized with
879     * this framework. Only those member variables which are registered by
880     * calling this method will be serialized and deserialized. It does not
881     * really matter in which order you register your individiual member
882     * variables by calling this method, but the sequence is actually stored
883     * as meta information with the resulting archive and the resulting raw
884     * data stream. That meta information might then be used by this
885     * framework to automatically correct and adapt deserializing that
886     * archive later on for a future (or older) and potentially heavily
887     * modified version of your software. So it is recommended, even though
888     * also not required, that you may retain the sequence of your
889     * serializeMember() calls for your individual C++ classes' members over
890     * all your software versions, to retain backward compatibility of older
891     * archives as much as possible.
892     *
893     * @param nativeObject - native C++ object to be registered for
894     * serialization / deserialization
895     * @param nativeMember - native C++ member variable of @a nativeObject
896     * to be registered for serialization /
897     * deserialization
898     * @param memberName - name of @a nativeMember to be stored with this
899     * archive
900     */
901 schoenebeck 3138 template<typename T_classType, typename T_memberType>
902     void serializeMember(const T_classType& nativeObject, const T_memberType& nativeMember, const char* memberName) {
903     const size_t offset =
904 schoenebeck 3182 ((const uint8_t*)(const void*)&nativeMember) -
905     ((const uint8_t*)(const void*)&nativeObject);
906 schoenebeck 3138 const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);
907     const DataType type = DataType::dataTypeOf(nativeMember);
908     const Member member(memberName, uids[0], offset, type);
909     const UID parentUID = UID::from(nativeObject);
910     Object& parent = m_allObjects[parentUID];
911     if (!parent) {
912     const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
913     const DataType type = DataType::dataTypeOf(nativeObject);
914     parent = Object(uids, type);
915     }
916     parent.members().push_back(member);
917     const Object obj(uids, type);
918     const bool bExistsAlready = m_allObjects.count(uids[0]);
919     const bool isValidObject = obj;
920     const bool bExistingObjectIsInvalid = !m_allObjects[uids[0]];
921     if (!bExistsAlready || (bExistingObjectIsInvalid && isValidObject)) {
922     m_allObjects[uids[0]] = obj;
923     // recurse serialization for all members of this member
924     // (only for struct/class types, noop for primitive types)
925     SerializationRecursion<T_memberType>::serializeObject(this, nativeMember);
926     }
927     }
928    
929 schoenebeck 3183 /** @brief Set current version number for your C++ class.
930 schoenebeck 3182 *
931 schoenebeck 3183 * By calling this method you can define a version number for your
932 schoenebeck 3182 * current C++ class (that is a version for its current data structure
933 schoenebeck 3183 * layout and method implementations) that is going to be stored along
934     * with the serialized archive. Only call this method if you really want
935     * to constrain compatibility of your C++ class.
936 schoenebeck 3182 *
937     * Along with calling @c setMinVersion() this provides a way for you
938 schoenebeck 3183 * to constrain backward compatibility regarding serialization and
939     * deserialization of your C++ class which the Archive class will obey
940     * to. If required, then typically you might do so in your
941     * @c serialize() method implementation like:
942 schoenebeck 3182 * @code
943     * #define SRLZ(member) \
944     * archive->serializeMember(*this, member, #member);
945     *
946     * void Foo::serialize(Serialization::Archive* archive) {
947     * // when serializing: the current version of this class that is
948     * // going to be stored with the serialized archive
949     * archive->setVersion(*this, 6);
950 schoenebeck 3183 * // when deserializing: the minimum version this C++ class is
951     * // compatible with
952 schoenebeck 3182 * archive->setMinVersion(*this, 3);
953     * // actual data mebers to serialize / deserialize
954     * SRLZ(a);
955     * SRLZ(b);
956     * SRLZ(c);
957     * }
958     * @endcode
959 schoenebeck 3183 * In this example above, the C++ class "Foo" would be serialized along
960     * with the version number @c 6 and minimum version @c 3 as additional
961     * meta information in the resulting archive (and its raw data stream
962     * respectively).
963 schoenebeck 3182 *
964     * When deserializing archives with the example C++ class code above,
965     * the Archive object would check whether your originally serialized
966     * C++ "Foo" object had at least version number @c 3, if not the
967     * deserialization process would automatically be stopped with a
968     * @c Serialization::Exception, claiming that the classes are version
969     * incompatible.
970     *
971 schoenebeck 3183 * But also consider the other way around: you might have serialized
972     * your latest version of your C++ class, and might deserialize that
973     * archive with an older version of your C++ class. In that case it will
974     * likewise be checked whether the version of that old C++ class is at
975     * least as high as the minimum version set with the already seralized
976     * bleeding edge C++ class.
977     *
978 schoenebeck 3182 * Since this Serialization / deserialization framework is designed to
979     * be robust on changes to your C++ classes and aims trying to
980     * deserialize all your C++ objects correctly even if your C++ classes
981     * have seen substantial software changes in the meantime; you might
982 schoenebeck 3183 * sometimes see it as necessary to constrain backward compatibility
983     * this way. Because obviously there are certain things this framework
984     * can cope with, like for example that you renamed a data member while
985     * keeping the layout consistent, or that you have added new members to
986     * your C++ class or simply changed the order of your members in your
987     * C++ class. But what this framework cannot detect is for example if
988     * you changed the semantics of the values stored with your members, or
989     * even substantially changed the algorithms in your class methods such
990     * that they would not handle the data of your C++ members in the same
991     * and correct way anymore.
992 schoenebeck 3182 *
993     * @param nativeObject - your C++ object you want to set a version for
994     * @param v - the version number to set for your C++ class (by default,
995     * that is if you do not explicitly call this method, then
996     * your C++ object will be stored with version number @c 0 ).
997     */
998     template<typename T_classType>
999     void setVersion(const T_classType& nativeObject, Version v) {
1000     const UID uid = UID::from(nativeObject);
1001     Object& obj = m_allObjects[uid];
1002     if (!obj) {
1003     const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
1004     const DataType type = DataType::dataTypeOf(nativeObject);
1005     obj = Object(uids, type);
1006     }
1007     setVersion(obj, v);
1008     }
1009    
1010     /** @brief Set a minimum version number for your C++ class.
1011     *
1012     * Call this method to define a minimum version that your current C++
1013     * class implementation would be compatible with when it comes to
1014 schoenebeck 3183 * deserialization of an archive containing an object of your C++ class.
1015     * Like the version information, the minimum version will also be stored
1016     * for objects of your C++ class with the resulting archive (and its
1017     * resulting raw data stream respectively).
1018 schoenebeck 3182 *
1019 schoenebeck 3183 * When you start to constrain version compatibility of your C++ class
1020     * you usually start by using 1 as version and 1 as minimum version.
1021     * So it is eligible to set the same number to both version and minimum
1022     * version. However you must @b not set a minimum version higher than
1023     * version. Doing so would not raise an exception, but the resulting
1024     * behavior would be undefined.
1025     *
1026     * It is not relevant whether you first set version and then minimum
1027     * version or vice versa. It is also not relevant when exactly you set
1028     * those two numbers, even though usually you would set both in your
1029     * serialize() method implementation.
1030     *
1031 schoenebeck 3182 * @see @c setVersion() for more details about this overall topic.
1032 schoenebeck 3183 *
1033     * @param nativeObject - your C++ object you want to set a version for
1034     * @param v - the minimum version you want to define for your C++ class
1035     * (by default, that is if you do not explicitly call this
1036     * method, then a minium version of @c 0 is assumed for your
1037     * C++ class instead).
1038 schoenebeck 3182 */
1039     template<typename T_classType>
1040     void setMinVersion(const T_classType& nativeObject, Version v) {
1041     const UID uid = UID::from(nativeObject);
1042     Object& obj = m_allObjects[uid];
1043     if (!obj) {
1044     const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
1045     const DataType type = DataType::dataTypeOf(nativeObject);
1046     obj = Object(uids, type);
1047     }
1048     setMinVersion(obj, v);
1049     }
1050    
1051 schoenebeck 3138 virtual void decode(const RawData& data);
1052     virtual void decode(const uint8_t* data, size_t size);
1053     void clear();
1054 schoenebeck 3150 bool isModified() const;
1055 schoenebeck 3153 void removeMember(Object& parent, const Member& member);
1056 schoenebeck 3138 void remove(const Object& obj);
1057     Object& rootObject();
1058     Object& objectByUID(const UID& uid);
1059 schoenebeck 3150 void setAutoValue(Object& object, String value);
1060     void setIntValue(Object& object, int64_t value);
1061     void setRealValue(Object& object, double value);
1062     void setBoolValue(Object& object, bool value);
1063     void setEnumValue(Object& object, uint64_t value);
1064 schoenebeck 3771 void setStringValue(Object& object, String value);
1065 schoenebeck 3150 String valueAsString(const Object& object);
1066 schoenebeck 3169 int64_t valueAsInt(const Object& object);
1067     double valueAsReal(const Object& object);
1068     bool valueAsBool(const Object& object);
1069 schoenebeck 3182 void setVersion(Object& object, Version v);
1070     void setMinVersion(Object& object, Version v);
1071 schoenebeck 3156 String name() const;
1072     void setName(String name);
1073     String comment() const;
1074     void setComment(String comment);
1075     time_t timeStampCreated() const;
1076     time_t timeStampModified() const;
1077     tm dateTimeCreated(time_base_t base = LOCAL_TIME) const;
1078     tm dateTimeModified(time_base_t base = LOCAL_TIME) const;
1079 schoenebeck 3774 operation_t operation() const;
1080 schoenebeck 3138
1081     protected:
1082     // UID resolver for non-pointer types
1083     template<typename T>
1084     class UIDChainResolver {
1085     public:
1086     UIDChainResolver(const T& data) {
1087     m_uid.push_back(UID::from(data));
1088     }
1089    
1090     operator UIDChain() const { return m_uid; }
1091     UIDChain operator()() const { return m_uid; }
1092     private:
1093     UIDChain m_uid;
1094     };
1095    
1096     // UID resolver for pointer types (of 1st degree)
1097     template<typename T>
1098     class UIDChainResolver<T*> {
1099     public:
1100     UIDChainResolver(const T*& data) {
1101 schoenebeck 3168 const UID uids[2] = {
1102     { &data, sizeof(data) },
1103     { data, sizeof(*data) }
1104     };
1105     m_uid.push_back(uids[0]);
1106     m_uid.push_back(uids[1]);
1107 schoenebeck 3138 }
1108    
1109     operator UIDChain() const { return m_uid; }
1110     UIDChain operator()() const { return m_uid; }
1111     private:
1112     UIDChain m_uid;
1113     };
1114    
1115     // SerializationRecursion for non-pointer class/struct types.
1116     template<typename T, bool T_isRecursive>
1117     struct SerializationRecursionImpl {
1118     static void serializeObject(Archive* archive, const T& obj) {
1119     const_cast<T&>(obj).serialize(archive);
1120     }
1121     };
1122    
1123     // SerializationRecursion for pointers (of 1st degree) to class/structs.
1124     template<typename T, bool T_isRecursive>
1125     struct SerializationRecursionImpl<T*,T_isRecursive> {
1126     static void serializeObject(Archive* archive, const T*& obj) {
1127     if (!obj) return;
1128     const_cast<T*&>(obj)->serialize(archive);
1129     }
1130     };
1131    
1132     // NOOP SerializationRecursion for primitive types.
1133     template<typename T>
1134     struct SerializationRecursionImpl<T,false> {
1135     static void serializeObject(Archive* archive, const T& obj) {}
1136     };
1137    
1138     // NOOP SerializationRecursion for pointers (of 1st degree) to primitive types.
1139     template<typename T>
1140     struct SerializationRecursionImpl<T*,false> {
1141     static void serializeObject(Archive* archive, const T*& obj) {}
1142     };
1143    
1144 schoenebeck 3771 // NOOP SerializationRecursion for String objects.
1145 schoenebeck 3772 template<bool T_isRecursive>
1146     struct SerializationRecursionImpl<String,T_isRecursive> {
1147 schoenebeck 3771 static void serializeObject(Archive* archive, const String& obj) {}
1148     };
1149    
1150     // NOOP SerializationRecursion for String pointers (of 1st degree).
1151 schoenebeck 3772 template<bool T_isRecursive>
1152     struct SerializationRecursionImpl<String*,T_isRecursive> {
1153 schoenebeck 3771 static void serializeObject(Archive* archive, const String*& obj) {}
1154     };
1155    
1156 schoenebeck 3138 // Automatically handles recursion for class/struct types, while ignoring all primitive types.
1157     template<typename T>
1158 schoenebeck 3167 struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> {
1159 schoenebeck 3138 };
1160    
1161     class ObjectPool : public std::map<UID,Object> {
1162     public:
1163     // prevent passing obvious invalid UID values from creating a new pair entry
1164     Object& operator[](const UID& k) {
1165     static Object invalid;
1166     if (!k.isValid()) {
1167     invalid = Object();
1168     return invalid;
1169     }
1170     return std::map<UID,Object>::operator[](k);
1171     }
1172     };
1173    
1174     friend String _encode(const ObjectPool& objects);
1175    
1176     private:
1177     String _encodeRootBlob();
1178     void _popRootBlob(const char*& p, const char* end);
1179     void _popObjectsBlob(const char*& p, const char* end);
1180    
1181     protected:
1182     class Syncer {
1183     public:
1184     Syncer(Archive& dst, Archive& src);
1185     protected:
1186     void syncObject(const Object& dst, const Object& src);
1187     void syncPrimitive(const Object& dst, const Object& src);
1188 schoenebeck 3771 void syncString(const Object& dst, const Object& src);
1189 schoenebeck 3138 void syncPointer(const Object& dst, const Object& src);
1190     void syncMember(const Member& dstMember, const Member& srcMember);
1191     static Member dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember);
1192     private:
1193     Archive& m_dst;
1194     Archive& m_src;
1195     };
1196    
1197     virtual void encode();
1198    
1199     ObjectPool m_allObjects;
1200     operation_t m_operation;
1201     UID m_root;
1202     RawData m_rawData;
1203 schoenebeck 3150 bool m_isModified;
1204 schoenebeck 3156 String m_name;
1205     String m_comment;
1206     time_t m_timeCreated;
1207     time_t m_timeModified;
1208 schoenebeck 3138 };
1209    
1210     /**
1211     * Will be thrown whenever an error occurs during an serialization or
1212     * deserialization process.
1213     */
1214     class Exception {
1215     public:
1216     String Message;
1217    
1218 schoenebeck 3198 Exception(String format, ...);
1219     Exception(String format, va_list arg);
1220 schoenebeck 3138 void PrintMessage();
1221     virtual ~Exception() {}
1222 schoenebeck 3198
1223     protected:
1224     Exception();
1225     static String assemble(String format, va_list arg);
1226 schoenebeck 3138 };
1227    
1228     } // namespace Serialization
1229    
1230     #endif // LIBGIG_SERIALIZATION_H

  ViewVC Help
Powered by ViewVC