libgig 4.6.0
Serialization.h
1/***************************************************************************
2 * *
3 * Copyright (C) 2017-2026 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#include <set>
38#include <time.h>
39#include <stdarg.h>
40#include <assert.h>
41#include <functional>
42#include <sstream>
43#include <locale>
44#include <limits>
45
46#include "sysdef.h"
47
48#ifndef __has_extension
49# define __has_extension(x) 0
50#endif
51
52#ifndef HAS_BUILTIN_TYPE_TRAITS
53# if __cplusplus >= 201103L
54# define HAS_BUILTIN_TYPE_TRAITS 1
55# elif ( __has_extension(is_class) && __has_extension(is_enum) )
56# define HAS_BUILTIN_TYPE_TRAITS 1
57# elif ( __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 3 ) )
58# define HAS_BUILTIN_TYPE_TRAITS 1
59# elif _MSC_VER >= 1400 /* MS Visual C++ 8.0 (Visual Studio 2005) */
60# define HAS_BUILTIN_TYPE_TRAITS 1
61# elif __INTEL_COMPILER >= 1100
62# define HAS_BUILTIN_TYPE_TRAITS 1
63# else
64# define HAS_BUILTIN_TYPE_TRAITS 0
65# endif
66#endif
67
68#if !HAS_BUILTIN_TYPE_TRAITS
69# include <tr1/type_traits>
70# define LIBGIG_IS_CLASS(type) std::tr1::__is_union_or_class<type>::value //NOTE: without compiler support we cannot distinguish union from class
71#else
72# define LIBGIG_IS_CLASS(type) __is_class(type)
73#endif
74
166namespace Serialization {
167
168 // just symbol prototyping
169 class DataType;
170 class Object;
171 class Member;
172 class Archive;
173 class Exception;
174
182 typedef std::string String;
183
196 template<class T>
197 using Array = std::vector<T>;
198
208 template<class T>
209 using Set = std::set<T>;
210
229 template<class T_key, class T_value>
230 using Map = std::map<T_key,T_value>;
231
240 typedef std::vector<uint8_t> RawData;
241
252 typedef void* ID;
253
261 typedef uint32_t Version;
262
272
280 template<typename T>
281 bool IsEnum(const T& /*data*/) {
282 #if !HAS_BUILTIN_TYPE_TRAITS
283 return std::tr1::is_enum<T>::value;
284 #else
285 return __is_enum(T);
286 #endif
287 }
288
299 template<typename T>
300 bool IsUnion(const T& /*data*/) {
301 #if !HAS_BUILTIN_TYPE_TRAITS
302 return false; // without compiler support we cannot distinguish union from class
303 #else
304 return __is_union(T);
305 #endif
306 }
307
317 template<typename T>
318 bool IsClass(const T& /*data*/) {
319 #if !HAS_BUILTIN_TYPE_TRAITS
320 return std::tr1::__is_union_or_class<T>::value; // without compiler support we cannot distinguish union from class
321 #else
322 return __is_class(T);
323 #endif
324 }
325
326 /*template<typename T>
327 bool IsTrivial(T data) {
328 return __is_trivial(T);
329 }*/
330
331 /*template<typename T>
332 bool IsPOD(T data) {
333 return __is_pod(T);
334 }*/
335
336 /*template<typename T>
337 bool IsArray(const T& data) {
338 return false;
339 }*/
340
341 /*template<typename T>
342 bool IsArray(const Array<T>& data) {
343 return true;
344 }*/
345
346 // generalized C-locale guaranteed T to string conversion
347 template<typename T> inline
348 String toString(const T& value) {
349 //TODO: replace by locale-independent, maybe faster std::to_chars() [C++17]
350 std::stringstream ss;
351 ss.imbue(std::locale::classic()); // a.k.a. "C" locale
352 ss << value;
353 return ss.str();
354 }
355
356 // C-locale guaranteed double to string conversion
357 template<> inline
358 String toString(const double& value) {
359 //TODO: replace by locale-independent, maybe faster std::to_chars() [C++17]
360 std::stringstream ss;
361 ss.imbue(std::locale::classic()); // a.k.a. "C" locale
362 // stringstream has lower precision by default compared to std::to_string()
363 ss.precision(std::numeric_limits<double>::max_digits10);
364 ss << value;
365 return ss.str();
366 }
367
368 template<typename T> inline
369 String toString(T* ptr) {
370 std::stringstream ss;
371 ss.imbue(std::locale::classic()); // a.k.a. "C" locale
372 ss << "0x" << std::hex << size_t(ptr);
373 return ss.str();
374 }
375
376 template<> inline
377 String toString(const String& value) {
378 return value;
379 }
380
381#if LIBGIG_SERIALIZATION_INTERNAL
382 // prototyping of private internal friend functions
383 template<typename T>
384 static T _primitiveObjectValueToNumber(const Object& obj);
385#endif // LIBGIG_SERIALIZATION_INTERNAL
386
403 class DataType {
404 public:
405 DataType();
406 size_t size() const { return m_size; }
407 bool isValid() const;
408 bool isPointer() const;
409 bool isClass() const;
410 bool isPrimitive() const;
411 bool isString() const;
412 bool isChar() const;
413 bool isInteger() const;
414 bool isReal() const;
415 bool isNumber() const;
416 bool isBool() const;
417 bool isEnum() const;
418 bool isArray() const;
419 bool isSet() const;
420 bool isMap() const;
421 bool isSigned() const;
422 operator bool() const { return isValid(); }
423 //bool operator()() const { return isValid(); }
424 bool operator==(const DataType& other) const;
425 bool operator!=(const DataType& other) const;
426 bool operator<(const DataType& other) const;
427 bool operator>(const DataType& other) const;
428 String asLongDescr() const;
429 String baseTypeName() const;
430 String customTypeName(bool demangle = false) const;
431 String customTypeName2(bool demangle = false) const;
432
448 template<typename T>
449 static DataType dataTypeOf(const T& data, bool registerType = true) {
450 const DataType type = Resolver<T>::resolve(data);
451 if (registerType)
452 registerNativeDataType(type, data);
453 return type;
454 }
455
469 template<typename T>
470 static DataType dataType(bool registerType = true) {
471 T unused = T();
472 const DataType type = Resolver<T>::resolve(unused);
473 if (registerType)
474 registerNativeDataType(type, unused);
475 return type;
476 }
477
487 template<typename T>
488 static size_t sizeOf(const T& data) {
489 DataType type = dataTypeOf(data);
490 const auto itNativeType = m_nativeTypes.find(type.internalID());
491 return (itNativeType != m_nativeTypes.end()) ?
492 itNativeType->second.size : sizeof(data);
493 }
494
530 template<typename T>
531 static void registerNativeDataType();
532
562 template<typename T>
563 class NativeDataTypeRegistry {
564 public:
565 NativeDataTypeRegistry() {
567 }
568 };
569
570 protected:
571 DataType(bool isPointer, int size, String baseType,
572 String customType1 = "", String customType2 = "");
573
574 String internalID() const;
575 Object newInstance(Archive* archive) const;
576
577 template<typename T, typename std::enable_if<
578 !std::is_pointer<T>::value &&
579 std::is_default_constructible<T>::value, bool>::type = true>
580 static void registerNativeDataType(const DataType& type, const T& nativeData);
581
582 template<typename T, typename std::enable_if<
583 !std::is_pointer<T>::value &&
584 !std::is_default_constructible<T>::value, bool>::type = true>
585 static void registerNativeDataType(const DataType& type, const T& nativeData);
586
587 template<typename T, typename std::enable_if<
588 std::is_default_constructible<T>::value, bool>::type = true>
589 static void registerNativeDataType(const DataType& type, const T* const& nativeData);
590
591 template<typename T, typename std::enable_if<
592 !std::is_default_constructible<T>::value, bool>::type = true>
593 static void registerNativeDataType(const DataType& type, const T* const& nativeData);
594
595 // DataType resolver for primitive / built-in types
596 template<typename T, bool T_isPointer>
597 struct ResolverBase {
598 static DataType resolve(const T& data);
599 };
600
601 // DataType resolver for non-pointer types
602 template<typename T>
603 struct Resolver : ResolverBase<T,false> {
604 static DataType resolve(const T& data) {
605 return ResolverBase<T,false>::resolve(data);
606 }
607 };
608
609 // DataType resolver for pointer types (of 1st degree)
610 template<typename T>
611 struct Resolver<T*> : ResolverBase<T,true> {
612 static DataType resolve(const T* const & data) {
613 return ResolverBase<T,true>::resolve(*data);
614 }
615 };
616
617 // DataType resolver for non-pointer Array<> container object types.
618 template<typename T>
619 struct Resolver<Array<T>> {
620 static DataType resolve(const Array<T>& data) {
621 const int sz = sizeof(data);
622 T unused = T();
623 return DataType(false, sz, "Array", rawCppTypeNameOf(unused));
624 }
625 };
626
627 // DataType resolver for Array<> pointer types (of 1st degree).
628 template<typename T>
629 struct Resolver<Array<T>*> {
630 static DataType resolve(const Array<T>*& data) {
631 const int sz = sizeof(*data);
632 T unused = T();
633 return DataType(true, sz, "Array", rawCppTypeNameOf(unused));
634 }
635 };
636
637 // DataType resolver for non-pointer Set<> container object types.
638 template<typename T>
639 struct Resolver<Set<T>> {
640 static DataType resolve(const Set<T>& data) {
641 const int sz = sizeof(data);
642 T unused = T();
643 return DataType(false, sz, "Set", rawCppTypeNameOf(unused));
644 }
645 };
646
647 // DataType resolver for Set<> pointer types (of 1st degree).
648 template<typename T>
649 struct Resolver<Set<T>*> {
650 static DataType resolve(const Set<T>*& data) {
651 const int sz = sizeof(*data);
652 T unused = T();
653 return DataType(true, sz, "Set", rawCppTypeNameOf(unused));
654 }
655 };
656
657 // DataType resolver for non-pointer Map<> container object types.
658 template<typename T_key, typename T_value>
659 struct Resolver<Map<T_key,T_value>> {
660 static DataType resolve(const Map<T_key,T_value>& data) {
661 const int sz = sizeof(data);
662 T_key unused1 = T_key();
663 T_value unused2 = T_value();
664 return DataType(false, sz, "Map", rawCppTypeNameOf(unused1),
665 rawCppTypeNameOf(unused2));
666 }
667 };
668
669 // DataType resolver for Map<> pointer types (of 1st degree).
670 template<typename T_key, typename T_value>
671 struct Resolver<Map<T_key,T_value>*> {
672 static DataType resolve(const Map<T_key,T_value>*& data) {
673 const int sz = sizeof(*data);
674 T_key unused1 = T_key();
675 T_value unused2 = T_value();
676 return DataType(true, sz, "Map", rawCppTypeNameOf(unused1),
677 rawCppTypeNameOf(unused2));
678 }
679 };
680
681 // for compile-time known types
682 template<typename T>
683 static String rawCppTypeName() {
684 const std::type_info& type = typeid(T);
685 #if defined _MSC_VER // Microsoft compiler ...
686 String name = type.raw_name();
687 #else // i.e. especially GCC and clang ...
688 String name = type.name();
689 #endif
690 //while (!name.empty() && name[0] >= 0 && name[0] <= 9)
691 // name = name.substr(1);
692 return name;
693 }
694
695 // for RTTI resolved pointer types
696 template<typename T>
697 static String rawCppTypeNameOf(const T* const & data) {
698 return rawCppTypeName<T*>();
699 }
700
701 // for RTTI resolved non-pointer types
702 template<typename T>
703 static String rawCppTypeNameOf(const T& data) {
704 const std::type_info& type = typeid(data);
705 #if defined _MSC_VER // Microsoft compiler ...
706 String name = type.raw_name();
707 #else // i.e. especially GCC and clang ...
708 String name = type.name();
709 #endif
710 //while (!name.empty() && name[0] >= 0 && name[0] <= 9)
711 // name = name.substr(1);
712 return name;
713 }
714
715 private:
716 struct NativeType {
717 size_t size;
718 std::function<Object(Archive*)> allocFn;
719 };
720
721 int m_size;
722 bool m_isPointer;
723 String m_baseTypeName;
724 String m_customTypeName;
725 String m_customTypeName2;
726
727 static std::map<String,NativeType> m_nativeTypes;
728
729 friend class Archive;
730 friend class SrxFormat;
731 friend class SrxJSONDecoder;
732 };
733
749 class UID {
750 public:
752 size_t size;
753
754 bool isValid() const;
755 operator bool() const { return isValid(); }
756 //bool operator()() const { return isValid(); }
757 bool operator==(const UID& other) const { return id == other.id && size == other.size; }
758 bool operator!=(const UID& other) const { return id != other.id || size != other.size; }
759 bool operator<(const UID& other) const { return id < other.id || (id == other.id && size < other.size); }
760 bool operator>(const UID& other) const { return id > other.id || (id == other.id && size > other.size); }
761
769 template<typename T>
770 static UID from(const T& obj) {
771 return Resolver<T>::resolve(obj);
772 }
773
774 protected:
775 // UID resolver for non-pointer types
776 template<typename T>
777 struct Resolver {
778 static UID resolve(const T& obj);
779 };
780
781 // UID resolver for pointer types (of 1st degree)
782 template<typename T>
783 struct Resolver<T*> {
784 static UID resolve(const T* const & obj);
785 };
786 };
787
793 extern const UID NO_UID;
794
826 typedef std::vector<UID> UIDChain;
827
849 class Member {
850 public:
851 Member();
852 UID uid() const;
853 UID parentUID() const;
854 String name() const;
855 ssize_t offset() const;
856 const DataType& type() const;
857 bool isValid() const;
858 operator bool() const { return isValid(); }
859 //bool operator()() const { return isValid(); }
860 bool operator==(const Member& other) const;
861 bool operator!=(const Member& other) const;
862 bool operator<(const Member& other) const;
863 bool operator>(const Member& other) const;
864
865 protected:
866 Member(String name, UID uid, ssize_t offset, DataType type, const Object& parent);
867 friend class Archive;
868
869 private:
870 UID m_uid;
871 ssize_t m_offset;
872 String m_name;
873 DataType m_type;
874 UID m_parentUID;
875
876 friend class SrxFormat;
877 friend class SrxJSONDecoder;
878 };
879
904 class Object {
905 public:
906 Object();
907 Object(UIDChain uidChain, DataType type, const Object& parent);
908
909 UID uid(int index = 0) const;
910 const UIDChain& uidChain() const;
911 UID parentUID() const;
912 const DataType& type() const;
913 const RawData& rawData() const;
914 Version version() const;
915 Version minVersion() const;
916 bool isVersionCompatibleTo(const Object& other) const;
917 std::vector<Member>& members();
918 const std::vector<Member>& members() const;
919 Member memberNamed(String name) const;
920 Member memberByUID(const UID& uid) const;
921 std::vector<Member> membersOfType(const DataType& type) const;
922 int sequenceIndexOf(const Member& member) const;
923 bool isValid() const;
924 operator bool() const { return isValid(); }
925 //bool operator()() const { return isValid(); }
926 bool operator==(const Object& other) const;
927 bool operator!=(const Object& other) const;
928 bool operator<(const Object& other) const;
929 bool operator>(const Object& other) const;
930 void setNativeValueFromString(const String& s);
931
932 protected:
933 void remove(const Member& member);
934 void setVersion(Version v);
935 void setMinVersion(Version v);
936
937 private:
938 DataType m_type;
939 UIDChain m_uid;
940 UID m_parentUID;
941 Version m_version;
942 Version m_minVersion;
943 RawData m_data;
944 std::vector<Member> m_members;
945 std::function<void(Object& dstObj, const Object& srcObj, void* syncer)> m_sync;
946
947#if LIBGIG_SERIALIZATION_INTERNAL
948 template<typename T>
949 friend T _primitiveObjectValueToNumber(const Object& obj);
950#endif // LIBGIG_SERIALIZATION_INTERNAL
951
952 friend class Archive;
953 friend class SrxFormat;
954 friend class SrxJSONDecoder;
955 };
956
1095 class Archive {
1096 public:
1104
1131
1132 Archive(format_t format = FORMAT_AUTO);
1133 Archive(const RawData& data, format_t format = FORMAT_AUTO);
1134 Archive(const uint8_t* data, size_t size, format_t format = FORMAT_AUTO);
1135 virtual ~Archive();
1136
1162 template<typename T>
1163 void serialize(const T* obj) {
1164 m_operation = OPERATION_SERIALIZE;
1165 m_allObjects.clear();
1166 m_rawData.clear();
1167 preregisterNativeObject(*obj);
1168 m_root = UID::from(obj);
1169 const_cast<T*>(obj)->serialize(this);
1170 encode();
1171 m_operation = OPERATION_NONE;
1172 }
1173
1198 template<typename T>
1199 void deserialize(T* obj) {
1200 Archive a;
1201 a.m_operation = m_operation = OPERATION_DESERIALIZE;
1202 a.preregisterNativeObject(*obj);
1203 obj->serialize(&a);
1204 a.m_root = UID::from(obj);
1205 Syncer s(a, *this);
1206 a.m_operation = m_operation = OPERATION_NONE;
1207 }
1208
1223 template<typename T>
1224 void operator<<(const T& obj) {
1225 serialize(&obj);
1226 }
1227
1246 template<typename T>
1247 void operator>>(T& obj) {
1248 deserialize(&obj);
1249 }
1250
1251 const RawData& rawData();
1252 virtual String rawDataFormat() const;
1253
1310 template<typename T_classType, typename T_memberType>
1311 void serializeMember(const T_classType& nativeObject, const T_memberType& nativeMember, const char* memberName) {
1312 Object& parent = preregisterNativeObject(nativeObject);
1313 const ssize_t offset =
1314 ((const uint8_t*)(const void*)&nativeMember) -
1315 ((const uint8_t*)(const void*)&nativeObject);
1316 const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);
1317 const DataType type = DataType::dataTypeOf(nativeMember);
1318 const Member member(memberName, uids[0], offset, type, parent);
1319 std::vector<Member>& members = parent.members();
1320 for (const Member& m : members)
1321 assert(m.name() != memberName);
1322 parent.members().push_back(member);
1323 const Object obj(uids, type, parent);
1324 const bool bExistsAlready = m_allObjects.count(uids[0]);
1325 const bool isValidObject = obj;
1326 const Object& existingObj = m_allObjects[uids[0]];
1327 const bool bExistingObjectIsInvalid = !existingObj;
1328 if (isValidObject && (
1329 !bExistsAlready || bExistingObjectIsInvalid || (
1330 obj.parentUID() && !existingObj.parentUID()
1331 )
1332 ))
1333 {
1334 m_allObjects[uids[0]] = obj;
1335 // recurse serialization for all members of this member
1336 // (only for struct/class types, noop for primitive types)
1337 SerializationRecursion<T_memberType>::serializeObject(this, nativeMember);
1338 }
1339 }
1340
1371 template<typename T_classType, typename T_memberType>
1372 void serializeHeapMember(const T_classType& nativeObject, const T_memberType& heapMember, const char* memberName) {
1373 Object& parent = preregisterNativeObject(nativeObject);
1374 const ssize_t offset = -1; // used for all members on heap
1375 const UIDChain uids = UIDChainResolver<T_memberType>(heapMember);
1376 const DataType type = DataType::dataTypeOf(heapMember);
1377 const Member member(memberName, uids[0], offset, type, parent);
1378 parent.members().push_back(member);
1379 const Object obj(uids, type, parent);
1380 const bool bExistsAlready = m_allObjects.count(uids[0]);
1381 const bool isValidObject = obj;
1382 const Object& existingObj = m_allObjects[uids[0]];
1383 const bool bExistingObjectIsInvalid = !existingObj;
1384 if (isValidObject && (
1385 !bExistsAlready || bExistingObjectIsInvalid || (
1386 obj.parentUID() && !existingObj.parentUID()
1387 )
1388 ))
1389 {
1390 m_allObjects[uids[0]] = obj;
1391 // recurse serialization for all members of this member
1392 // (only for struct/class types, noop for primitive types)
1393 SerializationRecursion<T_memberType>::serializeObject(this, heapMember);
1394 }
1395 }
1396
1417 template<typename T_objectType>
1418 void serializeAnonymousObject(const T_objectType& heapObject) {
1419 const UIDChain uids = UIDChainResolver<T_objectType>(heapObject);
1420 const DataType type = DataType::dataTypeOf(heapObject);
1421 const Object obj(uids, type, Object());
1422 const bool bExistsAlready = m_allObjects.count(uids[0]);
1423 const bool isValidObject = obj;
1424 const Object& existingObj = m_allObjects[uids[0]];
1425 const bool bExistingObjectIsInvalid = !existingObj;
1426 if (isValidObject && (
1427 !bExistsAlready || bExistingObjectIsInvalid || (
1428 obj.parentUID() && !existingObj.parentUID()
1429 )
1430 ))
1431 {
1432 m_allObjects[uids[0]] = obj;
1433 // recurse serialization for all members of this object
1434 // (only for struct/class types, noop for primitive types)
1435 SerializationRecursion<T_objectType>::serializeObject(this, heapObject);
1436 }
1437 }
1438
1518 template<typename T_classType>
1519 void setVersion(const T_classType& nativeObject, Version v) {
1520 const UID uid = UID::from(nativeObject);
1521 Object& obj = m_allObjects[uid];
1522 if (!obj) {
1523 const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
1524 const DataType type = DataType::dataTypeOf(nativeObject);
1525 obj = Object(uids, type, Object());
1526 }
1527 setVersion(obj, v);
1528 }
1529
1559 template<typename T_classType>
1560 void setMinVersion(const T_classType& nativeObject, Version v) {
1561 const UID uid = UID::from(nativeObject);
1562 Object& obj = m_allObjects[uid];
1563 if (!obj) {
1564 const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
1565 const DataType type = DataType::dataTypeOf(nativeObject);
1566 obj = Object(uids, type, Object());
1567 }
1568 setMinVersion(obj, v);
1569 }
1570
1571 virtual void decode(const RawData& data);
1572 virtual void decode(const RawData& data, format_t format);
1573 virtual void decode(const uint8_t* data, size_t size);
1574 virtual void decode(const uint8_t* data, size_t size, format_t format);
1575 void clear();
1576 bool isModified() const;
1577 void removeMember(Object& parent, const Member& member);
1578 void remove(const Object& obj);
1579 Object& rootObject();
1580 Object& objectByUID(const UID& uid);
1581 Object& parentObjectOf(const Object& obj);
1582 Object& parentObjectOf(const Member& member);
1583 void setAutoValue(Object& object, String value);
1584 void setIntValue(Object& object, int64_t value);
1585 void setRealValue(Object& object, double value);
1586 void setCharValue(Object& object, char value);
1587 void setBoolValue(Object& object, bool value);
1588 void setEnumValue(Object& object, uint64_t value);
1589 void setStringValue(Object& object, String value);
1590 String valueAsString(const Object& object);
1591 int64_t valueAsInt(const Object& object);
1592 double valueAsReal(const Object& object);
1593 char valueAsChar(const Object& object);
1594 bool valueAsBool(const Object& object);
1595 void setVersion(Object& object, Version v);
1596 void setMinVersion(Object& object, Version v);
1597 String name() const;
1598 void setName(String name);
1599 String comment() const;
1601 time_t timeStampCreated() const;
1602 time_t timeStampModified() const;
1603 tm dateTimeCreated(time_base_t base = LOCAL_TIME) const;
1604 tm dateTimeModified(time_base_t base = LOCAL_TIME) const;
1605 operation_t operation() const;
1606
1607 protected:
1608 Object& objectByBaseUID(const UID& uid);
1609 static String primitiveObjectValueToString(const Object& obj);
1610
1611 template<typename T_classType>
1612 Object& preregisterNativeObject(const T_classType& nativeObject) {
1613 const UID uid = UID::from(nativeObject);
1614 Object& obj = m_allObjects[uid];
1615 if (!obj) {
1616 const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
1617 const DataType type = DataType::dataTypeOf(nativeObject);
1618 obj = Object(uids, type, Object());
1619 }
1620 return obj;
1621 }
1622
1623 // UID resolver for non-pointer types
1624 template<typename T>
1625 class UIDChainResolver {
1626 public:
1627 UIDChainResolver(const T& data) {
1628 m_uid.push_back(UID::from(data));
1629 }
1630
1631 operator UIDChain() const { return m_uid; }
1632 UIDChain operator()() const { return m_uid; }
1633 private:
1634 UIDChain m_uid;
1635 };
1636
1637 // UID resolver for pointer types (of 1st degree)
1638 template<typename T>
1639 class UIDChainResolver<T*> {
1640 public:
1641 UIDChainResolver(const T* const & data) {
1642 m_uid.push_back({ (ID) &data, sizeof(data) });
1643 if (data)
1644 m_uid.push_back(UID::from(*data));
1645 else
1646 m_uid.push_back({ (ID) data, sizeof(*data) });
1647 }
1648
1649 operator UIDChain() const { return m_uid; }
1650 UIDChain operator()() const { return m_uid; }
1651 private:
1652 UIDChain m_uid;
1653 };
1654
1655 // SerializationRecursion for non-pointer class/struct types.
1656 template<typename T, bool T_isRecursive>
1657 struct SerializationRecursionImpl {
1658 static void serializeObject(Archive* archive, const T& obj) {
1659 const_cast<T&>(obj).serialize(archive);
1660 }
1661 };
1662
1663 // SerializationRecursion for pointers (of 1st degree) to class/structs.
1664 template<typename T, bool T_isRecursive>
1665 struct SerializationRecursionImpl<T*,T_isRecursive> {
1666 static void serializeObject(Archive* archive, const T*& obj) {
1667 if (!obj) return;
1668 const_cast<T*&>(obj)->serialize(archive);
1669 }
1670 };
1671
1672 // NOOP SerializationRecursion for primitive types.
1673 template<typename T>
1674 struct SerializationRecursionImpl<T,false> {
1675 static void serializeObject(Archive* /*archive*/, const T& /*obj*/) {}
1676 };
1677
1678 // SerializationRecursion for pointers (of 1st degree) to primitive types.
1679 template<typename T>
1680 struct SerializationRecursionImpl<T*,false> {
1681 static void serializeObject(Archive* archive, const T* const & obj) {
1682 if (!obj) return;
1683 archive->serializeAnonymousObject(*obj);
1684 }
1685 };
1686
1687 // NOOP SerializationRecursion for String objects.
1688 template<bool T_isRecursive>
1689 struct SerializationRecursionImpl<String,T_isRecursive> {
1690 static void serializeObject(Archive* archive, const String& obj) {}
1691 };
1692
1693 // SerializationRecursion for String pointers (of 1st degree).
1694 template<bool T_isRecursive>
1695 struct SerializationRecursionImpl<String*,T_isRecursive> {
1696 static void serializeObject(Archive* archive, const String*& obj) {
1697 if (!obj) return;
1698 archive->serializeAnonymousObject(*obj);
1699 }
1700 };
1701
1702 // SerializationRecursion for Array<> objects.
1703 template<typename T, bool T_isRecursive>
1704 struct SerializationRecursionImpl<Array<T>,T_isRecursive> {
1705 static void serializeObject(Archive* archive, const Array<T>& obj) {
1706 const UIDChain uids = UIDChainResolver<Array<T>>(obj);
1707 const Object& object = archive->objectByUID(uids[0]);
1708 if (archive->operation() == OPERATION_SERIALIZE) {
1709 for (size_t i = 0; i < obj.size(); ++i) {
1710 archive->serializeHeapMember(
1711 obj, obj[i], ("[" + toString(i) + "]").c_str()
1712 );
1713 }
1714 } else {
1715 const_cast<Object&>(object).m_sync =
1716 [&obj,archive](Object& dstObj, const Object& srcObj,
1717 void* syncer)
1718 {
1719 const size_t n = srcObj.members().size();
1720 const_cast<Array<T>&>(obj).resize(n);
1721 for (size_t i = 0; i < n; ++i) {
1722 archive->serializeHeapMember(
1723 obj, obj[i], ("[" + toString(i) + "]").c_str()
1724 );
1725 }
1726 // updating dstObj required as serializeHeapMember()
1727 // replaced the original object by a new one
1728 dstObj = archive->objectByUID(dstObj.uid());
1729 for (size_t i = 0; i < n; ++i) {
1730 String name = "[" + toString(i) + "]";
1731 Member srcMember = srcObj.memberNamed(name);
1732 Member dstMember = dstObj.memberNamed(name);
1733 ((Syncer*)syncer)->syncMember(dstMember, srcMember);
1734 }
1735 };
1736 }
1737 }
1738 };
1739
1740 // SerializationRecursion for Array<> pointers (of 1st degree).
1741 template<typename T, bool T_isRecursive>
1742 struct SerializationRecursionImpl<Array<T>*,T_isRecursive> {
1743 static void serializeObject(Archive* archive, const Array<T>*& obj) {
1744 if (!obj) return;
1745 SerializationRecursionImpl<Array<T>,T_isRecursive>::serializeObject(
1746 archive, *obj
1747 );
1748 }
1749 };
1750
1751 // SerializationRecursion for Set<> objects.
1752 template<typename T, bool T_isRecursive>
1753 struct SerializationRecursionImpl<Set<T>,T_isRecursive> {
1754 static void serializeObject(Archive* archive, const Set<T>& obj) {
1755 const UIDChain uids = UIDChainResolver<Set<T>>(obj);
1756 const Object& object = archive->objectByUID(uids[0]);
1757 if (archive->operation() == OPERATION_SERIALIZE) {
1758 for (const T& key : obj) {
1759 archive->serializeHeapMember(
1760 obj, key, ("[" + toString(key) + "]").c_str()
1761 );
1762 }
1763 } else {
1764 const_cast<Object&>(object).m_sync =
1765 [&obj,archive](Object& dstObj, const Object& srcObj,
1766 void* syncer)
1767 {
1768 const size_t n = srcObj.members().size();
1769 const_cast<Set<T>&>(obj).clear();
1770 for (size_t i = 0; i < n; ++i) {
1771 const Member& member = srcObj.members()[i];
1772 String name = member.name();
1773 // strip brackets from name
1774 if (name.length() < 2 || name[0] != '[' ||
1775 *name.rbegin() != ']') continue;
1776 name = name.substr(1, name.length() - 2);
1777 T key = T();
1778 const UIDChain uids = UIDChainResolver<T>(key);
1779 const DataType type = DataType::dataTypeOf(key);
1780 Object tmpObj(uids, type, Object());
1781 // set the value of "key" variable by abstraction API
1782 tmpObj.setNativeValueFromString(name);
1783 // only for keys of pointer type: translation of
1784 // memory address from source archive to destination
1785 // archive required, NOOP for all other data types
1786 ((Syncer*)syncer)->translateKey(key);
1787 // insert (translated) key into set
1788 const_cast<Set<T>&>(obj).insert(key);
1789 }
1790 // continue serialization recursion
1791 for (const T& key : obj) {
1792 archive->serializeHeapMember(
1793 obj, key, ("[" + toString(key) + "]").c_str()
1794 );
1795 }
1796 // updating dstObj required as serializeHeapMember()
1797 // replaced the original object by a new one
1798 dstObj = archive->objectByUID(dstObj.uid());
1799 };
1800 }
1801 }
1802 };
1803
1804 // SerializationRecursion for Set<> pointers (of 1st degree).
1805 template<typename T, bool T_isRecursive>
1806 struct SerializationRecursionImpl<Set<T>*,T_isRecursive> {
1807 static void serializeObject(Archive* archive, const Set<T>*& obj) {
1808 if (!obj) return;
1809 SerializationRecursionImpl<Set<T>,T_isRecursive>::serializeObject(
1810 archive, *obj
1811 );
1812 }
1813 };
1814
1815 // SerializationRecursion for Map<> objects.
1816 template<typename T_key, typename T_value, bool T_isRecursive>
1817 struct SerializationRecursionImpl<Map<T_key,T_value>,T_isRecursive> {
1818 static void serializeObject(Archive* archive, const Map<T_key,T_value>& obj) {
1819 const UIDChain uids = UIDChainResolver<Map<T_key,T_value>>(obj);
1820 const Object& object = archive->objectByUID(uids[0]);
1821 if (archive->operation() == OPERATION_SERIALIZE) {
1822 for (const auto& it : obj) {
1823 // recurse serialization for map's key ...
1824 SerializationRecursion<T_key>::serializeObject(archive, it.first);
1825 // ... and map's value
1826 archive->serializeHeapMember(
1827 obj, it.second, ("[" + toString(it.first) + "]").c_str()
1828 );
1829 }
1830 } else {
1831 const_cast<Object&>(object).m_sync =
1832 [&obj,archive](Object& dstObj, const Object& srcObj,
1833 void* syncer)
1834 {
1835 const size_t n = srcObj.members().size();
1836 const_cast<Map<T_key,T_value>&>(obj).clear();
1837 // translation is neutral except for keys of pointer type (see comments below)
1838 std::map<String,String> memberNameTranslation;
1839 for (size_t i = 0; i < n; ++i) {
1840 const Member& member = srcObj.members()[i];
1841 String name = member.name();
1842 // strip brackets from name
1843 if (name.length() < 2 || name[0] != '[' ||
1844 *name.rbegin() != ']') continue;
1845 name = name.substr(1, name.length() - 2);
1846 T_key srcKey = T_key();
1847 const UIDChain uids = UIDChainResolver<T_key>(srcKey);
1848 const DataType type = DataType::dataTypeOf(srcKey);
1849 Object tmpObj(uids, type, Object());
1850 // set the value of "srcKey" variable by abstraction API
1851 tmpObj.setNativeValueFromString(name);
1852 T_key dstKey = srcKey;
1853 // only for keys of pointer type: translation of
1854 // memory address from source archive to destination
1855 // archive required, NOOP for all other data types
1856 ((Syncer*)syncer)->translateKey(dstKey);
1857 memberNameTranslation[member.name()] = "[" + toString(dstKey) + "]";
1858 // insert (translated) key into the destination map
1859 const_cast<Map<T_key,T_value>&>(obj)[dstKey] = T_value();
1860 }
1861 // continue serialization recursion ...
1862 for (const auto& it : obj) {
1863 // ... for map's key ...
1864 SerializationRecursion<T_key>::serializeObject(archive, it.first);
1865 // ... and map's value
1866 archive->serializeHeapMember(
1867 obj, it.second, ("[" + toString(it.first) + "]").c_str()
1868 );
1869 }
1870 // updating dstObj required as serializeHeapMember()
1871 // replaced the original object by a new one
1872 dstObj = archive->objectByUID(dstObj.uid());
1873 // sync map's values
1874 for (size_t i = 0; i < n; ++i) {
1875 Member srcMember = srcObj.members()[i];
1876 Member dstMember = dstObj.memberNamed(
1877 memberNameTranslation[srcMember.name()]
1878 );
1879 ((Syncer*)syncer)->syncMember(dstMember, srcMember);
1880 }
1881 };
1882 }
1883 }
1884 };
1885
1886 // SerializationRecursion for Map<> pointers (of 1st degree).
1887 template<typename T_key, typename T_value, bool T_isRecursive>
1888 struct SerializationRecursionImpl<Map<T_key,T_value>*,T_isRecursive> {
1889 static void serializeObject(Archive* archive, const Map<T_key,T_value>*& obj) {
1890 if (!obj) return;
1891 SerializationRecursionImpl<Map<T_key,T_value>,T_isRecursive>::serializeObject(
1892 archive, *obj
1893 );
1894 }
1895 };
1896
1897 // Automatically handles recursion for class/struct types, while ignoring all primitive types.
1898 template<typename T>
1899 struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> {
1900 };
1901
1902 class ObjectPool : public std::map<UID,Object> {
1903 public:
1904 // prevent passing obvious invalid UID values from creating a new pair entry
1905 Object& operator[](const UID& k) {
1906 if (!k.isValid())
1907 return invalidObject();
1908 return std::map<UID,Object>::operator[](k);
1909 }
1910
1911 static Object& invalidObject() {
1912 static Object invalid;
1913 invalid = Object();
1914 return invalid;
1915 }
1916 };
1917
1918 protected:
1958 class Syncer {
1959 public:
1960 Syncer(Archive& dst, Archive& src);
1961 void syncObject(const Object& dst, const Object& src);
1962 void syncPrimitive(const Object& dst, const Object& src);
1963 void syncString(const Object& dst, const Object& src);
1964 void syncArray(const Object& dst, const Object& src);
1965 void syncSet(const Object& dst, const Object& src);
1966 void syncMap(const Object& dst, const Object& src);
1967 void syncPointer(const Object& dst, const Object& src);
1968 void syncMember(const Member& dstMember, const Member& srcMember);
1969
1970 // Address translation for keys of pointer type.
1971 //
1972 // Translates memory address from source archive, to memory address
1973 // of destination archive. For "strong" pointers memory is allocated
1974 // and the pointed data is synced with data from source archive.
1975 template<typename T>
1976 void translateKey(T*& key) {
1977 if (!key) return; // NULL pointer on source side
1978
1979 // UIDChainResolver can't be used here, since the passed memory
1980 // address is an abstract one that originates from the source
1981 // archive (e.g. potentially even from another machine) and
1982 // UIDChainResolver would then crash on its typeid() operator.
1983 UID uid = { (ID) key, sizeof(*key) };
1984
1985 const Object& pointedSrcObject = m_src.objectByBaseUID(uid);
1986 assert(pointedSrcObject);
1987 std::map<UID,UID>::iterator uidRelation = m_counterparts.find(uid);
1988 if (pointedSrcObject.parentUID() || uidRelation != m_counterparts.end()) { // "weak" pointer to object ...
1989 assert(uidRelation != m_counterparts.end());
1990 key = (T*) uidRelation->second.id;
1991 } else { // "strong" pointer to object, allocation required ...
1992 assert(pointedSrcObject.type());
1993 Object pointedDstObject = pointedSrcObject.type().newInstance(&m_dst);
1994 assert(pointedDstObject);
1995 m_dst.m_allObjects[pointedDstObject.uid()] = pointedDstObject;
1996 key = (T*) pointedDstObject.uid().id;
1997
1998 syncObject(pointedDstObject, pointedSrcObject);
1999 }
2000 }
2001
2002 // NOOP, no key translation required for any other type
2003 template<typename T>
2004 void translateKey(T& key) {}
2005 protected:
2006 static Member dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember);
2007 private:
2008 Archive& m_dst;
2009 Archive& m_src;
2010 std::map<UID,UID> m_counterparts;
2011 };
2012
2013 virtual void encode();
2014 virtual void encode(format_t format);
2015
2016 ObjectPool m_allObjects;
2017 format_t m_format;
2018 operation_t m_operation;
2019 UID m_root;
2020 RawData m_rawData;
2021 bool m_isModified;
2022 String m_name;
2023 String m_comment;
2024 time_t m_timeCreated;
2025 time_t m_timeModified;
2026
2027 friend class DataType;
2028 friend class SrxFormat;
2029 friend class SrxJSONEncoder;
2030 friend class SrxJSONDecoder;
2031 };
2032
2033 // UID resolver implementation for non-pointer types
2034 template<typename T>
2035 UID UID::Resolver<T>::resolve(const T& obj) {
2036 const size_t size = DataType::sizeOf(obj);
2037 const UID uid = { (ID) &obj, size };
2038 return uid;
2039 }
2040
2041 // UID resolver implementation for pointer types (of 1st degree)
2042 template<typename T>
2043 UID UID::Resolver<T*>::resolve(const T* const & obj) {
2044 const size_t size = DataType::sizeOf(*obj);
2045 const UID uid = { (ID) obj, size };
2046 return uid;
2047 }
2048
2049 // Manual registration of native data types by application.
2050 template<typename T>
2052 static_assert(
2053 std::is_default_constructible<T>::value,
2054 "missing default constructor for type: registration of data "
2055 "types is only needed for creating instances of that type at "
2056 "runtime by this framework's reflection API; which in turn "
2057 "requires the type to provide a default constructor."
2058 );
2059 const DataType staticType = DataType::dataType<T>(false);
2060 assert(staticType);
2061 const auto itType = m_nativeTypes.find(staticType.internalID());
2062 if (itType != m_nativeTypes.end()) {
2063 assert(itType->second.size >= sizeof(T));
2064 return;
2065 }
2066
2067 // register lambda function for allocating a new instance of this type
2068 m_nativeTypes[staticType.internalID()] = {
2069 /*.size =*/ sizeof(T),
2070 /*.allocFn =*/ [](Archive* archive) -> Object {
2071 T* instance = new T;
2072 Archive::SerializationRecursion<T>::serializeObject(archive, *instance);
2073 const UIDChain uids = Archive::UIDChainResolver<T>(*instance);
2074 Object& obj = archive->objectByUID(uids[0]);
2075 if (obj) return obj;
2076 const DataType type = DataType::dataTypeOf(*instance);
2077 return Object(uids, type, Object());
2078 }
2079 };
2080 }
2081
2082 // Automatic self-registration of native data types by this framework.
2083 // (SFINAE variant for types WITH default constructor)
2084 template<typename T, typename std::enable_if<
2085 !std::is_pointer<T>::value &&
2086 std::is_default_constructible<T>::value, bool>::type>
2087 void DataType::registerNativeDataType(const DataType& rttiType, const T& nativeData) {
2088 if (!rttiType) return;
2089 if (m_nativeTypes.find(rttiType.internalID()) != m_nativeTypes.end())
2090 return;
2091
2092 // Ensure compile-time native data type 'T' matches passed runtime
2093 // native data type 'nativeData', because we can only offer to create
2094 // new instances of native data types by our reflection API if this
2095 // framework is aware about the data type already. Usually this happens
2096 // automatically without applications having to do anything. But
2097 // this only works for types this framework gets in touch with at
2098 // compile-time.
2099 //
2100 // Root problem of all of this: ATM there is no way in C++ to create new
2101 // instances of a type by using RTTI (typeid(), std::type_info, etc.).
2102 // If there is a way in a future C++ version then we can get rid of
2103 // manual type registration altogether. Bur for now we must use new T,
2104 // which is a compile-time only construct.
2105 //
2106 // See the discussion in DataType::registerNativeDataType() for an
2107 // example where manual type registration would be required.
2108 const DataType staticType = DataType::dataType<T>(false);
2109 if (staticType != rttiType) {
2110 fprintf(stderr, "Serialization Failure: run-time data type '%s' does not match compile-time data type '%s'!\n",
2111 rttiType.asLongDescr().c_str(),
2112 staticType.asLongDescr().c_str());
2113 assert(false && "You may need to explicitly register this run-time "
2114 "data type by either calling "
2115 "DataType::registerNativeType<T>() or using class "
2116 "NativeDataTypeRegistry");
2117 }
2118
2119 // register lambda function for allocating a new instance of this type
2120 m_nativeTypes[rttiType.internalID()] = {
2121 /*.size =*/ sizeof(T),
2122 /*.allocFn =*/ [](Archive* archive) -> Object {
2123 T* instance = new T;
2124 Archive::SerializationRecursion<T>::serializeObject(archive, *instance);
2125 const UIDChain uids = Archive::UIDChainResolver<T>(*instance);
2126 Object& obj = archive->objectByUID(uids[0]);
2127 if (obj) return obj;
2128 const DataType type = DataType::dataTypeOf(*instance);
2129 return Object(uids, type, Object());
2130 }
2131 };
2132 }
2133
2134 // Automatic self-registration of native data types by this framework.
2135 // (SFINAE variant for types WITHOUT default constructor)
2136 template<typename T, typename std::enable_if<
2137 !std::is_pointer<T>::value &&
2138 !std::is_default_constructible<T>::value, bool>::type>
2139 void DataType::registerNativeDataType(const DataType& type, const T& nativeData) {
2140 if (!type) return;
2141 if (m_nativeTypes.find(type.internalID()) != m_nativeTypes.end())
2142 return;
2143 m_nativeTypes[type.internalID()] = {
2144 /*.size =*/ sizeof(T),
2145 /*.allocFn =*/ [](Archive*) -> Object {
2146 assert(false && "instance not possible: native data type does not have default constructor");
2147 return Object();
2148 }
2149 };
2150 }
2151
2152 // Automatic self-registration of native data types by this framework.
2153 // (SFINAE variant for pointer types of 1st degree WITH default constructor)
2154 template<typename T, typename std::enable_if<
2155 std::is_default_constructible<T>::value, bool>::type>
2157 const T* const& nativeData)
2158 {
2159 // register static type
2160 T unused = T();
2161 const DataType staticType = DataType::Resolver<T>::resolve(unused);
2162 registerNativeDataType(staticType, unused);
2163 // register rtti type
2164 if (!nativeData) return;
2165 const DataType rttiType = DataType::Resolver<T>::resolve(*nativeData);
2166 registerNativeDataType(rttiType, *nativeData);
2167 }
2168
2169 // Automatic self-registration of native data types by this framework.
2170 // (SFINAE variant for pointer types of 1st degree WITHOUT default constructor)
2171 template<typename T, typename std::enable_if<
2172 !std::is_default_constructible<T>::value, bool>::type>
2174 const T* const& nativeData)
2175 {
2176 // register rtti type only
2177 if (!nativeData) return;
2178 const DataType nonPointerType = DataType::Resolver<T>::resolve(*nativeData);
2179 registerNativeDataType(nonPointerType, *nativeData);
2180 }
2181
2182 // DataType resolver implementation for primitive / built-in types
2183 template<typename T, bool T_isPointer>
2184 DataType DataType::ResolverBase<T,T_isPointer>::resolve(const T& data) {
2185 // for pointers we must stick to the compile-time declared type
2186 // of pointer to prevent potential type mismatches caused by
2187 // RTTI in a polymorphic scenario
2188 const std::type_info& type = (T_isPointer) ? typeid(T) : typeid(data);
2189 const int sz = sizeof(data);
2190
2191 // for primitive types we are using our own type names instead of
2192 // using std:::type_info::name(), because the precise output of the
2193 // latter may vary between compilers
2194 if (type == typeid(char))
2195 // special case: C++ defines "char" to be neither type
2196 // equivalent to "signed char" nor to "unsigned char",
2197 // because e.g. char's signedness is implementation specific
2198 return DataType(T_isPointer, sz, "char");
2199 if (type == typeid(int8_t)) return DataType(T_isPointer, sz, "int8");
2200 if (type == typeid(uint8_t)) return DataType(T_isPointer, sz, "uint8");
2201 if (type == typeid(int16_t)) return DataType(T_isPointer, sz, "int16");
2202 if (type == typeid(uint16_t)) return DataType(T_isPointer, sz, "uint16");
2203 if (type == typeid(int32_t)) return DataType(T_isPointer, sz, "int32");
2204 if (type == typeid(uint32_t)) return DataType(T_isPointer, sz, "uint32");
2205 if (type == typeid(int64_t)) return DataType(T_isPointer, sz, "int64");
2206 if (type == typeid(uint64_t)) return DataType(T_isPointer, sz, "uint64");
2207 // CAUTION: depending on the system, "long" or "int" types may not
2208 // already been covered by the typedefs above
2209 if (type == typeid(long) || type == typeid(int)) {
2210 if (sz == 1) return DataType(T_isPointer, sz, "int8");
2211 if (sz == 2) return DataType(T_isPointer, sz, "int16");
2212 if (sz == 4) return DataType(T_isPointer, sz, "int32");
2213 if (sz == 8) return DataType(T_isPointer, sz, "int64");
2214 else assert(false /* unknown int / long size */);
2215 }
2216 if (type == typeid(unsigned long) || type == typeid(unsigned int)) {
2217 if (sz == 1) return DataType(T_isPointer, sz, "uint8");
2218 if (sz == 2) return DataType(T_isPointer, sz, "uint16");
2219 if (sz == 4) return DataType(T_isPointer, sz, "uint32");
2220 if (sz == 8) return DataType(T_isPointer, sz, "uint64");
2221 else assert(false /* unknown unsigned int / unsigned long size */);
2222 }
2223 if (type == typeid(bool)) return DataType(T_isPointer, sz, "bool");
2224 if (type == typeid(float)) return DataType(T_isPointer, sz, "real32");
2225 if (type == typeid(double)) return DataType(T_isPointer, sz, "real64");
2226 if (type == typeid(String)) return DataType(T_isPointer, sz, "String");
2227
2228 // for pointers we must stick to pointer's compile-time declared
2229 // type and not use RTTI for the pointed object, because the
2230 // latter would easily create a type mismatch conflict for
2231 // pointers and objects in a polymorphic scenario, because the
2232 // pointer might have been declared with a base class type,
2233 // while actually assigned objects to these pointers might be of
2234 // any other derived class and RTTI would resolve the latter
2235 const String rawCppName =
2236 (T_isPointer) ? rawCppTypeName<T>()
2237 : rawCppTypeNameOf(data);
2238
2239 if (IsEnum(data)) return DataType(T_isPointer, sz, "enum", rawCppName);
2240 if (IsUnion(data)) return DataType(T_isPointer, sz, "union", rawCppName);
2241 if (IsClass(data)) return DataType(T_isPointer, sz, "class", rawCppName);
2242
2243 assert(false /* unknown data type*/);
2244 return DataType();
2245 }
2246
2251 class Exception {
2252 public:
2253 String Message;
2254
2255 Exception(String format, ...);
2256 Exception(String format, va_list arg);
2257 void PrintMessage() const;
2258 virtual ~Exception() {}
2259
2260 protected:
2261 Exception();
2262 static String assemble(String format, va_list arg);
2263 };
2264
2265} // namespace Serialization
2266
2267#endif // LIBGIG_SERIALIZATION_H
Synchronizes 2 archives with each other.
Destination container for serialization, and source container for deserialization.
void setStringValue(Object &object, String value)
Set new textual string for given String object.
void setRealValue(Object &object, double value)
Set new floating point value for given floating point object.
void setCharValue(Object &object, char value)
Set new char value for given character object.
char valueAsChar(const Object &object)
Get char value of object.
void serializeMember(const T_classType &nativeObject, const T_memberType &nativeMember, const char *memberName)
Serialize a native C/C++ member variable.
void setMinVersion(const T_classType &nativeObject, Version v)
Set a minimum version number for your C++ class.
void setName(String name)
Assign a name to this archive.
void serialize(const T *obj)
Initiate serialization.
time_t timeStampCreated() const
Date and time when this archive was initially created.
void setBoolValue(Object &object, bool value)
Set new boolean value for given boolean object.
void clear()
Clear content of this archive.
double valueAsReal(const Object &object)
Get floating point value of object.
Archive(format_t format=FORMAT_AUTO)
Create an "empty" archive.
time_t timeStampModified() const
Date and time when this archive was modified for the last time.
virtual String rawDataFormat() const
Name of the encoding format used by this Archive class.
void setIntValue(Object &object, int64_t value)
Set new integer value for given integer object.
operation_t
Current activity of Archive object.
@ OPERATION_DESERIALIZE
Archive is currently deserializing.
@ OPERATION_NONE
Archive is currently neither serializing, nor deserializing.
@ OPERATION_SERIALIZE
Archive is currently serializing.
const RawData & rawData()
Raw data stream of this archive content.
bool isModified() const
Whether this archive was modified.
void deserialize(T *obj)
Initiate deserialization.
virtual void decode(const RawData &data)
Fill this archive with the given serialized raw data.
void serializeAnonymousObject(const T_objectType &heapObject)
Serialize anonymous C/C++ data.
tm dateTimeCreated(time_base_t base=LOCAL_TIME) const
Date and time when this archive was initially created.
Object & rootObject()
Root C++ object of this archive.
Object & objectByUID(const UID &uid)
Access object by its unique identifier.
String valueAsString(const Object &object)
Get value of object as string.
int64_t valueAsInt(const Object &object)
Get integer value of object.
void setVersion(const T_classType &nativeObject, Version v)
Set current version number for your C++ class.
Object & objectByBaseUID(const UID &uid)
Find true object for a base pointer.
void removeMember(Object &parent, const Member &member)
Remove a member variable from the given object.
void remove(const Object &obj)
Remove an object from this archive.
bool valueAsBool(const Object &object)
Get boolean value of object.
void setEnumValue(Object &object, uint64_t value)
Set new value for given enum object.
void serializeHeapMember(const T_classType &nativeObject, const T_memberType &heapMember, const char *memberName)
Serialize a C/C++ member variable allocated on the heap.
Object & parentObjectOf(const Object &obj)
Access parent of supplied object.
void operator<<(const T &obj)
Initiate serialization of your C++ objects.
void setComment(String comment)
Assign a comment to this archive.
format_t
Encoding format of serialized data stream.
@ FORMAT_AUTO
Automatically handle the encoding format:
@ FORMAT_SRX_JSON
Use and expect "Srx-JSON" encoding format (user-friendly to read and modify).
@ FORMAT_SRX
Use and expect "Srx" encoding format (simple, reliable, fast).
String name() const
Optional name of this archive.
void setAutoValue(Object &object, String value)
Automatically cast and assign appropriate value to object.
String comment() const
Optional comments for this archive.
void operator>>(T &obj)
Initiate deserialization of your C++ objects.
tm dateTimeModified(time_base_t base=LOCAL_TIME) const
Date and time when this archive was modified for the last time.
Abstract reflection of a native C++ data type.
bool isPrimitive() const
Whether this is reflecting a fundamental C/C++ data type.
bool isSet() const
Whether this is a C++ Set<> object type.
static DataType dataTypeOf(const T &data, bool registerType=true)
Construct a DataType object for the given native C++ data.
bool isNumber() const
Whether this is a numeric C/C++ data type.
bool isPointer() const
Whether this is reflecting a C/C++ pointer type.
bool isSigned() const
Whether this is a signed integer C/C++ data type.
String baseTypeName() const
The base type name of this data type.
bool operator!=(const DataType &other) const
Comparison for inequalness.
bool isReal() const
Whether this is a floating point based C/C++ data type.
DataType()
Default constructor (as "invalid" DataType).
static void registerNativeDataType()
Manual registration of native C++ data types.
String asLongDescr() const
Human readable long description for this data type.
bool isBool() const
Whether this is a boolean C/C++ data type.
static size_t sizeOf(const T &data)
True size of passed native data in bytes.
bool isMap() const
Whether this is a C++ Map<> object type.
bool isValid() const
Check if this is a valid DataType object.
bool isArray() const
Whether this is a C++ Array<> object type.
bool operator>(const DataType &other) const
Greater than comparison.
bool isEnum() const
Whether this is a C/C++ enum data type.
bool operator<(const DataType &other) const
Smaller than comparison.
bool isChar() const
Whether this is a character C/C++ data type.
String customTypeName2(bool demangle=false) const
The 2nd user defined C/C++ data type name of this data type.
bool isClass() const
Whether this is reflecting a C/C++ struct or class type.
bool isInteger() const
Whether this is an integer C/C++ data type.
bool operator==(const DataType &other) const
Comparison for equalness.
String internalID() const
Unique key for native data type, for internal purposes only.
static DataType dataType(bool registerType=true)
Construct a DataType object for the given native C++ type.
bool isString() const
Whether this is a C++ String data type.
String customTypeName(bool demangle=false) const
The 1st user defined C/C++ data type name of this data type.
size_t size() const
Returns native memory size of the respective C++ object or variable.
Object newInstance(Archive *archive) const
Allocate and initialize a native instance of data type.
Will be thrown whenever an error occurs during an serialization or deserialization process.
void PrintMessage() const
Print exception message to stdout.
Abstract reflection of a native C++ class/struct's member variable.
UID parentUID() const
Unique identifier of parent object.
Member()
Default constructor.
bool operator!=(const Member &other) const
Comparison for inequalness.
bool operator<(const Member &other) const
Smaller than comparison.
bool operator>(const Member &other) const
Greater than comparison.
ssize_t offset() const
Offset of member in its containing parent data structure.
String name() const
Name of the member.
bool operator==(const Member &other) const
Comparison for equalness.
const DataType & type() const
C/C++ Data type of this member.
bool isValid() const
Check if this is a valid Member object.
UID uid() const
Unique identifier of this member instance.
Abstract reflection of some native serialized C/C++ data.
bool isValid() const
Check if this is a valid Object instance.
Member memberNamed(String name) const
Get the member of this Object with given name.
Version version() const
Version of original user defined C/C++ struct or class.
UID uid(int index=0) const
Unique identifier of this Object.
const UIDChain & uidChain() const
Unique identifier chain of this Object.
const RawData & rawData() const
Raw data of the original native C/C++ data.
bool operator<(const Object &other) const
Smaller than comparison.
Object()
Default constructor (for an "invalid" Object).
Member memberByUID(const UID &uid) const
Get the member of this Object with given unique identifier.
std::vector< Member > membersOfType(const DataType &type) const
Get all members of this Object with given data type.
int sequenceIndexOf(const Member &member) const
Serialization/deserialization sequence number of the requested member.
void setNativeValueFromString(const String &s)
Cast from string to object's data type and assign value natively.
bool operator!=(const Object &other) const
Comparison for inequalness.
bool operator>(const Object &other) const
Greater than comparison.
UID parentUID() const
Unique identifier of parent object.
std::vector< Member > & members()
All members of the original native C/C++ struct or class instance.
const DataType & type() const
C/C++ data type this Object is reflecting.
bool isVersionCompatibleTo(const Object &other) const
Check version compatibility between Object instances.
Version minVersion() const
Minimum version of original user defined C/C++ struct or class.
bool operator==(const Object &other) const
Comparison for equalness.
Unique identifier referring to one specific native C++ object, member, fundamental variable,...
static UID from(const T &obj)
Create an unique indentifier for a native C++ object/member/variable.
bool isValid() const
Check whether this is a valid unique identifier.
size_t size
Memory size of the object or member in question.
ID id
Abstract non-unique ID of the object or member in question.
C++ Serialization / Deserialization Framework.
Definition gig.h:98
bool IsUnion(const T &)
Check whether data is a C++ union type.
void * ID
Abstract identifier for serialized C++ objects.
bool IsClass(const T &)
Check whether data is a C/C++ struct or C++ class type.
std::vector< T > Array
Array<> template.
const UID NO_UID
Reflects an invalid UID and behaves similar to NULL as invalid value for pointer types.
std::set< T > Set
Set<> template.
std::string String
Textual string.
std::map< T_key, T_value > Map
Map<> template.
bool IsEnum(const T &)
Check whether data is a C/C++ enum type.
uint32_t Version
Version number data type.
std::vector< UID > UIDChain
Chain of UIDs.
std::vector< uint8_t > RawData
Raw data stream of serialized C++ objects.
time_base_t
To which time zone a certain timing information relates to.
@ UTC_TIME
The time stamp relates to "Greenwhich Mean Time" zone, also known as "Coordinated Universal Time"....
@ LOCAL_TIME
The time stamp relates to the machine's local time zone. Request a time stamp in local time if you wa...