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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3142 - (hide annotations) (download) (as text)
Wed May 3 17:37:33 2017 UTC (6 years, 11 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 26645 byte(s)
- Serialization.h: Just Doxygen fixes.

1 schoenebeck 3138 /***************************************************************************
2     * *
3     * Copyright (C) 2017 Christian Schoenebeck *
4     * <cuse@users.sourceforge.net> *
5     * *
6     * This library is part of libgig. *
7     * *
8     * This library is free software; you can redistribute it and/or modify *
9     * it under the terms of the GNU General Public License as published by *
10     * the Free Software Foundation; either version 2 of the License, or *
11     * (at your option) any later version. *
12     * *
13     * This library is distributed in the hope that it will be useful, *
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16     * GNU General Public License for more details. *
17     * *
18     * You should have received a copy of the GNU General Public License *
19     * along with this library; if not, write to the Free Software *
20     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21     * MA 02111-1307 USA *
22     ***************************************************************************/
23    
24     #ifndef LIBGIG_SERIALIZATION_H
25     #define LIBGIG_SERIALIZATION_H
26    
27     #ifdef HAVE_CONFIG_H
28     # include <config.h>
29     #endif
30    
31     #include <stdint.h>
32     #include <stdio.h>
33     #include <typeinfo>
34     #include <string>
35     #include <vector>
36     #include <map>
37    
38     /** @brief Serialization / deserialization framework.
39     *
40     * See class Archive as starting point for how to implement serialization and
41     * deserialization with your application.
42     *
43     * The classes in this namespace allow to serialize and deserialize native
44     * C++ objects in a portable, easy and flexible way. Serialization is a
45     * technique that allows to transform the current state and data of native
46     * (in this case C++) objects into a data stream (including all other objects
47     * the "serialized" objects relate to); the data stream may then be sent over
48     * "wire" (for example via network connection to another computer, which might
49     * also have a different OS, CPU architecture, native memory word size and
50     * endian type); and finally the data stream would be "deserialized" on that
51     * receiver side, that is transformed again to modify all objects and data
52     * structures on receiver side to resemble the objects' state and data as it
53     * was originally on sender side.
54     *
55     * In contrast to many other already existing serialization frameworks, this
56     * implementation has a strong focus on robustness regarding long-term changes
57     * to the serialized C++ classes of the serialized objects. So even if sender
58     * and receiver are using different versions of their serialized/deserialized
59     * C++ classes, structures and data types (thus having different data structure
60     * layout to a certain extent), this framework aims trying to automatically
61     * adapt its serialization and deserialization process in that case so that
62     * the deserialized objects on receiver side would still reflect the overall
63     * expected states and overall data as intended by the sender. For being able to
64     * do so, this framework stores all kind of additional information about each
65     * serialized object and each data structure member (for example name of each
66     * data structure member, but also the offset of each member within its
67     * containing data structure, precise data types, and more).
68     *
69     * Like most other serialization frameworks, this frameworks does not require a
70     * tree-structured layout of the serialized data structures. So it automatically
71     * handles also cyclic dependencies between serialized data structures
72     * correctly, without i.e. causing endless recursion or redundancy.
73     *
74     * Additionally this framework also allows partial deserialization. Which means
75     * the receiver side may for example decide that it wants to restrict
76     * deserialization so that it would only modify certain objects or certain
77     * members by the deserialization process, leaving all other ones untouched.
78     * So this partial deserialization technique for example allows to implement
79     * flexible preset features for applications in a powerful and easy way.
80     */
81     namespace Serialization {
82    
83     class Archive;
84     class Exception;
85    
86     typedef std::string String;
87    
88     typedef std::vector<uint8_t> RawData;
89    
90     typedef void* ID;
91    
92     typedef uint32_t Version;
93    
94     enum operation_t {
95     OPERATION_NONE,
96     OPERATION_SERIALIZE,
97     OPERATION_DESERIALIZE
98     };
99    
100     template<typename T>
101     bool IsEnum(const T& data) {
102     return __is_enum(T);
103     }
104    
105     template<typename T>
106     bool IsUnion(const T& data) {
107     return __is_union(T);
108     }
109    
110     template<typename T>
111     bool IsClass(const T& data) {
112     return __is_class(T);
113     }
114    
115     /*template<typename T>
116     bool IsTrivial(T data) {
117     return __is_trivial(T);
118     }*/
119    
120     /*template<typename T>
121     bool IsPOD(T data) {
122     return __is_pod(T);
123     }*/
124    
125     /** @brief Unique identifier for one specific C++ object, member or fundamental variable.
126     *
127     * Reflects a unique identifier for one specific serialized C++ class
128     * instance, struct instance, member, primitive pointer, or fundamental
129     * variables.
130     */
131     class UID {
132     public:
133     ID id;
134     size_t size;
135    
136     bool isValid() const;
137     operator bool() const { return isValid(); }
138     //bool operator()() const { return isValid(); }
139     bool operator==(const UID& other) const { return id == other.id && size == other.size; }
140     bool operator!=(const UID& other) const { return id != other.id || size != other.size; }
141     bool operator<(const UID& other) const { return id < other.id || (id == other.id && size < other.size); }
142     bool operator>(const UID& other) const { return id > other.id || (id == other.id && size > other.size); }
143    
144     template<typename T>
145     static UID from(const T& obj) {
146     return Resolver<T>::resolve(obj);
147     }
148    
149     protected:
150     // UID resolver for non-pointer types
151     template<typename T>
152     struct Resolver {
153     static UID resolve(const T& obj) {
154     return (UID) { (ID) &obj, sizeof(obj) };
155     }
156     };
157    
158     // UID resolver for pointer types (of 1st degree)
159     template<typename T>
160     struct Resolver<T*> {
161     static UID resolve(const T* const & obj) {
162     return (UID) { (ID) obj, sizeof(*obj) };
163     }
164     };
165     };
166    
167     /**
168     * Reflects an invalid UID and behaves similar to NULL as invalid value for
169     * pointer types.
170     */
171     extern const UID NO_UID;
172    
173     typedef std::vector<UID> UIDChain;
174    
175     /** @brief Abstract reflection of a native C++ data type.
176     *
177     * Provides detailed information about a C++ data type, whether it is a
178     * fundamental C/C++ data type (like int, float, char, etc.) or custom
179     * defined data type like a C++ class, struct, enum, as well as other
180     * features of the data type like its native memory size and more.
181     */
182     class DataType {
183     public:
184     DataType();
185     size_t size() const { return m_size; }
186     bool isValid() const;
187     bool isPointer() const;
188     bool isClass() const;
189     bool isPrimitive() const;
190     bool isInteger() const;
191     bool isReal() const;
192     bool isBool() const;
193     bool isEnum() const;
194     bool isSigned() const;
195     operator bool() const { return isValid(); }
196     //bool operator()() const { return isValid(); }
197     bool operator==(const DataType& other) const;
198     bool operator!=(const DataType& other) const;
199     bool operator<(const DataType& other) const;
200     bool operator>(const DataType& other) const;
201     String asLongDescr() const;
202     String baseTypeName() const { return m_baseTypeName; }
203     String customTypeName() const { return m_customTypeName; }
204    
205     template<typename T>
206     static DataType dataTypeOf(const T& data) {
207     return Resolver<T>::resolve(data);
208     }
209    
210     protected:
211     DataType(bool isPointer, int size, String baseType, String customType = "");
212    
213     template<typename T, bool T_isPointer>
214     struct ResolverBase {
215     static DataType resolve(const T& data) {
216     const std::type_info& type = typeid(data);
217     const int sz = sizeof(data);
218    
219     // for primitive types we are using our own type names instead of
220     // using std:::type_info::name(), because the precise output of the
221     // latter may vary between compilers
222     if (type == typeid(int8_t)) return DataType(T_isPointer, sz, "int8");
223     if (type == typeid(uint8_t)) return DataType(T_isPointer, sz, "uint8");
224     if (type == typeid(int16_t)) return DataType(T_isPointer, sz, "int16");
225     if (type == typeid(uint16_t)) return DataType(T_isPointer, sz, "uint16");
226     if (type == typeid(int32_t)) return DataType(T_isPointer, sz, "int32");
227     if (type == typeid(uint32_t)) return DataType(T_isPointer, sz, "uint32");
228     if (type == typeid(int64_t)) return DataType(T_isPointer, sz, "int64");
229     if (type == typeid(uint64_t)) return DataType(T_isPointer, sz, "uint64");
230     if (type == typeid(bool)) return DataType(T_isPointer, sz, "bool");
231     if (type == typeid(float)) return DataType(T_isPointer, sz, "real32");
232     if (type == typeid(double)) return DataType(T_isPointer, sz, "real64");
233    
234     if (IsEnum(data)) return DataType(T_isPointer, sz, "enum", rawCppTypeNameOf(data));
235     if (IsUnion(data)) return DataType(T_isPointer, sz, "union", rawCppTypeNameOf(data));
236     if (IsClass(data)) return DataType(T_isPointer, sz, "class", rawCppTypeNameOf(data));
237    
238     return DataType();
239     }
240     };
241    
242     // DataType resolver for non-pointer types
243     template<typename T>
244     struct Resolver : ResolverBase<T,false> {
245     static DataType resolve(const T& data) {
246     return ResolverBase<T,false>::resolve(data);
247     }
248     };
249    
250     // DataType resolver for pointer types (of 1st degree)
251     template<typename T>
252     struct Resolver<T*> : ResolverBase<T,true> {
253     static DataType resolve(const T*& data) {
254     return ResolverBase<T,true>::resolve(*data);
255     }
256     };
257    
258     template<typename T>
259     static String rawCppTypeNameOf(const T& data) {
260     #if defined _MSC_VER // Microsoft compiler ...
261     # warning type_info::raw_name() demangling has not been tested yet with Microsoft compiler! Feedback appreciated!
262     String name = typeid(data).raw_name(); //NOTE: I haven't checked yet what MSC actually outputs here exactly
263     #else // i.e. especially GCC and clang ...
264     String name = typeid(data).name();
265     #endif
266     //while (!name.empty() && name[0] >= 0 && name[0] <= 9)
267     // name = name.substr(1);
268     return name;
269     }
270    
271     private:
272     String m_baseTypeName;
273     String m_customTypeName;
274     int m_size;
275     bool m_isPointer;
276    
277     friend DataType _popDataTypeBlob(const char*& p, const char* end);
278     };
279    
280     /** @brief Abstract reflection of a native C++ class/struct's member variable.
281     *
282     * Provides detailed information about a specific C++ member variable of
283     * serialized C++ object, like its C++ data type, offset of this member
284     * within its containing data structure/class, its C++ member variable name
285     * and more.
286     */
287     class Member {
288     public:
289     Member();
290     UID uid() const { return m_uid; }
291     String name() const { return m_name; }
292     size_t offset() const { return m_offset; }
293     const DataType& type() const { return m_type; }
294     bool isValid() const;
295     operator bool() const { return isValid(); }
296     //bool operator()() const { return isValid(); }
297     bool operator==(const Member& other) const;
298     bool operator!=(const Member& other) const;
299     bool operator<(const Member& other) const;
300     bool operator>(const Member& other) const;
301    
302     protected:
303     Member(String name, UID uid, size_t offset, DataType type);
304     friend class Archive;
305    
306     private:
307     UID m_uid;
308     size_t m_offset;
309     String m_name;
310     DataType m_type;
311    
312     friend Member _popMemberBlob(const char*& p, const char* end);
313     };
314    
315     /** @brief Abstract reflection of a native C++ class/struct instance.
316     *
317     * Provides detailed information about a specific serialized C++ object,
318     * like its C++ member variables, its C++ class/struct name, its native
319     * memory size and more.
320     */
321     class Object {
322     public:
323     Object();
324     Object(UIDChain uidChain, DataType type);
325    
326     UID uid(int index = 0) const {
327     return (index < m_uid.size()) ? m_uid[index] : NO_UID;
328     }
329    
330     const UIDChain& uidChain() const { return m_uid; }
331     const DataType& type() const { return m_type; }
332     const RawData& rawData() const { return m_data; }
333    
334     Version version() const { return m_version; }
335    
336     void setVersion(Version v) {
337     m_version = v;
338     }
339    
340     Version minVersion() const { return m_minVersion; }
341    
342     void setMinVersion(Version v) {
343     m_minVersion = v;
344     }
345    
346     bool isVersionCompatibleTo(const Object& other) const;
347    
348     std::vector<Member>& members() { return m_members; }
349     const std::vector<Member>& members() const { return m_members; }
350     Member memberNamed(String name) const;
351     void remove(const Member& member);
352     std::vector<Member> membersOfType(const DataType& type) const;
353     int sequenceIndexOf(const Member& member) const;
354     bool isValid() const;
355     operator bool() const { return isValid(); }
356     //bool operator()() const { return isValid(); }
357     bool operator==(const Object& other) const;
358     bool operator!=(const Object& other) const;
359     bool operator<(const Object& other) const;
360     bool operator>(const Object& other) const;
361    
362     private:
363     DataType m_type;
364     UIDChain m_uid;
365     Version m_version;
366     Version m_minVersion;
367     RawData m_data;
368     std::vector<Member> m_members;
369    
370     friend Object _popObjectBlob(const char*& p, const char* end);
371     friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
372     };
373    
374     /** @brief Destination container for serialization, and source container for deserialization.
375     *
376     * This is the main class for implementing serialization and deserialization
377     * with your C++ application. This framework does not require a a tree
378     * structured layout of your C++ objects being serialized/deserialized, it
379     * uses a concept of a "root" object though. So to start serialization
380     * construct an empty Archive object and then instruct it to serialize your
381     * C++ objects by pointing it to your "root" object:
382     * @code
383     * Archive a;
384     * a.serialize(&myRootObject);
385 schoenebeck 3142 * @endcode
386 schoenebeck 3138 * Or if you prefer the look of operator based code:
387     * @code
388     * Archive a;
389     * a << myRootObject;
390 schoenebeck 3142 * @endcode
391 schoenebeck 3138 * The Archive object will then serialize all members of the passed C++
392     * object, and will recursively serialize all other C++ objects which it
393     * contains or points to. So the root object is the starting point for the
394     * overall serialization. After the serialize() method returned, you can
395     * then access the serialized data stream by calling rawData() and send that
396     * data stream over "wire", or store it on disk or whatever you may intend
397     * to do with it.
398     *
399     * Then on receiver side likewise, you create a new Archive object, pass the
400     * received data stream i.e. via constructor to the Archive object and call
401     * deserialize() by pointing it to the root object on receiver side:
402     * @code
403     * Archive a(rawDataStream);
404     * a.deserialize(&myRootObject);
405 schoenebeck 3142 * @endcode
406 schoenebeck 3138 * Or with operator instead:
407     * @code
408     * Archive a(rawDataStream);
409     * a >> myRootObject;
410 schoenebeck 3142 * @endcode
411 schoenebeck 3138 * Now this framework automatically handles serialization and
412     * deserialization of fundamental data types automatically for you (like
413     * i.e. char, int, long int, float, double, etc.). However for your own
414     * custom C++ classes and structs you must implement one method which
415     * defines which members of your class should actually be serialized and
416     * deserialized. That method to be added must have the following signature:
417     * @code
418     * void serialize(Serialization::Archive* archive);
419     * @endcode
420     * So let's say you have the following simple data structures:
421     * @code
422     * struct Foo {
423     * int a;
424     * bool b;
425     * double c;
426     * };
427     *
428     * struct Bar {
429     * char one;
430     * float two;
431     * Foo foo1;
432     * Foo* pFoo2;
433     * Foo* pFoo3DontTouchMe; // shall not be serialized/deserialized
434     * };
435     * @endcode
436     * So in order to be able to serialize and deserialize objects of those two
437     * structures you would first add the mentioned method to each struct
438     * definition (i.e. in your header file):
439     * @code
440     * struct Foo {
441     * int a;
442     * bool b;
443     * double c;
444     *
445     * void serialize(Serialization::Archive* archive);
446     * };
447     *
448     * struct Bar {
449     * char one;
450     * float two;
451     * Foo foo1;
452     * Foo* pFoo2;
453     * Foo* pFoo3DontTouchMe; // shall not be serialized/deserialized
454     *
455     * void serialize(Serialization::Archive* archive);
456     * };
457     * @endcode
458     * And then you would implement those two new methods like this (i.e. in
459     * your .cpp file):
460     * @code
461     * #define SRLZ(member) \
462     * archive->serializeMember(*this, member, #member);
463     *
464     * void Foo::serialize(Serialization::Archive* archive) {
465     * SRLZ(a);
466     * SRLZ(b);
467     * SRLZ(c);
468     * }
469     *
470     * void Bar::serialize(Serialization::Archive* archive) {
471     * SRLZ(one);
472     * SRLZ(two);
473     * SRLZ(foo1);
474     * SRLZ(pFoo2);
475     * // leaving out pFoo3DontTouchMe here
476     * }
477     * @endcode
478     * Now when you serialize such a Bar object, this framework will also
479     * automatically serialize the respective Foo object(s) accordingly, also
480     * for the pFoo2 pointer for instance (as long as it is not a NULL pointer
481     * that is).
482     *
483     * Note that there is only one method that you need to implement. So the
484     * respective serialize() method implementation of your classes/structs are
485     * both called for serialization, as well as for deserialization!
486     */
487     class Archive {
488     public:
489     Archive();
490     Archive(const RawData& data);
491     Archive(const uint8_t* data, size_t size);
492     virtual ~Archive();
493    
494     template<typename T>
495     void serialize(const T* obj) {
496     m_operation = OPERATION_SERIALIZE;
497     m_allObjects.clear();
498     m_rawData.clear();
499     m_root = UID::from(obj);
500     const_cast<T*>(obj)->serialize(this);
501     encode();
502     m_operation = OPERATION_NONE;
503     }
504    
505     template<typename T>
506     void deserialize(T* obj) {
507     Archive a;
508     m_operation = OPERATION_DESERIALIZE;
509     obj->serialize(&a);
510     a.m_root = UID::from(obj);
511     Syncer s(a, *this);
512     m_operation = OPERATION_NONE;
513     }
514    
515     template<typename T>
516     void operator<<(const T& obj) {
517     serialize(&obj);
518     }
519    
520     template<typename T>
521     void operator>>(T& obj) {
522     deserialize(&obj);
523     }
524    
525     const RawData& rawData() const { return m_rawData; }
526     virtual String rawDataFormat() const;
527    
528     template<typename T_classType, typename T_memberType>
529     void serializeMember(const T_classType& nativeObject, const T_memberType& nativeMember, const char* memberName) {
530     const size_t offset =
531     ((const uint8_t*)(const void*)&nativeMember) -
532     ((const uint8_t*)(const void*)&nativeObject);
533     const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);
534     const DataType type = DataType::dataTypeOf(nativeMember);
535     const Member member(memberName, uids[0], offset, type);
536     const UID parentUID = UID::from(nativeObject);
537     Object& parent = m_allObjects[parentUID];
538     if (!parent) {
539     const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
540     const DataType type = DataType::dataTypeOf(nativeObject);
541     parent = Object(uids, type);
542     }
543     parent.members().push_back(member);
544     const Object obj(uids, type);
545     const bool bExistsAlready = m_allObjects.count(uids[0]);
546     const bool isValidObject = obj;
547     const bool bExistingObjectIsInvalid = !m_allObjects[uids[0]];
548     if (!bExistsAlready || (bExistingObjectIsInvalid && isValidObject)) {
549     m_allObjects[uids[0]] = obj;
550     // recurse serialization for all members of this member
551     // (only for struct/class types, noop for primitive types)
552     SerializationRecursion<T_memberType>::serializeObject(this, nativeMember);
553     }
554     }
555    
556     virtual void decode(const RawData& data);
557     virtual void decode(const uint8_t* data, size_t size);
558     void clear();
559     void remove(const Object& obj);
560     Object& rootObject();
561     Object& objectByUID(const UID& uid);
562    
563     protected:
564     // UID resolver for non-pointer types
565     template<typename T>
566     class UIDChainResolver {
567     public:
568     UIDChainResolver(const T& data) {
569     m_uid.push_back(UID::from(data));
570     }
571    
572     operator UIDChain() const { return m_uid; }
573     UIDChain operator()() const { return m_uid; }
574     private:
575     UIDChain m_uid;
576     };
577    
578     // UID resolver for pointer types (of 1st degree)
579     template<typename T>
580     class UIDChainResolver<T*> {
581     public:
582     UIDChainResolver(const T*& data) {
583     m_uid.push_back((UID) { &data, sizeof(data) });
584     m_uid.push_back((UID) { data, sizeof(*data) });
585     }
586    
587     operator UIDChain() const { return m_uid; }
588     UIDChain operator()() const { return m_uid; }
589     private:
590     UIDChain m_uid;
591     };
592    
593     // SerializationRecursion for non-pointer class/struct types.
594     template<typename T, bool T_isRecursive>
595     struct SerializationRecursionImpl {
596     static void serializeObject(Archive* archive, const T& obj) {
597     const_cast<T&>(obj).serialize(archive);
598     }
599     };
600    
601     // SerializationRecursion for pointers (of 1st degree) to class/structs.
602     template<typename T, bool T_isRecursive>
603     struct SerializationRecursionImpl<T*,T_isRecursive> {
604     static void serializeObject(Archive* archive, const T*& obj) {
605     if (!obj) return;
606     const_cast<T*&>(obj)->serialize(archive);
607     }
608     };
609    
610     // NOOP SerializationRecursion for primitive types.
611     template<typename T>
612     struct SerializationRecursionImpl<T,false> {
613     static void serializeObject(Archive* archive, const T& obj) {}
614     };
615    
616     // NOOP SerializationRecursion for pointers (of 1st degree) to primitive types.
617     template<typename T>
618     struct SerializationRecursionImpl<T*,false> {
619     static void serializeObject(Archive* archive, const T*& obj) {}
620     };
621    
622     // Automatically handles recursion for class/struct types, while ignoring all primitive types.
623     template<typename T>
624     struct SerializationRecursion : SerializationRecursionImpl<T, __is_class(T)> {
625     };
626    
627     class ObjectPool : public std::map<UID,Object> {
628     public:
629     // prevent passing obvious invalid UID values from creating a new pair entry
630     Object& operator[](const UID& k) {
631     static Object invalid;
632     if (!k.isValid()) {
633     invalid = Object();
634     return invalid;
635     }
636     return std::map<UID,Object>::operator[](k);
637     }
638     };
639    
640     friend String _encode(const ObjectPool& objects);
641    
642     private:
643     String _encodeRootBlob();
644     void _popRootBlob(const char*& p, const char* end);
645     void _popObjectsBlob(const char*& p, const char* end);
646    
647     protected:
648     class Syncer {
649     public:
650     Syncer(Archive& dst, Archive& src);
651     protected:
652     void syncObject(const Object& dst, const Object& src);
653     void syncPrimitive(const Object& dst, const Object& src);
654     void syncPointer(const Object& dst, const Object& src);
655     void syncMember(const Member& dstMember, const Member& srcMember);
656     static Member dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember);
657     private:
658     Archive& m_dst;
659     Archive& m_src;
660     };
661    
662     virtual void encode();
663    
664     ObjectPool m_allObjects;
665     operation_t m_operation;
666     UID m_root;
667     RawData m_rawData;
668     };
669    
670     /**
671     * Will be thrown whenever an error occurs during an serialization or
672     * deserialization process.
673     */
674     class Exception {
675     public:
676     String Message;
677    
678     Exception(String Message) { Exception::Message = Message; }
679     void PrintMessage();
680     virtual ~Exception() {}
681     };
682    
683     } // namespace Serialization
684    
685     #endif // LIBGIG_SERIALIZATION_H

  ViewVC Help
Powered by ViewVC