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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3772 - (hide annotations) (download) (as text)
Sun May 17 17:21:52 2020 UTC (3 years, 11 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 52684 byte(s)
Fixed compiler error with (non-C++14 compliant) GCC compilers.

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

  ViewVC Help
Powered by ViewVC