/[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 3774 by schoenebeck, Tue May 19 14:55:48 2020 UTC revision 3775 by schoenebeck, Tue May 19 15:23:11 2020 UTC
# Line 37  Line 37 
37  #include <time.h>  #include <time.h>
38  #include <stdarg.h>  #include <stdarg.h>
39  #include <assert.h>  #include <assert.h>
40    #include <functional>
41    
42  #ifndef __has_extension  #ifndef __has_extension
43  # define __has_extension(x) 0  # define __has_extension(x) 0
# Line 118  namespace Serialization { Line 119  namespace Serialization {
119      class ObjectPool;      class ObjectPool;
120      class Exception;      class Exception;
121    
122        /** @brief Textual string.
123         *
124         * This type is used for built-in automatic serialization / deserialization
125         * of C++ @c String objects (a.k.a. @c std::string from the STL). This
126         * framework supports serializing this common data type out of the box and
127         * is handled by this framework as it was a primitive C++ data type.
128         */
129      typedef std::string String;      typedef std::string String;
130    
131        /** @brief Array<> template.
132         *
133         * This type is used for built-in automatic serialization / deserialization
134         * of C++ array containers (a.k.a. @c std::vector from the STL). This
135         * framework supports serializing this common data type out of the box, with
136         * only one constraint: the precise element type used with arrays must be
137         * serializable. So the array's element type should either be a) any
138         * primitive data type (e.g. @c int, @c double, etc.) or b) any other data
139         * structure or class types enjoying out of the box serialization support by
140         * this framework, or c) if it is a custom @c struct or @c class then it
141         * must have a @c serialize() method implementation.
142         */
143        template<class T>
144        using Array = std::vector<T>;
145    
146      /** @brief Raw data stream of serialized C++ objects.      /** @brief Raw data stream of serialized C++ objects.
147       *       *
148       * This data type is used for the data stream as a result of serializing       * This data type is used for the data stream as a result of serializing
# Line 224  namespace Serialization { Line 247  namespace Serialization {
247          return __is_pod(T);          return __is_pod(T);
248      }*/      }*/
249    
250        /*template<typename T>
251        bool IsArray(const T& data) {
252            return false;
253        }*/
254    
255        /*template<typename T>
256        bool IsArray(const Array<T>& data) {
257            return true;
258        }*/
259    
260      /** @brief Unique identifier referring to one specific native C++ object, member, fundamental variable, or any other native C++ data.      /** @brief Unique identifier referring to one specific native C++ object, member, fundamental variable, or any other native C++ data.
261       *       *
262       * Reflects a unique identifier for one specific serialized C++ data, i.e.       * Reflects a unique identifier for one specific serialized C++ data, i.e.
# Line 366  namespace Serialization { Line 399  namespace Serialization {
399          bool isReal() const;          bool isReal() const;
400          bool isBool() const;          bool isBool() const;
401          bool isEnum() const;          bool isEnum() const;
402            bool isArray() const;
403          bool isSigned() const;          bool isSigned() const;
404          operator bool() const { return isValid(); } ///< Same as calling isValid().          operator bool() const { return isValid(); } ///< Same as calling isValid().
405          //bool operator()() const { return isValid(); }          //bool operator()() const { return isValid(); }
# Line 455  namespace Serialization { Line 489  namespace Serialization {
489              }              }
490          };          };
491    
492            // DataType resolver for non-pointer Array<> container object types.
493            template<typename T>
494            struct Resolver<Array<T>> {
495                static DataType resolve(const Array<T>& data) {
496                    const int sz = sizeof(data);
497                    T unused;
498                    return DataType(false, sz, "Array", rawCppTypeNameOf(unused));
499                }
500            };
501    
502            // DataType resolver for Array<> pointer types (of 1st degree).
503            template<typename T>
504            struct Resolver<Array<T>*> {
505                static DataType resolve(const Array<T>*& data) {
506                    const int sz = sizeof(*data);
507                    T unused;
508                    return DataType(true, sz, "Array", rawCppTypeNameOf(unused));
509                }
510            };
511    
512          template<typename T>          template<typename T>
513          static String rawCppTypeNameOf(const T& data) {          static String rawCppTypeNameOf(const T& data) {
514              #if defined _MSC_VER // Microsoft compiler ...              #if defined _MSC_VER // Microsoft compiler ...
# Line 505  namespace Serialization { Line 559  namespace Serialization {
559          Member();          Member();
560          UID uid() const;          UID uid() const;
561          String name() const;          String name() const;
562          size_t offset() const;          ssize_t offset() const;
563          const DataType& type() const;          const DataType& type() const;
564          bool isValid() const;          bool isValid() const;
565          operator bool() const { return isValid(); } ///< Same as calling isValid().          operator bool() const { return isValid(); } ///< Same as calling isValid().
# Line 516  namespace Serialization { Line 570  namespace Serialization {
570          bool operator>(const Member& other) const;          bool operator>(const Member& other) const;
571    
572      protected:      protected:
573          Member(String name, UID uid, size_t offset, DataType type);          Member(String name, UID uid, ssize_t offset, DataType type);
574          friend class Archive;          friend class Archive;
575    
576      private:      private:
577          UID m_uid;          UID m_uid;
578          size_t m_offset;          ssize_t m_offset;
579          String m_name;          String m_name;
580          DataType m_type;          DataType m_type;
581    
# Line 592  namespace Serialization { Line 646  namespace Serialization {
646          Version m_minVersion;          Version m_minVersion;
647          RawData m_data;          RawData m_data;
648          std::vector<Member> m_members;          std::vector<Member> m_members;
649            std::function<void(Object& dstObj, const Object& srcObj, void* syncer)> m_sync;
650    
651  #if LIBGIG_SERIALIZATION_INTERNAL  #if LIBGIG_SERIALIZATION_INTERNAL
652          friend String _encodePrimitiveValue(const Object& obj);          friend String _encodePrimitiveValue(const Object& obj);
# Line 865  namespace Serialization { Line 920  namespace Serialization {
920           * #define SRLZ(member) \           * #define SRLZ(member) \
921           *   archive->serializeMember(*this, member, #member);           *   archive->serializeMember(*this, member, #member);
922           *           *
923             * struct Foo {
924             *     int a;
925             *     Bar b; // a custom struct or class having a serialize() method
926             *     std::string c;
927             *     std::vector<double> d;
928             *
929             *     void serialize(Serialization::Archive* archive);
930             * };
931             *
932           * void Foo::serialize(Serialization::Archive* archive) {           * void Foo::serialize(Serialization::Archive* archive) {
933           *     SRLZ(a);           *     SRLZ(a);
934           *     SRLZ(b);           *     SRLZ(b);
935           *     SRLZ(c);           *     SRLZ(c);
936             *     SRLZ(d);
937           * }           * }
938           * @endcode           * @endcode
939           * As you can see, using such a macro makes your code more readable and           * As you can see, using such a macro makes your code more readable,
940           * less error prone.           * compact and less error prone.
941           *           *
942           * It is completely up to you to decide which ones of your member           * It is completely up to you to decide which ones of your member
943           * variables shall automatically be serialized and deserialized with           * variables shall automatically be serialized and deserialized with
# Line 897  namespace Serialization { Line 962  namespace Serialization {
962           *                       deserialization           *                       deserialization
963           * @param memberName - name of @a nativeMember to be stored with this           * @param memberName - name of @a nativeMember to be stored with this
964           *                     archive           *                     archive
965             * @see serializeHeapMember() for variables on the RAM heap
966           */           */
967          template<typename T_classType, typename T_memberType>          template<typename T_classType, typename T_memberType>
968          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) {
969              const size_t offset =              const ssize_t offset =
970                  ((const uint8_t*)(const void*)&nativeMember) -                  ((const uint8_t*)(const void*)&nativeMember) -
971                  ((const uint8_t*)(const void*)&nativeObject);                  ((const uint8_t*)(const void*)&nativeObject);
972              const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);              const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);
# Line 926  namespace Serialization { Line 992  namespace Serialization {
992              }              }
993          }          }
994    
995            /** @brief Serialize a C/C++ member variable allocated on the heap.
996             *
997             * This method is essentially used by applications in the same way as
998             * @c serializeMember() above, however @c serializeMember() must only be
999             * used for native C/C++ members which are variables that are memory
1000             * located within their owning parent data structures. For any variable
1001             * that's located on the RAM heap though, applications must use this
1002             * method instead to make it clear that there's no constant, static
1003             * offset relationship between the passed member and its owning parent.
1004             *
1005             * @discussion To avoid member name conflicts with native members, it is
1006             * recommended to always choose member names which would be impossible
1007             * as names to be declared in C/C++ code. For instance this framework
1008             * uses heap member names "[0]", "[1]", "[3]", ... in its out of the box
1009             * support when serializing elements of @c Array<> objects, since
1010             * brackets in general cannot be used as part of variable names in C++,
1011             * so using such or other special characters in heap member names, makes
1012             * such naming conflicts impossible.
1013             *
1014             * @param nativeObject - native C++ object to be registered for
1015             *                       serialization / deserialization
1016             * @param heapMember - C/C++ variable (located on the heap) of
1017             *                     @a nativeObject to be registered for
1018             *                     serialization / deserialization
1019             * @param memberName - name of @a heapMember to be stored with this
1020             *                     archive; an arbitrary but unique name should be
1021             *                     chosen which must not collide with names of
1022             *                     native members (see discussion above)
1023             * @see serializeMember() for native member variables
1024             */
1025            template<typename T_classType, typename T_memberType>
1026            void serializeHeapMember(const T_classType& nativeObject, const T_memberType& heapMember, const char* memberName) {
1027                const ssize_t offset = -1; // used for all members on heap
1028                const UIDChain uids = UIDChainResolver<T_memberType>(heapMember);
1029                const DataType type = DataType::dataTypeOf(heapMember);
1030                const Member member(memberName, uids[0], offset, type);
1031                const UID parentUID = UID::from(nativeObject);
1032                Object& parent = m_allObjects[parentUID];
1033                if (!parent) {
1034                    const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
1035                    const DataType type = DataType::dataTypeOf(nativeObject);
1036                    parent = Object(uids, type);
1037                }
1038                parent.members().push_back(member);
1039                const Object obj(uids, type);
1040                const bool bExistsAlready = m_allObjects.count(uids[0]);
1041                const bool isValidObject = obj;
1042                const bool bExistingObjectIsInvalid = !m_allObjects[uids[0]];
1043                if (!bExistsAlready || (bExistingObjectIsInvalid && isValidObject)) {
1044                    m_allObjects[uids[0]] = obj;
1045                    // recurse serialization for all members of this member
1046                    // (only for struct/class types, noop for primitive types)
1047                    SerializationRecursion<T_memberType>::serializeObject(this, heapMember);
1048                }
1049            }
1050    
1051          /** @brief Set current version number for your C++ class.          /** @brief Set current version number for your C++ class.
1052           *           *
1053           * By calling this method you can define a version number for your           * By calling this method you can define a version number for your
# Line 943  namespace Serialization { Line 1065  namespace Serialization {
1065           * #define SRLZ(member) \           * #define SRLZ(member) \
1066           *   archive->serializeMember(*this, member, #member);           *   archive->serializeMember(*this, member, #member);
1067           *           *
1068             * struct Foo {
1069             *     int a;
1070             *     Bar b; // a custom struct or class having a serialize() method
1071             *     std::string c;
1072             *     std::vector<double> d;
1073             *
1074             *     void serialize(Serialization::Archive* archive);
1075             * };
1076             *
1077           * void Foo::serialize(Serialization::Archive* archive) {           * void Foo::serialize(Serialization::Archive* archive) {
1078           *     // when serializing: the current version of this class that is           *     // when serializing: the current version of this class that is
1079           *     // going to be stored with the serialized archive           *     // going to be stored with the serialized archive
# Line 954  namespace Serialization { Line 1085  namespace Serialization {
1085           *     SRLZ(a);           *     SRLZ(a);
1086           *     SRLZ(b);           *     SRLZ(b);
1087           *     SRLZ(c);           *     SRLZ(c);
1088             *     SRLZ(d);
1089           * }           * }
1090           * @endcode           * @endcode
1091           * In this example above, the C++ class "Foo" would be serialized along           * In this example above, the C++ class "Foo" would be serialized along
# Line 1153  namespace Serialization { Line 1285  namespace Serialization {
1285              static void serializeObject(Archive* archive, const String*& obj) {}              static void serializeObject(Archive* archive, const String*& obj) {}
1286          };          };
1287    
1288            // SerializationRecursion for Array<> objects.
1289            template<typename T, bool T_isRecursive>
1290            struct SerializationRecursionImpl<Array<T>,T_isRecursive> {
1291                static void serializeObject(Archive* archive, const Array<T>& obj) {
1292                    const UIDChain uids = UIDChainResolver<Array<T>>(obj);
1293                    const Object& object = archive->objectByUID(uids[0]);
1294                    if (archive->operation() == OPERATION_SERIALIZE) {
1295                        for (size_t i = 0; i < obj.size(); ++i) {
1296                            archive->serializeHeapMember(
1297                                obj, obj[i], ("[" + std::to_string(i) + "]").c_str()
1298                            );
1299                        }
1300                    } else {
1301                        const_cast<Object&>(object).m_sync =
1302                            [&obj,archive](Object& dstObj, const Object& srcObj,
1303                                           void* syncer)
1304                        {
1305                            const size_t n = srcObj.members().size();
1306                            const_cast<Array<T>&>(obj).resize(n);
1307                            for (size_t i = 0; i < obj.size(); ++i) {
1308                                archive->serializeHeapMember(
1309                                    obj, obj[i], ("[" + std::to_string(i) + "]").c_str()
1310                                );
1311                            }
1312                            // updating dstObj required as serializeHeapMember()
1313                            // replaced the original object by a new one
1314                            dstObj = archive->objectByUID(dstObj.uid());
1315                            for (size_t i = 0; i < obj.size(); ++i) {
1316                                String name = "[" + std::to_string(i) + "]";
1317                                Member srcMember = srcObj.memberNamed(name);
1318                                Member dstMember = dstObj.memberNamed(name);
1319                                ((Syncer*)syncer)->syncMember(dstMember, srcMember);
1320                            }
1321                        };
1322                    }
1323                }
1324            };
1325    
1326            // SerializationRecursion for Array<> pointers (of 1st degree).
1327            template<typename T, bool T_isRecursive>
1328            struct SerializationRecursionImpl<Array<T>*,T_isRecursive> {
1329                static void serializeObject(Archive* archive, const Array<T>*& obj) {
1330                    if (!obj) return;
1331                    SerializationRecursionImpl<Array<T>,T_isRecursive>::serializeObject(
1332                        archive, *obj
1333                    );
1334                }
1335            };
1336    
1337          // Automatically handles recursion for class/struct types, while ignoring all primitive types.          // Automatically handles recursion for class/struct types, while ignoring all primitive types.
1338          template<typename T>          template<typename T>
1339          struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> {          struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> {
# Line 1179  namespace Serialization { Line 1360  namespace Serialization {
1360          void _popObjectsBlob(const char*& p, const char* end);          void _popObjectsBlob(const char*& p, const char* end);
1361    
1362      protected:      protected:
1363            /** @brief Synchronizes 2 archives with each other.
1364             *
1365             * This class is used internally at final stage of deserialization. It
1366             * is not used for serialization.
1367             *
1368             * The deserialization algorithm of this framework works like this:
1369             *
1370             * 1. The (currently running) application constructs an @c Archive
1371             *    object by passing a previously serialized raw data stream to the
1372             *    @c Archive constructor. At this stage, the raw data stream is
1373             *    decoded and this archive's object pool is populated with objects,
1374             *    which in turn are filled with data (at temporary storage
1375             *    location inside the respective @c Object instances) of the decoded
1376             *    data stream.
1377             *
1378             * 2. A temporary (2nd) @c Archive object is constructed internally by
1379             *    this framework, reflecting the (currently running) application's
1380             *    latest data structre layout, without touching any actual data yet.
1381             *    The individual @c Object instances of this 2nd @c Archive are
1382             *    bound to the running application's native (target) C/C++ member
1383             *    variables to be updated (written to) next.
1384             *
1385             * Note that at this point, the 2 archives might very well have quite
1386             * different data structure layouts, due to potential software changes
1387             * between the original serializing application and this currently
1388             * deserializing software application.
1389             *
1390             * 3. This Syncer class is used to transfer the actual data from the 1st
1391             *    to the 2nd (temporary) @c Archive object, it does so by traversing
1392             *    the 2 archives' object pools, trying to find the respective 2
1393             *    objects on the two sides to be synchronized, and if found, it
1394             *    transfers the data from the 1st archive's object to the 2nd
1395             *    archive's object, effectively writing to the currently running
1396             *    application's final C/C++ memory locations.
1397             *
1398             * This 3 staged approach allows to deserialize data in a much more
1399             * relaxed, adaptive and flexible (while still automatic) way than
1400             * traditional serialization frameworks would do.
1401             */
1402          class Syncer {          class Syncer {
1403          public:          public:
1404              Syncer(Archive& dst, Archive& src);              Syncer(Archive& dst, Archive& src);
         protected:  
1405              void syncObject(const Object& dst, const Object& src);              void syncObject(const Object& dst, const Object& src);
1406              void syncPrimitive(const Object& dst, const Object& src);              void syncPrimitive(const Object& dst, const Object& src);
1407              void syncString(const Object& dst, const Object& src);              void syncString(const Object& dst, const Object& src);
1408                void syncArray(const Object& dst, const Object& src);
1409              void syncPointer(const Object& dst, const Object& src);              void syncPointer(const Object& dst, const Object& src);
1410              void syncMember(const Member& dstMember, const Member& srcMember);              void syncMember(const Member& dstMember, const Member& srcMember);
1411            protected:
1412              static Member dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember);              static Member dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember);
1413          private:          private:
1414              Archive& m_dst;              Archive& m_dst;

Legend:
Removed from v.3774  
changed lines
  Added in v.3775

  ViewVC Help
Powered by ViewVC