/[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 3153 by schoenebeck, Sat May 6 13:43:43 2017 UTC revision 3476 by schoenebeck, Wed Feb 20 19:12:49 2019 UTC
# Line 1  Line 1 
1  /***************************************************************************  /***************************************************************************
2   *                                                                         *   *                                                                         *
3   *   Copyright (C) 2017 Christian Schoenebeck                              *   *   Copyright (C) 2017-2019 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 34  Line 34 
34  #include <string>  #include <string>
35  #include <vector>  #include <vector>
36  #include <map>  #include <map>
37    #include <time.h>
38    #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>
62    # 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    #endif
66    
67  /** @brief Serialization / deserialization framework.  /** @brief Serialization / deserialization framework.
68   *   *
# Line 90  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 {
159            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      };      };
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 !HAS_BUILTIN_TYPE_TRAITS
173            return std::tr1::is_enum<T>::value;
174            #else
175          return __is_enum(T);          return __is_enum(T);
176            #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 !HAS_BUILTIN_TYPE_TRAITS
192            return false; // without compiler support we cannot distinguish union from class
193            #else
194          return __is_union(T);          return __is_union(T);
195            #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 !HAS_BUILTIN_TYPE_TRAITS
210            return std::tr1::__is_union_or_class<T>::value; // without compiler support we cannot distinguish union from class
211            #else
212          return __is_class(T);          return __is_class(T);
213            #endif
214      }      }
215    
216      /*template<typename T>      /*template<typename T>
# Line 127  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 156  namespace Serialization { Line 268  namespace Serialization {
268          template<typename T>          template<typename T>
269          struct Resolver {          struct Resolver {
270              static UID resolve(const T& obj) {              static UID resolve(const T& obj) {
271                  return (UID) { (ID) &obj, sizeof(obj) };                  const UID uid = { (ID) &obj, sizeof(obj) };
272                    return uid;
273              }              }
274          };          };
275    
# Line 164  namespace Serialization { Line 277  namespace Serialization {
277          template<typename T>          template<typename T>
278          struct Resolver<T*> {          struct Resolver<T*> {
279              static UID resolve(const T* const & obj) {              static UID resolve(const T* const & obj) {
280                  return (UID) { (ID) obj, sizeof(*obj) };                  const UID uid = { (ID) obj, sizeof(*obj) };
281                    return uid;
282              }              }
283          };          };
284      };      };
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 184  namespace Serialization { Line 331  namespace Serialization {
331      static Object _popObjectBlob(const char*& p, const char* end);      static Object _popObjectBlob(const char*& p, const char* end);
332      static void _popPrimitiveValue(const char*& p, const char* end, Object& obj);      static void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
333      static String _primitiveObjectValueToString(const Object& obj);      static String _primitiveObjectValueToString(const Object& obj);
334        //  |
335        template<typename T>
336        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;
# Line 205  namespace Serialization { Line 365  namespace Serialization {
365          bool isBool() const;          bool isBool() const;
366          bool isEnum() const;          bool isEnum() const;
367          bool isSigned() const;          bool isSigned() const;
368          operator bool() const { return isValid(); }          operator bool() const { return isValid(); } ///< Same as calling isValid().
369          //bool operator()() const { return isValid(); }          //bool operator()() const { return isValid(); }
370          bool operator==(const DataType& other) const;          bool operator==(const DataType& other) const;
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          String asLongDescr() const;          String asLongDescr() const;
375          String baseTypeName() const { return m_baseTypeName; }          String baseTypeName() const;
376          String customTypeName() const { return m_customTypeName; }          String customTypeName(bool demangle = false) const;
377    
378            /** @brief Construct a DataType object for the given native C++ data.
379             *
380             * Use this function to create corresponding DataType objects for
381             * native C/C++ objects, members and variables.
382             *
383             * @param data - native C/C++ object/member/variable a DataType object
384             *               shall be created for
385             * @returns corresponding DataType object for the supplied native C/C++
386             *          object/member/variable
387             */
388          template<typename T>          template<typename T>
389          static DataType dataTypeOf(const T& data) {          static DataType dataTypeOf(const T& data) {
390              return Resolver<T>::resolve(data);              return Resolver<T>::resolve(data);
# Line 271  namespace Serialization { Line 441  namespace Serialization {
441          template<typename T>          template<typename T>
442          static String rawCppTypeNameOf(const T& data) {          static String rawCppTypeNameOf(const T& data) {
443              #if defined _MSC_VER // Microsoft compiler ...              #if defined _MSC_VER // Microsoft compiler ...
444              # 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  
445              #else // i.e. especially GCC and clang ...              #else // i.e. especially GCC and clang ...
446              String name = typeid(data).name();              String name = typeid(data).name();
447              #endif              #endif
# Line 287  namespace Serialization { Line 456  namespace Serialization {
456          int m_size;          int m_size;
457          bool m_isPointer;          bool m_isPointer;
458    
459    #if LIBGIG_SERIALIZATION_INTERNAL
460          friend DataType _popDataTypeBlob(const char*& p, const char* end);          friend DataType _popDataTypeBlob(const char*& p, const char* end);
461    #endif
462          friend class Archive;          friend class Archive;
463      };      };
464    
# Line 297  namespace Serialization { Line 468  namespace Serialization {
468       * serialized C++ object, like its C++ data type, offset of this member       * serialized C++ object, like its C++ data type, offset of this member
469       * within its containing data structure/class, its C++ member variable name       * within its containing data structure/class, its C++ member variable name
470       * and more.       * and more.
471         *
472         * Consider you defined the following user defined C/C++ @c struct type in
473         * your application:
474         * @code
475         * struct Foo {
476         *     int  a;
477         *     bool b;
478         *     double someValue;
479         * };
480         * @endcode
481         * Then @c a, @c b and @c someValue are "members" of @c struct @c Foo for
482         * instance. So that @c struct would have 3 members in the latter example.
483         *
484         * @see Object::members()
485       */       */
486      class Member {      class Member {
487      public:      public:
488          Member();          Member();
489          UID uid() const { return m_uid; }          UID uid() const;
490          String name() const { return m_name; }          String name() const;
491          size_t offset() const { return m_offset; }          size_t offset() const;
492          const DataType& type() const { return m_type; }          const DataType& type() const;
493          bool isValid() const;          bool isValid() const;
494          operator bool() const { return isValid(); }          operator bool() const { return isValid(); } ///< Same as calling isValid().
495          //bool operator()() const { return isValid(); }          //bool operator()() const { return isValid(); }
496          bool operator==(const Member& other) const;          bool operator==(const Member& other) const;
497          bool operator!=(const Member& other) const;          bool operator!=(const Member& other) const;
# Line 323  namespace Serialization { Line 508  namespace Serialization {
508          String m_name;          String m_name;
509          DataType m_type;          DataType m_type;
510    
511    #if LIBGIG_SERIALIZATION_INTERNAL
512          friend Member _popMemberBlob(const char*& p, const char* end);          friend Member _popMemberBlob(const char*& p, const char* end);
513    #endif
514      };      };
515    
516      /** @brief Abstract reflection of a native C++ class/struct instance.      /** @brief Abstract reflection of some native serialized C/C++ data.
517         *
518         * When your native C++ objects are serialized, all native data is
519         * translated and reflected by such an Object reflection. So each instance
520         * of your serialized native C++ class objects become available as an
521         * Object, but also each member variable of your C++ objects is translated
522         * into an Object, and any other native C/C++ data. So essentially every
523         * native data is turned into its own Object and accessible by this API.
524         *
525         * For each one of those Object reflections, this class provides detailed
526         * information about their native origin. For example if an Object
527         * represents a native C++ class instante, then it provides access to its
528         * C++ class/struct name, to its C++ member variables, its native memory
529         * size and much more.
530       *       *
531       * Provides detailed information about a specific serialized C++ object,       * Even though this framework allows you to adjust abstract Object instances
532       * like its C++ member variables, its C++ class/struct name, its native       * to a certain extent, most of the methods of this Object class are
533       * memory size and more.       * read-only though and the actual modifyable methods are made available
534         * not as part of this Object class, but as part of the Archive class
535         * instead. This design decision was made for performance and safety
536         * reasons.
537         *
538         * @see Archive::setIntValue() as an example for modifying Object instances.
539       */       */
540      class Object {      class Object {
541      public:      public:
542          Object();          Object();
543          Object(UIDChain uidChain, DataType type);          Object(UIDChain uidChain, DataType type);
544    
545          UID uid(int index = 0) const {          UID uid(int index = 0) const;
546              return (index < m_uid.size()) ? m_uid[index] : NO_UID;          const UIDChain& uidChain() const;
547          }          const DataType& type() const;
548            const RawData& rawData() const;
549          const UIDChain& uidChain() const { return m_uid; }          Version version() const;
550          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;  
         }  
   
551          bool isVersionCompatibleTo(const Object& other) const;          bool isVersionCompatibleTo(const Object& other) const;
552            std::vector<Member>& members();
553          std::vector<Member>& members() { return m_members; }          const std::vector<Member>& members() const;
         const std::vector<Member>& members() const { return m_members; }  
554          Member memberNamed(String name) const;          Member memberNamed(String name) const;
555          Member memberByUID(const UID& uid) const;          Member memberByUID(const UID& uid) const;
556          std::vector<Member> membersOfType(const DataType& type) const;          std::vector<Member> membersOfType(const DataType& type) const;
557          int sequenceIndexOf(const Member& member) const;          int sequenceIndexOf(const Member& member) const;
558          bool isValid() const;          bool isValid() const;
559          operator bool() const { return isValid(); }          operator bool() const { return isValid(); } ///< Same as calling isValid().
560          //bool operator()() const { return isValid(); }          //bool operator()() const { return isValid(); }
561          bool operator==(const Object& other) const;          bool operator==(const Object& other) const;
562          bool operator!=(const Object& other) const;          bool operator!=(const Object& other) const;
# Line 375  namespace Serialization { Line 565  namespace Serialization {
565    
566      protected:      protected:
567          void remove(const Member& member);          void remove(const Member& member);
568            void setVersion(Version v);
569            void setMinVersion(Version v);
570    
571      private:      private:
572          DataType m_type;          DataType m_type;
# Line 384  namespace Serialization { Line 576  namespace Serialization {
576          RawData m_data;          RawData m_data;
577          std::vector<Member> m_members;          std::vector<Member> m_members;
578    
579    #if LIBGIG_SERIALIZATION_INTERNAL
580          friend String _encodePrimitiveValue(const Object& obj);          friend String _encodePrimitiveValue(const Object& obj);
581          friend Object _popObjectBlob(const char*& p, const char* end);          friend Object _popObjectBlob(const char*& p, const char* end);
582          friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj);          friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
583          friend String _primitiveObjectValueToString(const Object& obj);          friend String _primitiveObjectValueToString(const Object& obj);
584            // |
585            template<typename T>
586            friend T _primitiveObjectValueToNumber(const Object& obj);
587    #endif // LIBGIG_SERIALIZATION_INTERNAL
588    
589          friend class Archive;          friend class Archive;
590      };      };
591    
# Line 503  namespace Serialization { Line 701  namespace Serialization {
701       * 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
702       * respective serialize() method implementation of your classes/structs are       * respective serialize() method implementation of your classes/structs are
703       * both called for serialization, as well as for deserialization!       * both called for serialization, as well as for deserialization!
704         *
705         * In case you need to enforce backward incompatiblity for one of your C++
706         * classes, you can do so by setting a version and minimum version for your
707         * class (see @c setVersion() and @c setMinVersion() for details).
708       */       */
709      class Archive {      class Archive {
710      public:      public:
# Line 511  namespace Serialization { Line 713  namespace Serialization {
713          Archive(const uint8_t* data, size_t size);          Archive(const uint8_t* data, size_t size);
714          virtual ~Archive();          virtual ~Archive();
715    
716            /** @brief Initiate serialization.
717             *
718             * Initiates serialization of all native C++ objects, which means
719             * capturing and storing the current data of all your C++ objects as
720             * content of this Archive.
721             *
722             * This framework has a concept of a "root" object which you must pass
723             * to this method. The root object is the starting point for
724             * serialization of your C++ objects. The framework will then
725             * recursively serialize all members of that C++ object an continue to
726             * serialize all other C++ objects that it might contain or point to.
727             *
728             * After this method returned, you might traverse all serialized objects
729             * by walking them starting from the rootObject(). You might then modify
730             * that abstract reflection of your C++ objects and finally you might
731             * call rawData() to get an encoded raw data stream which you might use
732             * for sending it "over wire" to somewhere where it is going to be
733             * deserialized later on.
734             *
735             * Note that whenever you call this method, the previous content of this
736             * Archive will first be cleared.
737             *
738             * @param obj - native C++ root object where serialization shall start
739             * @see Archive::operator<<()
740             */
741          template<typename T>          template<typename T>
742          void serialize(const T* obj) {          void serialize(const T* obj) {
743              m_operation = OPERATION_SERIALIZE;              m_operation = OPERATION_SERIALIZE;
# Line 522  namespace Serialization { Line 749  namespace Serialization {
749              m_operation = OPERATION_NONE;              m_operation = OPERATION_NONE;
750          }          }
751    
752            /** @brief Initiate deserialization.
753             *
754             * Initiates deserialization of all native C++ objects, which means all
755             * your C++ objects will be restored with the values contained in this
756             * Archive. So that also means calling deserialize() only makes sense if
757             * this a non-empty Archive, which i.e. is the case if you either called
758             * serialize() with this Archive object before or if you passed a
759             * previously serialized raw data stream to the constructor of this
760             * Archive object.
761             *
762             * This framework has a concept of a "root" object which you must pass
763             * to this method. The root object is the starting point for
764             * deserialization of your C++ objects. The framework will then
765             * recursively deserialize all members of that C++ object an continue to
766             * deserialize all other C++ objects that it might contain or point to,
767             * according to the values stored in this Archive.
768             *
769             * @param obj - native C++ root object where deserialization shall start
770             * @see Archive::operator>>()
771             *
772             * @throws Exception if the data stored in this Archive cannot be
773             *         restored to the C++ objects passed to this method, i.e.
774             *         because of version or type incompatibilities.
775             */
776          template<typename T>          template<typename T>
777          void deserialize(T* obj) {          void deserialize(T* obj) {
778              Archive a;              Archive a;
# Line 532  namespace Serialization { Line 783  namespace Serialization {
783              m_operation = OPERATION_NONE;              m_operation = OPERATION_NONE;
784          }          }
785    
786            /** @brief Initiate serialization of your C++ objects.
787             *
788             * Same as calling @c serialize(), this is just meant if you prefer
789             * to use operator based code instead, which you might find to be more
790             * intuitive.
791             *
792             * Example:
793             * @code
794             * Archive a;
795             * a << myRootObject;
796             * @endcode
797             *
798             * @see Archive::serialize() for more details.
799             */
800          template<typename T>          template<typename T>
801          void operator<<(const T& obj) {          void operator<<(const T& obj) {
802              serialize(&obj);              serialize(&obj);
803          }          }
804    
805            /** @brief Initiate deserialization of your C++ objects.
806             *
807             * Same as calling @c deserialize(), this is just meant if you prefer
808             * to use operator based code instead, which you might find to be more
809             * intuitive.
810             *
811             * Example:
812             * @code
813             * Archive a(rawDataStream);
814             * a >> myRootObject;
815             * @endcode
816             *
817             * @throws Exception if the data stored in this Archive cannot be
818             *         restored to the C++ objects passed to this method, i.e.
819             *         because of version or type incompatibilities.
820             *
821             * @see Archive::deserialize() for more details.
822             */
823          template<typename T>          template<typename T>
824          void operator>>(T& obj) {          void operator>>(T& obj) {
825              deserialize(&obj);              deserialize(&obj);
# Line 545  namespace Serialization { Line 828  namespace Serialization {
828          const RawData& rawData();          const RawData& rawData();
829          virtual String rawDataFormat() const;          virtual String rawDataFormat() const;
830    
831            /** @brief Serialize a native C/C++ member variable.
832             *
833             * This method is usually called by the serialize() method
834             * implementation of your C/C++ structs and classes, for each of the
835             * member variables that shall be serialized and deserialized
836             * automatically with this framework. It is recommend that you are not
837             * using this method name directly, but rather define a short hand C
838             * macro in your .cpp file like:
839             * @code
840             * #define SRLZ(member) \
841             *   archive->serializeMember(*this, member, #member);
842             *
843             * void Foo::serialize(Serialization::Archive* archive) {
844             *     SRLZ(a);
845             *     SRLZ(b);
846             *     SRLZ(c);
847             * }
848             * @endcode
849             * As you can see, using such a macro makes your code more readable and
850             * less error prone.
851             *
852             * It is completely up to you to decide which ones of your member
853             * variables shall automatically be serialized and deserialized with
854             * this framework. Only those member variables which are registered by
855             * calling this method will be serialized and deserialized. It does not
856             * really matter in which order you register your individiual member
857             * variables by calling this method, but the sequence is actually stored
858             * as meta information with the resulting archive and the resulting raw
859             * data stream. That meta information might then be used by this
860             * framework to automatically correct and adapt deserializing that
861             * archive later on for a future (or older) and potentially heavily
862             * modified version of your software. So it is recommended, even though
863             * also not required, that you may retain the sequence of your
864             * serializeMember() calls for your individual C++ classes' members over
865             * all your software versions, to retain backward compatibility of older
866             * archives as much as possible.
867             *
868             * @param nativeObject - native C++ object to be registered for
869             *                       serialization / deserialization
870             * @param nativeMember - native C++ member variable of @a nativeObject
871             *                       to be registered for serialization /
872             *                       deserialization
873             * @param memberName - name of @a nativeMember to be stored with this
874             *                     archive
875             */
876          template<typename T_classType, typename T_memberType>          template<typename T_classType, typename T_memberType>
877          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) {
878              const size_t offset =              const size_t offset =
879              ((const uint8_t*)(const void*)&nativeMember) -                  ((const uint8_t*)(const void*)&nativeMember) -
880              ((const uint8_t*)(const void*)&nativeObject);                  ((const uint8_t*)(const void*)&nativeObject);
881              const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);              const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);
882              const DataType type = DataType::dataTypeOf(nativeMember);              const DataType type = DataType::dataTypeOf(nativeMember);
883              const Member member(memberName, uids[0], offset, type);              const Member member(memberName, uids[0], offset, type);
# Line 573  namespace Serialization { Line 901  namespace Serialization {
901              }              }
902          }          }
903    
904            /** @brief Set current version number for your C++ class.
905             *
906             * By calling this method you can define a version number for your
907             * current C++ class (that is a version for its current data structure
908             * layout and method implementations) that is going to be stored along
909             * with the serialized archive. Only call this method if you really want
910             * to constrain compatibility of your C++ class.
911             *
912             * Along with calling @c setMinVersion() this provides a way for you
913             * to constrain backward compatibility regarding serialization and
914             * deserialization of your C++ class which the Archive class will obey
915             * to. If required, then typically you might do so in your
916             * @c serialize() method implementation like:
917             * @code
918             * #define SRLZ(member) \
919             *   archive->serializeMember(*this, member, #member);
920             *
921             * void Foo::serialize(Serialization::Archive* archive) {
922             *     // when serializing: the current version of this class that is
923             *     // going to be stored with the serialized archive
924             *     archive->setVersion(*this, 6);
925             *     // when deserializing: the minimum version this C++ class is
926             *     // compatible with
927             *     archive->setMinVersion(*this, 3);
928             *     // actual data mebers to serialize / deserialize
929             *     SRLZ(a);
930             *     SRLZ(b);
931             *     SRLZ(c);
932             * }
933             * @endcode
934             * In this example above, the C++ class "Foo" would be serialized along
935             * with the version number @c 6 and minimum version @c 3 as additional
936             * meta information in the resulting archive (and its raw data stream
937             * respectively).
938             *
939             * When deserializing archives with the example C++ class code above,
940             * the Archive object would check whether your originally serialized
941             * C++ "Foo" object had at least version number @c 3, if not the
942             * deserialization process would automatically be stopped with a
943             * @c Serialization::Exception, claiming that the classes are version
944             * incompatible.
945             *
946             * But also consider the other way around: you might have serialized
947             * your latest version of your C++ class, and might deserialize that
948             * archive with an older version of your C++ class. In that case it will
949             * likewise be checked whether the version of that old C++ class is at
950             * least as high as the minimum version set with the already seralized
951             * bleeding edge C++ class.
952             *
953             * Since this Serialization / deserialization framework is designed to
954             * be robust on changes to your C++ classes and aims trying to
955             * deserialize all your C++ objects correctly even if your C++ classes
956             * have seen substantial software changes in the meantime; you might
957             * sometimes see it as necessary to constrain backward compatibility
958             * this way. Because obviously there are certain things this framework
959             * can cope with, like for example that you renamed a data member while
960             * keeping the layout consistent, or that you have added new members to
961             * your C++ class or simply changed the order of your members in your
962             * C++ class. But what this framework cannot detect is for example if
963             * you changed the semantics of the values stored with your members, or
964             * even substantially changed the algorithms in your class methods such
965             * that they would not handle the data of your C++ members in the same
966             * and correct way anymore.
967             *
968             * @param nativeObject - your C++ object you want to set a version for
969             * @param v - the version number to set for your C++ class (by default,
970             *            that is if you do not explicitly call this method, then
971             *            your C++ object will be stored with version number @c 0 ).
972             */
973            template<typename T_classType>
974            void setVersion(const T_classType& nativeObject, Version v) {
975                const UID uid = UID::from(nativeObject);
976                Object& obj = m_allObjects[uid];
977                if (!obj) {
978                    const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
979                    const DataType type = DataType::dataTypeOf(nativeObject);
980                    obj = Object(uids, type);
981                }
982                setVersion(obj, v);
983            }
984    
985            /** @brief Set a minimum version number for your C++ class.
986             *
987             * Call this method to define a minimum version that your current C++
988             * class implementation would be compatible with when it comes to
989             * deserialization of an archive containing an object of your C++ class.
990             * Like the version information, the minimum version will also be stored
991             * for objects of your C++ class with the resulting archive (and its
992             * resulting raw data stream respectively).
993             *
994             * When you start to constrain version compatibility of your C++ class
995             * you usually start by using 1 as version and 1 as minimum version.
996             * So it is eligible to set the same number to both version and minimum
997             * version. However you must @b not set a minimum version higher than
998             * version. Doing so would not raise an exception, but the resulting
999             * behavior would be undefined.
1000             *
1001             * It is not relevant whether you first set version and then minimum
1002             * version or vice versa. It is also not relevant when exactly you set
1003             * those two numbers, even though usually you would set both in your
1004             * serialize() method implementation.
1005             *
1006             * @see @c setVersion() for more details about this overall topic.
1007             *
1008             * @param nativeObject - your C++ object you want to set a version for
1009             * @param v - the minimum version you want to define for your C++ class
1010             *            (by default, that is if you do not explicitly call this
1011             *            method, then a minium version of @c 0 is assumed for your
1012             *            C++ class instead).
1013             */
1014            template<typename T_classType>
1015            void setMinVersion(const T_classType& nativeObject, Version v) {
1016                const UID uid = UID::from(nativeObject);
1017                Object& obj = m_allObjects[uid];
1018                if (!obj) {
1019                    const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
1020                    const DataType type = DataType::dataTypeOf(nativeObject);
1021                    obj = Object(uids, type);
1022                }
1023                setMinVersion(obj, v);
1024            }
1025    
1026          virtual void decode(const RawData& data);          virtual void decode(const RawData& data);
1027          virtual void decode(const uint8_t* data, size_t size);          virtual void decode(const uint8_t* data, size_t size);
1028          void clear();          void clear();
# Line 587  namespace Serialization { Line 1037  namespace Serialization {
1037          void setBoolValue(Object& object, bool value);          void setBoolValue(Object& object, bool value);
1038          void setEnumValue(Object& object, uint64_t value);          void setEnumValue(Object& object, uint64_t value);
1039          String valueAsString(const Object& object);          String valueAsString(const Object& object);
1040            int64_t valueAsInt(const Object& object);
1041            double valueAsReal(const Object& object);
1042            bool valueAsBool(const Object& object);
1043            void setVersion(Object& object, Version v);
1044            void setMinVersion(Object& object, Version v);
1045            String name() const;
1046            void setName(String name);
1047            String comment() const;
1048            void setComment(String comment);
1049            time_t timeStampCreated() const;
1050            time_t timeStampModified() const;
1051            tm dateTimeCreated(time_base_t base = LOCAL_TIME) const;
1052            tm dateTimeModified(time_base_t base = LOCAL_TIME) const;
1053    
1054      protected:      protected:
1055          // UID resolver for non-pointer types          // UID resolver for non-pointer types
# Line 608  namespace Serialization { Line 1071  namespace Serialization {
1071          class UIDChainResolver<T*> {          class UIDChainResolver<T*> {
1072          public:          public:
1073              UIDChainResolver(const T*& data) {              UIDChainResolver(const T*& data) {
1074                  m_uid.push_back((UID) { &data, sizeof(data) });                  const UID uids[2] = {
1075                  m_uid.push_back((UID) { data, sizeof(*data) });                      { &data, sizeof(data) },
1076                        { data, sizeof(*data) }
1077                    };
1078                    m_uid.push_back(uids[0]);
1079                    m_uid.push_back(uids[1]);
1080              }              }
1081    
1082              operator UIDChain() const { return m_uid; }              operator UIDChain() const { return m_uid; }
# Line 649  namespace Serialization { Line 1116  namespace Serialization {
1116    
1117          // Automatically handles recursion for class/struct types, while ignoring all primitive types.          // Automatically handles recursion for class/struct types, while ignoring all primitive types.
1118          template<typename T>          template<typename T>
1119          struct SerializationRecursion : SerializationRecursionImpl<T, __is_class(T)> {          struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> {
1120          };          };
1121    
1122          class ObjectPool : public std::map<UID,Object> {          class ObjectPool : public std::map<UID,Object> {
# Line 687  namespace Serialization { Line 1154  namespace Serialization {
1154              Archive& m_src;              Archive& m_src;
1155          };          };
1156    
1157            enum operation_t {
1158                OPERATION_NONE,
1159                OPERATION_SERIALIZE,
1160                OPERATION_DESERIALIZE
1161            };
1162    
1163          virtual void encode();          virtual void encode();
1164    
1165          ObjectPool m_allObjects;          ObjectPool m_allObjects;
# Line 694  namespace Serialization { Line 1167  namespace Serialization {
1167          UID m_root;          UID m_root;
1168          RawData m_rawData;          RawData m_rawData;
1169          bool m_isModified;          bool m_isModified;
1170            String m_name;
1171            String m_comment;
1172            time_t m_timeCreated;
1173            time_t m_timeModified;
1174      };      };
1175    
1176      /**      /**
# Line 704  namespace Serialization { Line 1181  namespace Serialization {
1181          public:          public:
1182              String Message;              String Message;
1183    
1184              Exception(String Message) { Exception::Message = Message; }              Exception(String format, ...);
1185                Exception(String format, va_list arg);
1186              void PrintMessage();              void PrintMessage();
1187              virtual ~Exception() {}              virtual ~Exception() {}
1188    
1189            protected:
1190                Exception();
1191                static String assemble(String format, va_list arg);
1192      };      };
1193    
1194  } // namespace Serialization  } // namespace Serialization

Legend:
Removed from v.3153  
changed lines
  Added in v.3476

  ViewVC Help
Powered by ViewVC