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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 3169 by schoenebeck, Wed May 10 21:17:10 2017 UTC revision 3771 by schoenebeck, Sun May 17 17:14:31 2020 UTC
# Line 1  Line 1 
1  /***************************************************************************  /***************************************************************************
2   *                                                                         *   *                                                                         *
3   *   Copyright (C) 2017 Christian Schoenebeck                              *   *   Copyright (C) 2017-2020 Christian Schoenebeck                         *
4   *                      <cuse@users.sourceforge.net>                       *   *                           <cuse@users.sourceforge.net>                  *
5   *                                                                         *   *                                                                         *
6   *   This library is part of libgig.                                       *   *   This library is part of libgig.                                       *
7   *                                                                         *   *                                                                         *
# Line 35  Line 35 
35  #include <vector>  #include <vector>
36  #include <map>  #include <map>
37  #include <time.h>  #include <time.h>
38  #if __cplusplus < 201103L  #include <stdarg.h>
39    
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  # include <tr1/type_traits>  # include <tr1/type_traits>
62  # define LIBGIG_IS_CLASS(type) std::tr1::__is_union_or_class<type>::value //NOTE: without compiler support we cannot distinguish union from class  # 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  #else
# Line 97  namespace Serialization { Line 119  namespace Serialization {
119    
120      typedef std::string String;      typedef std::string String;
121    
122        /** @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      typedef std::vector<uint8_t> RawData;      typedef std::vector<uint8_t> RawData;
131    
132        /** @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      typedef void* ID;      typedef void* ID;
143    
144        /** @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      typedef uint32_t Version;      typedef uint32_t Version;
152    
153      enum operation_t {      /** @brief To which time zone a certain timing information relates to.
154          OPERATION_NONE,       *
155          OPERATION_SERIALIZE,       * The constants in this enum type are used to define to which precise time
156          OPERATION_DESERIALIZE       * zone a time stamp relates to.
157      };       */
   
