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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3773 - (hide annotations) (download) (as text)
Tue May 19 14:28:20 2020 UTC (3 years, 11 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 53584 byte(s)
* Serialization.h: Fixed assertion fault on some systems if a member
  variable was serialised which was declared as size_t or ssize_t
  data type.

* Bumped version (4.2.0.svn11).

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     Archive();
729     Archive(const RawData& data);
730     Archive(const uint8_t* data, size_t size);
731     virtual ~Archive();
732    
733 schoenebeck 3183 /** @brief Initiate serialization.
734     *
735     * Initiates serialization of all native C++ objects, which means
736     * capturing and storing the current data of all your C++ objects as
737     * content of this Archive.
738     *
739     * This framework has a concept of a "root" object which you must pass
740     * to this method. The root object is the starting point for
741     * serialization of your C++ objects. The framework will then
742     * recursively serialize all members of that C++ object an continue to
743     * serialize all other C++ objects that it might contain or point to.
744     *
745     * After this method returned, you might traverse all serialized objects
746     * by walking them starting from the rootObject(). You might then modify
747     * that abstract reflection of your C++ objects and finally you might
748     * call rawData() to get an encoded raw data stream which you might use
749     * for sending it "over wire" to somewhere where it is going to be
750     * deserialized later on.
751     *
752     * Note that whenever you call this method, the previous content of this
753     * Archive will first be cleared.
754     *
755     * @param obj - native C++ root object where serialization shall start
756     * @see Archive::operator<<()
757     */
758 schoenebeck 3138 template<typename T>
759     void serialize(const T* obj) {
760     m_operation = OPERATION_SERIALIZE;
761     m_allObjects.clear();
762     m_rawData.clear();
763     m_root = UID::from(obj);
764     const_cast<T*>(obj)->serialize(this);
765     encode();
766     m_operation = OPERATION_NONE;
767     }
768    
769 schoenebeck 3183 /** @brief Initiate deserialization.
770     *
771     * Initiates deserialization of all native C++ objects, which means all
772     * your C++ objects will be restored with the values contained in this
773     * Archive. So that also means calling deserialize() only makes sense if
774     * this a non-empty Archive, which i.e. is the case if you either called
775     * serialize() with this Archive object before or if you passed a
776     * previously serialized raw data stream to the constructor of this
777     * Archive object.
778     *
779     * This framework has a concept of a "root" object which you must pass
780     * to this method. The root object is the starting point for
781     * deserialization of your C++ objects. The framework will then
782     * recursively deserialize all members of that C++ object an continue to
783     * deserialize all other C++ objects that it might contain or point to,
784     * according to the values stored in this Archive.
785     *
786     * @param obj - native C++ root object where deserialization shall start
787     * @see Archive::operator>>()
788     *
789     * @throws Exception if the data stored in this Archive cannot be
790     * restored to the C++ objects passed to this method, i.e.
791     * because of version or type incompatibilities.
792     */
793 schoenebeck 3138 template<typename T>
794     void deserialize(T* obj) {
795     Archive a;
796     m_operation = OPERATION_DESERIALIZE;
797     obj->serialize(&a);
798     a.m_root = UID::from(obj);
799     Syncer s(a, *this);
800     m_operation = OPERATION_NONE;
801     }
802    
803 schoenebeck 3183 /** @brief Initiate serialization of your C++ objects.
804     *
805     * Same as calling @c serialize(), this is just meant if you prefer
806     * to use operator based code instead, which you might find to be more
807     * intuitive.
808     *
809     * Example:
810     * @code
811     * Archive a;
812     * a << myRootObject;
813     * @endcode
814     *
815     * @see Archive::serialize() for more details.
816     */
817 schoenebeck 3138 template<typename T>
818     void operator<<(const T& obj) {
819     serialize(&obj);
820     }
821    
822 schoenebeck 3183 /** @brief Initiate deserialization of your C++ objects.
823     *
824     * Same as calling @c deserialize(), this is just meant if you prefer
825     * to use operator based code instead, which you might find to be more
826     * intuitive.
827     *
828     * Example:
829     * @code
830     * Archive a(rawDataStream);
831     * a >> myRootObject;
832     * @endcode
833     *
834     * @throws Exception if the data stored in this Archive cannot be
835     * restored to the C++ objects passed to this method, i.e.
836     * because of version or type incompatibilities.
837     *
838     * @see Archive::deserialize() for more details.
839     */
840 schoenebeck 3138 template<typename T>
841     void operator>>(T& obj) {
842     deserialize(&obj);
843     }
844    
845 schoenebeck 3150 const RawData& rawData();
846 schoenebeck 3138 virtual String rawDataFormat() const;
847    
848 schoenebeck 3183 /** @brief Serialize a native C/C++ member variable.
849     *
850     * This method is usually called by the serialize() method
851     * implementation of your C/C++ structs and classes, for each of the
852     * member variables that shall be serialized and deserialized
853     * automatically with this framework. It is recommend that you are not
854     * using this method name directly, but rather define a short hand C
855     * macro in your .cpp file like:
856     * @code
857     * #define SRLZ(member) \
858     * archive->serializeMember(*this, member, #member);
859     *
860     * void Foo::serialize(Serialization::Archive* archive) {
861     * SRLZ(a);
862     * SRLZ(b);
863     * SRLZ(c);
864     * }
865     * @endcode
866     * As you can see, using such a macro makes your code more readable and
867     * less error prone.
868     *
869     * It is completely up to you to decide which ones of your member
870     * variables shall automatically be serialized and deserialized with
871     * this framework. Only those member variables which are registered by
872     * calling this method will be serialized and deserialized. It does not
873     * really matter in which order you register your individiual member
874     * variables by calling this method, but the sequence is actually stored
875     * as meta information with the resulting archive and the resulting raw
876     * data stream. That meta information might then be used by this
877     * framework to automatically correct and adapt deserializing that
878     * archive later on for a future (or older) and potentially heavily
879     * modified version of your software. So it is recommended, even though
880     * also not required, that you may retain the sequence of your
881     * serializeMember() calls for your individual C++ classes' members over
882     * all your software versions, to retain backward compatibility of older
883     * archives as much as possible.
884     *
885     * @param nativeObject - native C++ object to be registered for
886     * serialization / deserialization
887     * @param nativeMember - native C++ member variable of @a nativeObject
888     * to be registered for serialization /
889     * deserialization
890     * @param memberName - name of @a nativeMember to be stored with this
891     * archive
892     */
893 schoenebeck 3138 template<typename T_classType, typename T_memberType>
894     void serializeMember(const T_classType& nativeObject, const T_memberType& nativeMember, const char* memberName) {
895     const size_t offset =
896 schoenebeck 3182 ((const uint8_t*)(const void*)&nativeMember) -
897     ((const uint8_t*)(const void*)&nativeObject);
898 schoenebeck 3138 const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);
899     const DataType type = DataType::dataTypeOf(nativeMember);
900     const Member member(memberName, uids[0], offset, type);
901     const UID parentUID = UID::from(nativeObject);
902     Object& parent = m_allObjects[parentUID];
903     if (!parent) {
904     const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
905     const DataType type = DataType::dataTypeOf(nativeObject);
906     parent = Object(uids, type);
907     }
908     parent.members().push_back(member);
909     const Object obj(uids, type);
910     const bool bExistsAlready = m_allObjects.count(uids[0]);
911     const bool isValidObject = obj;
912     const bool bExistingObjectIsInvalid = !m_allObjects[uids[0]];
913     if (!bExistsAlready || (bExistingObjectIsInvalid && isValidObject)) {
914     m_allObjects[uids[0]] = obj;
915     // recurse serialization for all members of this member
916     // (only for struct/class types, noop for primitive types)
917     SerializationRecursion<T_memberType>::serializeObject(this, nativeMember);
918     }
919     }
920    
921 schoenebeck 3183 /** @brief Set current version number for your C++ class.
922 schoenebeck 3182 *
923 schoenebeck 3183 * By calling this method you can define a version number for your
924 schoenebeck 3182 * current C++ class (that is a version for its current data structure
925 schoenebeck 3183 * layout and method implementations) that is going to be stored along
926     * with the serialized archive. Only call this method if you really want
927     * to constrain compatibility of your C++ class.
928 schoenebeck 3182 *
929     * Along with calling @c setMinVersion() this provides a way for you
930 schoenebeck 3183 * to constrain backward compatibility regarding serialization and
931     * deserialization of your C++ class which the Archive class will obey
932     * to. If required, then typically you might do so in your
933     * @c serialize() method implementation like:
934 schoenebeck 3182 * @code
935     * #define SRLZ(member) \
936     * archive->serializeMember(*this, member, #member);
937     *
938     * void Foo::serialize(Serialization::Archive* archive) {
939     * // when serializing: the current version of this class that is
940     * // going to be stored with the serialized archive
941     * archive->setVersion(*this, 6);
942 schoenebeck 3183 * // when deserializing: the minimum version this C++ class is
943     * // compatible with
944 schoenebeck 3182 * archive->setMinVersion(*this, 3);
945     * // actual data mebers to serialize / deserialize
946     * SRLZ(a);
947     * SRLZ(b);
948     * SRLZ(c);
949     * }
950     * @endcode
951 schoenebeck 3183 * In this example above, the C++ class "Foo" would be serialized along
952     * with the version number @c 6 and minimum version @c 3 as additional
953     * meta information in the resulting archive (and its raw data stream
954     * respectively).
955 schoenebeck 3182 *
956     * When deserializing archives with the example C++ class code above,
957     * the Archive object would check whether your originally serialized
958     * C++ "Foo" object had at least version number @c 3, if not the
959     * deserialization process would automatically be stopped with a
960     * @c Serialization::Exception, claiming that the classes are version
961     * incompatible.
962     *
963 schoenebeck 3183 * But also consider the other way around: you might have serialized
964     * your latest version of your C++ class, and might deserialize that
965     * archive with an older version of your C++ class. In that case it will
966     * likewise be checked whether the version of that old C++ class is at
967     * least as high as the minimum version set with the already seralized
968     * bleeding edge C++ class.
969     *
970 schoenebeck 3182 * Since this Serialization / deserialization framework is designed to
971     * be robust on changes to your C++ classes and aims trying to
972     * deserialize all your C++ objects correctly even if your C++ classes
973     * have seen substantial software changes in the meantime; you might
974 schoenebeck 3183 * sometimes see it as necessary to constrain backward compatibility
975     * this way. Because obviously there are certain things this framework
976     * can cope with, like for example that you renamed a data member while
977     * keeping the layout consistent, or that you have added new members to
978     * your C++ class or simply changed the order of your members in your
979     * C++ class. But what this framework cannot detect is for example if
980     * you changed the semantics of the values stored with your members, or
981     * even substantially changed the algorithms in your class methods such
982     * that they would not handle the data of your C++ members in the same
983     * and correct way anymore.
984 schoenebeck 3182 *
985     * @param nativeObject - your C++ object you want to set a version for
986     * @param v - the version number to set for your C++ class (by default,
987     * that is if you do not explicitly call this method, then
988     * your C++ object will be stored with version number @c 0 ).
989     */
990     template<typename T_classType>
991     void setVersion(const T_classType& nativeObject, Version v) {
992     const UID uid = UID::from(nativeObject);
993     Object& obj = m_allObjects[uid];
994     if (!obj) {
995     const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
996     const DataType type = DataType::dataTypeOf(nativeObject);
997     obj = Object(uids, type);
998     }
999     setVersion(obj, v);
1000     }
1001    
1002     /** @brief Set a minimum version number for your C++ class.
1003     *
1004     * Call this method to define a minimum version that your current C++
1005     * class implementation would be compatible with when it comes to
1006 schoenebeck 3183 * deserialization of an archive containing an object of your C++ class.
1007     * Like the version information, the minimum version will also be stored
1008     * for objects of your C++ class with the resulting archive (and its
1009     * resulting raw data stream respectively).
1010 schoenebeck 3182 *
1011 schoenebeck 3183 * When you start to constrain version compatibility of your C++ class
1012     * you usually start by using 1 as version and 1 as minimum version.
1013     * So it is eligible to set the same number to both version and minimum
1014     * version. However you must @b not set a minimum version higher than
1015     * version. Doing so would not raise an exception, but the resulting
1016     * behavior would be undefined.
1017     *
1018     * It is not relevant whether you first set version and then minimum
1019     * version or vice versa. It is also not relevant when exactly you set
1020     * those two numbers, even though usually you would set both in your
1021     * serialize() method implementation.
1022     *
1023 schoenebeck 3182 * @see @c setVersion() for more details about this overall topic.
1024 schoenebeck 3183 *
1025     * @param nativeObject - your C++ object you want to set a version for
1026     * @param v - the minimum version you want to define for your C++ class
1027     * (by default, that is if you do not explicitly call this
1028     * method, then a minium version of @c 0 is assumed for your
1029     * C++ class instead).
1030 schoenebeck 3182 */
1031     template<typename T_classType>
1032     void setMinVersion(const T_classType& nativeObject, Version v) {
1033     const UID uid = UID::from(nativeObject);
1034     Object& obj = m_allObjects[uid];
1035     if (!obj) {
1036     const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
1037     const DataType type = DataType::dataTypeOf(nativeObject);
1038     obj = Object(uids, type);
1039     }
1040     setMinVersion(obj, v);
1041     }
1042    
1043 schoenebeck 3138 virtual void decode(const RawData& data);
1044     virtual void decode(const uint8_t* data, size_t size);
1045     void clear();
1046 schoenebeck 3150 bool isModified() const;
1047 schoenebeck 3153 void removeMember(Object& parent, const Member& member);
1048 schoenebeck 3138 void remove(const Object& obj);
1049     Object& rootObject();
1050     Object& objectByUID(const UID& uid);
1051 schoenebeck 3150 void setAutoValue(Object& object, String value);
1052     void setIntValue(Object& object, int64_t value);
1053     void setRealValue(Object& object, double value);
1054     void setBoolValue(Object& object, bool value);
1055     void setEnumValue(Object& object, uint64_t value);
1056 schoenebeck 3771 void setStringValue(Object& object, String value);
1057 schoenebeck 3150 String valueAsString(const Object& object);
1058 schoenebeck 3169 int64_t valueAsInt(const Object& object);
1059     double valueAsReal(const Object& object);
1060     bool valueAsBool(const Object& object);
1061 schoenebeck 3182 void setVersion(Object& object, Version v);
1062     void setMinVersion(Object& object, Version v);
1063 schoenebeck 3156 String name() const;
1064     void setName(String name);
1065     String comment() const;
1066     void setComment(String comment);
1067     time_t timeStampCreated() const;
1068     time_t timeStampModified() const;
1069     tm dateTimeCreated(time_base_t base = LOCAL_TIME) const;
1070     tm dateTimeModified(time_base_t base = LOCAL_TIME) const;
1071 schoenebeck 3138
1072     protected:
1073     // UID resolver for non-pointer types
1074     template<typename T>
1075     class UIDChainResolver {
1076     public:
1077     UIDChainResolver(const T& data) {
1078     m_uid.push_back(UID::from(data));
1079     }
1080    
1081     operator UIDChain() const { return m_uid; }
1082     UIDChain operator()() const { return m_uid; }
1083     private:
1084     UIDChain m_uid;
1085     };
1086    
1087     // UID resolver for pointer types (of 1st degree)
1088     template<typename T>
1089     class UIDChainResolver<T*> {
1090     public:
1091     UIDChainResolver(const T*& data) {
1092 schoenebeck 3168 const UID uids[2] = {
1093     { &data, sizeof(data) },
1094     { data, sizeof(*data) }
1095     };
1096     m_uid.push_back(uids[0]);
1097     m_uid.push_back(uids[1]);
1098 schoenebeck 3138 }
1099    
1100     operator UIDChain() const { return m_uid; }
1101     UIDChain operator()() const { return m_uid; }
1102     private:
1103     UIDChain m_uid;
1104     };
1105    
1106     // SerializationRecursion for non-pointer class/struct types.
1107     template<typename T, bool T_isRecursive>
1108     struct SerializationRecursionImpl {
1109     static void serializeObject(Archive* archive, const T& obj) {
1110     const_cast<T&>(obj).serialize(archive);
1111     }
1112     };
1113    
1114     // SerializationRecursion for pointers (of 1st degree) to class/structs.
1115     template<typename T, bool T_isRecursive>
1116     struct SerializationRecursionImpl<T*,T_isRecursive> {
1117     static void serializeObject(Archive* archive, const T*& obj) {
1118     if (!obj) return;
1119     const_cast<T*&>(obj)->serialize(archive);
1120     }
1121     };
1122    
1123     // NOOP SerializationRecursion for primitive types.
1124     template<typename T>
1125     struct SerializationRecursionImpl<T,false> {
1126     static void serializeObject(Archive* archive, const T& obj) {}
1127     };
1128    
1129     // NOOP SerializationRecursion for pointers (of 1st degree) to primitive types.
1130     template<typename T>
1131     struct SerializationRecursionImpl<T*,false> {
1132     static void serializeObject(Archive* archive, const T*& obj) {}
1133     };
1134    
1135 schoenebeck 3771 // NOOP SerializationRecursion for String objects.
1136 schoenebeck 3772 template<bool T_isRecursive>
1137     struct SerializationRecursionImpl<String,T_isRecursive> {
1138 schoenebeck 3771 static void serializeObject(Archive* archive, const String& obj) {}
1139     };
1140    
1141     // NOOP SerializationRecursion for String pointers (of 1st degree).
1142 schoenebeck 3772 template<bool T_isRecursive>
1143     struct SerializationRecursionImpl<String*,T_isRecursive> {
1144 schoenebeck 3771 static void serializeObject(Archive* archive, const String*& obj) {}
1145     };
1146    
1147 schoenebeck 3138 // Automatically handles recursion for class/struct types, while ignoring all primitive types.
1148     template<typename T>
1149 schoenebeck 3167 struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> {
1150 schoenebeck 3138 };
1151    
1152     class ObjectPool : public std::map<UID,Object> {
1153     public:
1154     // prevent passing obvious invalid UID values from creating a new pair entry
1155     Object& operator[](const UID& k) {
1156     static Object invalid;
1157     if (!k.isValid()) {
1158     invalid = Object();
1159     return invalid;
1160     }
1161     return std::map<UID,Object>::operator[](k);
1162     }
1163     };
1164    
1165     friend String _encode(const ObjectPool& objects);
1166    
1167     private:
1168     String _encodeRootBlob();
1169     void _popRootBlob(const char*& p, const char* end);
1170     void _popObjectsBlob(const char*& p, const char* end);
1171    
1172     protected:
1173     class Syncer {
1174     public:
1175     Syncer(Archive& dst, Archive& src);
1176     protected:
1177     void syncObject(const Object& dst, const Object& src);
1178     void syncPrimitive(const Object& dst, const Object& src);
1179 schoenebeck 3771 void syncString(const Object& dst, const Object& src);
1180 schoenebeck 3138 void syncPointer(const Object& dst, const Object& src);
1181     void syncMember(const Member& dstMember, const Member& srcMember);
1182     static Member dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember);
1183     private:
1184     Archive& m_dst;
1185     Archive& m_src;
1186     };
1187    
1188 schoenebeck 3182 enum operation_t {
1189     OPERATION_NONE,
1190     OPERATION_SERIALIZE,
1191     OPERATION_DESERIALIZE
1192     };
1193    
1194 schoenebeck 3138 virtual void encode();
1195    
1196     ObjectPool m_allObjects;
1197     operation_t m_operation;
1198     UID m_root;
1199     RawData m_rawData;
1200 schoenebeck 3150 bool m_isModified;
1201 schoenebeck 3156 String m_name;
1202     String m_comment;
1203     time_t m_timeCreated;
1204     time_t m_timeModified;
1205 schoenebeck 3138 };
1206    
1207     /**
1208     * Will be thrown whenever an error occurs during an serialization or
1209     * deserialization process.
1210     */
1211     class Exception {
1212     public:
1213     String Message;
1214    
1215 schoenebeck 3198 Exception(String format, ...);
1216     Exception(String format, va_list arg);
1217 schoenebeck 3138 void PrintMessage();
1218     virtual ~Exception() {}
1219 schoenebeck 3198
1220     protected:
1221     Exception();
1222     static String assemble(String format, va_list arg);
1223 schoenebeck 3138 };
1224    
1225     } // namespace Serialization
1226    
1227     #endif // LIBGIG_SERIALIZATION_H

  ViewVC Help
Powered by ViewVC