158      enum time_base_t {      enum time_base_t {
159          LOCAL_TIME,          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          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      };      };
162    
163        /** @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      template<typename T>      template<typename T>
171      bool IsEnum(const T& data) {      bool IsEnum(const T& data) {
172          #if __cplusplus < 201103L          #if !HAS_BUILTIN_TYPE_TRAITS
173          return std::tr1::is_enum<T>::value;          return std::tr1::is_enum<T>::value;
174          #else          #else
175          return __is_enum(T);          return __is_enum(T);
176          #endif          #endif
177      }      }
178    
179        /** @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      template<typename T>      template<typename T>
190      bool IsUnion(const T& data) {      bool IsUnion(const T& data) {
191          #if __cplusplus < 201103L          #if !HAS_BUILTIN_TYPE_TRAITS
192          return false; // without compiler support we cannot distinguish union from class          return false; // without compiler support we cannot distinguish union from class
193          #else          #else
194          return __is_union(T);          return __is_union(T);
195          #endif          #endif
196      }      }
197    
198        /** @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      template<typename T>      template<typename T>
208      bool IsClass(const T& data) {      bool IsClass(const T& data) {
209          #if __cplusplus < 201103L          #if !HAS_BUILTIN_TYPE_TRAITS
210          return std::tr1::__is_union_or_class<T>::value; // without compiler support we cannot distinguish union from class          return std::tr1::__is_union_or_class<T>::value; // without compiler support we cannot distinguish union from class
211          #else          #else
212          return __is_class(T);          return __is_class(T);
# Line 151  namespace Serialization { Line 223  namespace Serialization {
223          return __is_pod(T);          return __is_pod(T);
224      }*/      }*/
225    
226      /** @brief Unique identifier for one specific C++ object, member or fundamental variable.      /** @brief Unique identifier referring to one specific native C++ object, member, fundamental variable, or any other native C++ data.
227         *
228         * 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       *       *
233       * Reflects a unique identifier for one specific serialized C++ class       * A unique identifier is composed of an id (an identifier which is not
234       * instance, struct instance, member, primitive pointer, or fundamental       * necessarily unique) and a size. Since the underlying ID is derived from
235       * variables.       * 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       */       */
241      class UID {      class UID {
242      public:      public:
243          ID id;          ID id; ///< Abstract non-unique ID of the object or member in question.
244          size_t size;          size_t size; ///< Memory size of the object or member in question.
245    
246          bool isValid() const;          bool isValid() const;
247          operator bool() const { return isValid(); }          operator bool() const { return isValid(); } ///< Same as calling isValid().
248          //bool operator()() const { return isValid(); }          //bool operator()() const { return isValid(); }
249          bool operator==(const UID& other) const { return id == other.id && size == other.size; }          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; }          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); }          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); }          bool operator>(const UID& other) const { return id > other.id || (id == other.id && size > other.size); }
253    
254            /** @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          template<typename T>          template<typename T>
262          static UID from(const T& obj) {          static UID from(const T& obj) {
263              return Resolver<T>::resolve(obj);              return Resolver<T>::resolve(obj);
# Line 197  namespace Serialization { Line 285  namespace Serialization {
285    
286      /**      /**
287       * Reflects an invalid UID and behaves similar to NULL as invalid value for       * Reflects an invalid UID and behaves similar to NULL as invalid value for
288       * pointer types.       * pointer types. All UID objects are first initialized with this value,
289         * and it essentially an all zero object.
290       */       */
291      extern const UID NO_UID;      extern const UID NO_UID;
292    
293        /** @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      typedef std::vector<UID> UIDChain;      typedef std::vector<UID> UIDChain;
325    
326    #if LIBGIG_SERIALIZATION_INTERNAL
327      // prototyping of private internal friend functions      // prototyping of private internal friend functions
328      static String _encodePrimitiveValue(const Object& obj);      static String _encodePrimitiveValue(const Object& obj);
329      static DataType _popDataTypeBlob(const char*& p, const char* end);      static DataType _popDataTypeBlob(const char*& p, const char* end);
# Line 213  namespace Serialization { Line 334  namespace Serialization {
334      //  |      //  |
335      template<typename T>      template<typename T>
336      static T _primitiveObjectValueToNumber(const Object& obj);      static T _primitiveObjectValueToNumber(const Object& obj);
337    #endif // LIBGIG_SERIALIZATION_INTERNAL
338    
339      /** @brief Abstract reflection of a native C++ data type.      /** @brief Abstract reflection of a native C++ data type.
340       *       *
341       * Provides detailed information about a C++ data type, whether it is a       * Provides detailed information about a serialized C++ data type, whether
342       * fundamental C/C++ data type (like int, float, char, etc.) or custom       * it is a fundamental C/C++ data type (like @c int, @c float, @c char,
343       * defined data type like a C++ class, struct, enum, as well as other       * etc.) or custom defined data types like a C++ @c class, C/C++ @c struct,
344       * features of the data type like its native memory size and more.       * @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       */       */
355      class DataType {      class DataType {
356      public:      public:
357          DataType();          DataType();
358          size_t size() const { return m_size; }          size_t size() const { return m_size; } ///< Returns native memory size of the respective C++ object or variable.
359          bool isValid() const;          bool isValid() const;
360          bool isPointer() const;          bool isPointer() const;
361          bool isClass() const;          bool isClass() const;
362          bool isPrimitive() const;          bool isPrimitive() const;
363            bool isString() const;
364          bool isInteger() const;          bool isInteger() const;
365          bool isReal() const;          bool isReal() const;
366          bool isBool() const;          bool isBool() const;
367          bool isEnum() const;          bool isEnum() const;
368          bool isSigned() const;          bool isSigned() const;
369          operator bool() const { return isValid(); }          operator bool() const { return isValid(); } ///< Same as calling isValid().
370          //bool operator()() const { return isValid(); }          //bool operator()() const { return isValid(); }
371          bool operator==(const DataType& other) const;          bool operator==(const DataType& other) const;
372          bool operator!=(const DataType& other) const;          bool operator!=(const DataType& other) const;
373          bool operator<(const DataType& other) const;          bool operator<(const DataType& other) const;
374          bool operator>(const DataType& other) const;          bool operator>(const DataType& other) const;
375          String asLongDescr() const;          String asLongDescr() const;
376          String baseTypeName() const { return m_baseTypeName; }          String baseTypeName() const;
377          String customTypeName() const { return m_customTypeName; }          String customTypeName(bool demangle = false) const;
378    
379            /** @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          template<typename T>          template<typename T>
390          static DataType dataTypeOf(const T& data) {          static DataType dataTypeOf(const T& data) {
391              return Resolver<T>::resolve(data);              return Resolver<T>::resolve(data);
# Line 272  namespace Serialization { Line 414  namespace Serialization {
414                  if (type == typeid(bool))     return DataType(T_isPointer, sz, "bool");                  if (type == typeid(bool))     return DataType(T_isPointer, sz, "bool");
415                  if (type == typeid(float))    return DataType(T_isPointer, sz, "real32");                  if (type == typeid(float))    return DataType(T_isPointer, sz, "real32");
416                  if (type == typeid(double))   return DataType(T_isPointer, sz, "real64");                  if (type == typeid(double))   return DataType(T_isPointer, sz, "real64");
417                    if (type == typeid(String))   return DataType(T_isPointer, sz, "String");
418    
419                  if (IsEnum(data)) return DataType(T_isPointer, sz, "enum", rawCppTypeNameOf(data));                  if (IsEnum(data)) return DataType(T_isPointer, sz, "enum", rawCppTypeNameOf(data));
420                  if (IsUnion(data)) return DataType(T_isPointer, sz, "union", rawCppTypeNameOf(data));                  if (IsUnion(data)) return DataType(T_isPointer, sz, "union", rawCppTypeNameOf(data));
# Line 300  namespace Serialization { Line 443  namespace Serialization {
443          template<typename T>          template<typename T>
444          static String rawCppTypeNameOf(const T& data) {          static String rawCppTypeNameOf(const T& data) {
445              #if defined _MSC_VER // Microsoft compiler ...              #if defined _MSC_VER // Microsoft compiler ...
446              # warning type_info::raw_name() demangling has not been tested yet with Microsoft compiler! Feedback appreciated!              String name = typeid(data).raw_name();
             String name = typeid(data).raw_name(); //NOTE: I haven't checked yet what MSC actually outputs here exactly  
447              #else // i.e. especially GCC and clang ...              #else // i.e. especially GCC and clang ...
448              String name = typeid(data).name();              String name = typeid(data).name();
449              #endif              #endif
# Line 316  namespace Serialization { Line 458  namespace Serialization {
458          int m_size;          int m_size;
459          bool m_isPointer;          bool m_isPointer;
460    
461    #if LIBGIG_SERIALIZATION_INTERNAL
462          friend DataType _popDataTypeBlob(const char*& p, const char* end);          friend DataType _popDataTypeBlob(const char*& p, const char* end);
463    #endif
464          friend class Archive;          friend class Archive;
465      };      };
466    
# Line 326  namespace Serialization { Line 470  namespace Serialization {
470       * serialized C++ object, like its C++ data type, offset of this member       * serialized C++ object, like its C++ data type, offset of this member
471       * within its containing data structure/class, its C++ member variable name       * within its containing data structure/class, its C++ member variable name
472       * and more.       * and more.
473         *
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       */       */
488      class Member {      class Member {
489      public:      public:
490          Member();          Member();
491          UID uid() const { return m_uid; }          UID uid() const;
492          String name() const { return m_name; }          String name() const;
493          size_t offset() const { return m_offset; }          size_t offset() const;
494          const DataType& type() const { return m_type; }          const DataType& type() const;
495          bool isValid() const;          bool isValid() const;
496          operator bool() const { return isValid(); }          operator bool() const { return isValid(); } ///< Same as calling isValid().
497          //bool operator()() const { return isValid(); }          //bool operator()() const { return isValid(); }
498          bool operator==(const Member& other) const;          bool operator==(const Member& other) const;
499          bool operator!=(const Member& other) const;          bool operator!=(const Member& other) const;
# Line 352  namespace Serialization { Line 510  namespace Serialization {
510          String m_name;          String m_name;
511          DataType m_type;          DataType m_type;
512    
513    #if LIBGIG_SERIALIZATION_INTERNAL
514          friend Member _popMemberBlob(const char*& p, const char* end);          friend Member _popMemberBlob(const char*& p, const char* end);
515    #endif
516      };      };
517    
518      /** @brief Abstract reflection of a native C++ class/struct instance.      /** @brief Abstract reflection of some native serialized C/C++ data.
519         *
520         * 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         *
527         * 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         * 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         * instead. This design decision was made for performance and safety
538         * reasons.
539       *       *
540       * Provides detailed information about a specific serialized C++ object,       * @see Archive::setIntValue() as an example for modifying Object instances.
      * like its C++ member variables, its C++ class/struct name, its native  
      * memory size and more.  
541       */       */
542      class Object {      class Object {
543      public:      public:
544          Object();          Object();
545          Object(UIDChain uidChain, DataType type);          Object(UIDChain uidChain, DataType type);
546    
547          UID uid(int index = 0) const {          UID uid(int index = 0) const;
548              return (index < m_uid.size()) ? m_uid[index] : NO_UID;          const UIDChain& uidChain() const;
549          }          const DataType& type() const;
550            const RawData& rawData() const;
551          const UIDChain& uidChain() const { return m_uid; }          Version version() const;
552          const DataType& type() const { return m_type; }          Version minVersion() const;
         const RawData& rawData() const { return m_data; }  
   
         Version version() const { return m_version; }  
   
         void setVersion(Version v) {  
             m_version = v;  
         }  
   
         Version minVersion() const { return m_minVersion; }  
   
         void setMinVersion(Version v) {  
             m_minVersion = v;  
         }  
   
553          bool isVersionCompatibleTo(const Object& other) const;          bool isVersionCompatibleTo(const Object& other) const;
554            std::vector<Member>& members();
555          std::vector<Member>& members() { return m_members; }          const std::vector<Member>& members() const;
         const std::vector<Member>& members() const { return m_members; }  
556          Member memberNamed(String name) const;          Member memberNamed(String name) const;
557          Member memberByUID(const UID& uid) const;          Member memberByUID(const UID& uid) const;
558          std::vector<Member> membersOfType(const DataType& type) const;          std::vector<Member> membersOfType(const DataType& type) const;
559          int sequenceIndexOf(const Member& member) const;          int sequenceIndexOf(const Member& member) const;
560          bool isValid() const;          bool isValid() const;
561          operator bool() const { return isValid(); }          operator bool() const { return isValid(); } ///< Same as calling isValid().
562          //bool operator()() const { return isValid(); }          //bool operator()() const { return isValid(); }
563          bool operator==(const Object& other) const;          bool operator==(const Object& other) const;
564          bool operator!=(const Object& other) const;          bool operator!=(const Object& other) const;
# Line 404  namespace Serialization { Line 567  namespace Serialization {
567    
568      protected:      protected:
569          void remove(const Member& member);          void remove(const Member& member);
570            void setVersion(Version v);
571            void setMinVersion(Version v);
572    
573      private:      private:
574          DataType m_type;          DataType m_type;
# Line 413  namespace Serialization { Line 578  namespace Serialization {
578          RawData m_data;          RawData m_data;
579          std::vector<Member> m_members;          std::vector<Member> m_members;
580    
581    #if LIBGIG_SERIALIZATION_INTERNAL
582          friend String _encodePrimitiveValue(const Object& obj);          friend String _encodePrimitiveValue(const Object& obj);
583          friend Object _popObjectBlob(const char*& p, const char* end);          friend Object _popObjectBlob(const char*& p, const char* end);
584          friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj);          friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
585          friend String _primitiveObjectValueToString(const Object& obj);          friend String _primitiveObjectValueToString(const Object& obj);
586            // |
587          template<typename T>          template<typename T>
588          friend T _primitiveObjectValueToNumber(const Object& obj);          friend T _primitiveObjectValueToNumber(const Object& obj);
589    #endif // LIBGIG_SERIALIZATION_INTERNAL
590    
591          friend class Archive;          friend class Archive;
592      };      };
# Line 536  namespace Serialization { Line 703  namespace Serialization {
703       * Note that there is only one method that you need to implement. So the       * Note that there is only one method that you need to implement. So the
704       * respective serialize() method implementation of your classes/structs are       * respective serialize() method implementation of your classes/structs are
705       * both called for serialization, as well as for deserialization!       * both called for serialization, as well as for deserialization!
706         *
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       */       */
711      class Archive {      class Archive {
712      public:      public:
# Line 544  namespace Serialization { Line 715  namespace Serialization {
715          Archive(const uint8_t* data, size_t size);          Archive(const uint8_t* data, size_t size);
716          virtual ~Archive();          virtual ~Archive();
717    
718            /** @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          template<typename T>          template<typename T>
744          void serialize(const T* obj) {          void serialize(const T* obj) {
745              m_operation = OPERATION_SERIALIZE;              m_operation = OPERATION_SERIALIZE;
# Line 555  namespace Serialization { Line 751  namespace Serialization {
751              m_operation = OPERATION_NONE;              m_operation = OPERATION_NONE;
752          }          }
753    
754            /** @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          template<typename T>          template<typename T>
779          void deserialize(T* obj) {          void deserialize(T* obj) {
780              Archive a;              Archive a;
# Line 565  namespace Serialization { Line 785  namespace Serialization {
785              m_operation = OPERATION_NONE;              m_operation = OPERATION_NONE;
786          }          }
787    
788            /** @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          template<typename T>          template<typename T>
803          void operator<<(const T& obj) {          void operator<<(const T& obj) {
804              serialize(&obj);              serialize(&obj);
805          }          }
806    
807            /** @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          template<typename T>          template<typename T>
826          void operator>>(T& obj) {          void operator>>(T& obj) {
827              deserialize(&obj);              deserialize(&obj);
# Line 578  namespace Serialization { Line 830  namespace Serialization {
830          const RawData& rawData();          const RawData& rawData();
831          virtual String rawDataFormat() const;          virtual String rawDataFormat() const;
832    
833            /** @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          template<typename T_classType, typename T_memberType>          template<typename T_classType, typename T_memberType>
879          void serializeMember(const T_classType& nativeObject, const T_memberType& nativeMember, const char* memberName) {          void serializeMember(const T_classType& nativeObject, const T_memberType& nativeMember, const char* memberName) {
880              const size_t offset =              const size_t offset =
881              ((const uint8_t*)(const void*)&nativeMember) -                  ((const uint8_t*)(const void*)&nativeMember) -
882              ((const uint8_t*)(const void*)&nativeObject);                  ((const uint8_t*)(const void*)&nativeObject);
883              const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);              const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);
884              const DataType type = DataType::dataTypeOf(nativeMember);              const DataType type = DataType::dataTypeOf(nativeMember);
885              const Member member(memberName, uids[0], offset, type);              const Member member(memberName, uids[0], offset, type);
# Line 606  namespace Serialization { Line 903  namespace Serialization {
903              }              }
904          }          }
905    
906            /** @brief Set current version number for your C++ class.
907             *
908             * By calling this method you can define a version number for your
909             * current C++ class (that is a version for its current data structure
910             * 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             *
914             * Along with calling @c setMinVersion() this provides a way for you
915             * 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             * @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             *     // when deserializing: the minimum version this C++ class is
928             *     // compatible with
929             *     archive->setMinVersion(*this, 3);
930             *     // actual data mebers to serialize / deserialize
931             *     SRLZ(a);
932             *     SRLZ(b);
933             *     SRLZ(c);
934             * }
935             * @endcode
936             * 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             *
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             * 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             * 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             * 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             *
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             * 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             *
996             * 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             * @see @c setVersion() for more details about this overall topic.
1009             *
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             */
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          virtual void decode(const RawData& data);          virtual void decode(const RawData& data);
1029          virtual void decode(const uint8_t* data, size_t size);          virtual void decode(const uint8_t* data, size_t size);
1030          void clear();          void clear();
# Line 619  namespace Serialization { Line 1038  namespace Serialization {
1038          void setRealValue(Object& object, double value);          void setRealValue(Object& object, double value);
1039          void setBoolValue(Object& object, bool value);          void setBoolValue(Object& object, bool value);
1040          void setEnumValue(Object& object, uint64_t value);          void setEnumValue(Object& object, uint64_t value);
1041            void setStringValue(Object& object, String value);
1042          String valueAsString(const Object& object);          String valueAsString(const Object& object);
1043          int64_t valueAsInt(const Object& object);          int64_t valueAsInt(const Object& object);
1044          double valueAsReal(const Object& object);          double valueAsReal(const Object& object);
1045          bool valueAsBool(const Object& object);          bool valueAsBool(const Object& object);
1046            void setVersion(Object& object, Version v);
1047            void setMinVersion(Object& object, Version v);
1048          String name() const;          String name() const;
1049          void setName(String name);          void setName(String name);
1050          String comment() const;          String comment() const;
# Line 695  namespace Serialization { Line 1117  namespace Serialization {
1117              static void serializeObject(Archive* archive, const T*& obj) {}              static void serializeObject(Archive* archive, const T*& obj) {}
1118          };          };
1119    
1120            // NOOP SerializationRecursion for String objects.
1121            template<>
1122            struct SerializationRecursionImpl<String,true> {
1123                static void serializeObject(Archive* archive, const String& obj) {}
1124            };
1125    
1126            // NOOP SerializationRecursion for String pointers (of 1st degree).
1127            template<>
1128            struct SerializationRecursionImpl<String*,true> {
1129                static void serializeObject(Archive* archive, const String*& obj) {}
1130            };
1131    
1132          // Automatically handles recursion for class/struct types, while ignoring all primitive types.          // Automatically handles recursion for class/struct types, while ignoring all primitive types.
1133          template<typename T>          template<typename T>
1134          struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> {          struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> {
# Line 727  namespace Serialization { Line 1161  namespace Serialization {
1161          protected:          protected:
1162              void syncObject(const Object& dst, const Object& src);              void syncObject(const Object& dst, const Object& src);
1163              void syncPrimitive(const Object& dst, const Object& src);              void syncPrimitive(const Object& dst, const Object& src);
1164                void syncString(const Object& dst, const Object& src);
1165              void syncPointer(const Object& dst, const Object& src);              void syncPointer(const Object& dst, const Object& src);
1166              void syncMember(const Member& dstMember, const Member& srcMember);              void syncMember(const Member& dstMember, const Member& srcMember);
1167              static Member dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember);              static Member dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember);
# Line 735  namespace Serialization { Line 1170  namespace Serialization {
1170              Archive& m_src;              Archive& m_src;
1171          };          };
1172    
1173            enum operation_t {
1174                OPERATION_NONE,
1175                OPERATION_SERIALIZE,
1176                OPERATION_DESERIALIZE
1177            };
1178    
1179          virtual void encode();          virtual void encode();
1180    
1181          ObjectPool m_allObjects;          ObjectPool m_allObjects;
# Line 756  namespace Serialization { Line 1197  namespace Serialization {
1197          public:          public:
1198              String Message;              String Message;
1199    
1200              Exception(String Message) { Exception::Message = Message; }              Exception(String format, ...);
1201                Exception(String format, va_list arg);
1202              void PrintMessage();              void PrintMessage();
1203              virtual ~Exception() {}              virtual ~Exception() {}
1204    
1205            protected:
1206                Exception();
1207                static String assemble(String format, va_list arg);
1208      };      };
1209    
1210  } // namespace Serialization  } // namespace Serialization

Legend:
Removed from v.3169  
changed lines
  Added in v.3771

  ViewVC Help
Powered by ViewVC