36 |
#include <map> |
#include <map> |
37 |
#include <time.h> |
#include <time.h> |
38 |
|
|
39 |
|
#ifndef __has_extension |
40 |
|
# define __has_extension(x) 0 |
41 |
|
#endif |
42 |
|
|
43 |
|
#ifndef HAS_BUILTIN_TYPE_TRAITS |
44 |
|
# if __cplusplus >= 201103L |
45 |
|
# define HAS_BUILTIN_TYPE_TRAITS 1 |
46 |
|
# elif ( __has_extension(is_class) && __has_extension(is_enum) ) |
47 |
|
# define HAS_BUILTIN_TYPE_TRAITS 1 |
48 |
|
# elif ( __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 3 ) ) |
49 |
|
# define HAS_BUILTIN_TYPE_TRAITS 1 |
50 |
|
# elif _MSC_VER >= 1400 /* MS Visual C++ 8.0 (Visual Studio 2005) */ |
51 |
|
# define HAS_BUILTIN_TYPE_TRAITS 1 |
52 |
|
# elif __INTEL_COMPILER >= 1100 |
53 |
|
# define HAS_BUILTIN_TYPE_TRAITS 1 |
54 |
|
# else |
55 |
|
# define HAS_BUILTIN_TYPE_TRAITS 0 |
56 |
|
# endif |
57 |
|
#endif |
58 |
|
|
59 |
|
#if !HAS_BUILTIN_TYPE_TRAITS |
60 |
|
# include <tr1/type_traits> |
61 |
|
# define LIBGIG_IS_CLASS(type) std::tr1::__is_union_or_class<type>::value //NOTE: without compiler support we cannot distinguish union from class |
62 |
|
#else |
63 |
|
# define LIBGIG_IS_CLASS(type) __is_class(type) |
64 |
|
#endif |
65 |
|
|
66 |
/** @brief Serialization / deserialization framework. |
/** @brief Serialization / deserialization framework. |
67 |
* |
* |
68 |
* See class Archive as starting point for how to implement serialization and |
* See class Archive as starting point for how to implement serialization and |
124 |
|
|
125 |
typedef uint32_t Version; |
typedef uint32_t Version; |
126 |
|
|
|
enum operation_t { |
|
|
OPERATION_NONE, |
|
|
OPERATION_SERIALIZE, |
|
|
OPERATION_DESERIALIZE |
|
|
}; |
|
|
|
|
127 |
enum time_base_t { |
enum time_base_t { |
128 |
LOCAL_TIME, |
LOCAL_TIME, |
129 |
UTC_TIME |
UTC_TIME |
131 |
|
|
132 |
template<typename T> |
template<typename T> |
133 |
bool IsEnum(const T& data) { |
bool IsEnum(const T& data) { |
134 |
|
#if !HAS_BUILTIN_TYPE_TRAITS |
135 |
|
return std::tr1::is_enum<T>::value; |
136 |
|
#else |
137 |
return __is_enum(T); |
return __is_enum(T); |
138 |
|
#endif |
139 |
} |
} |
140 |
|
|
141 |
template<typename T> |
template<typename T> |
142 |
bool IsUnion(const T& data) { |
bool IsUnion(const T& data) { |
143 |
|
#if !HAS_BUILTIN_TYPE_TRAITS |
144 |
|
return false; // without compiler support we cannot distinguish union from class |
145 |
|
#else |
146 |
return __is_union(T); |
return __is_union(T); |
147 |
|
#endif |
148 |
} |
} |
149 |
|
|
150 |
template<typename T> |
template<typename T> |
151 |
bool IsClass(const T& data) { |
bool IsClass(const T& data) { |
152 |
|
#if !HAS_BUILTIN_TYPE_TRAITS |
153 |
|
return std::tr1::__is_union_or_class<T>::value; // without compiler support we cannot distinguish union from class |
154 |
|
#else |
155 |
return __is_class(T); |
return __is_class(T); |
156 |
|
#endif |
157 |
} |
} |
158 |
|
|
159 |
/*template<typename T> |
/*template<typename T> |
195 |
template<typename T> |
template<typename T> |
196 |
struct Resolver { |
struct Resolver { |
197 |
static UID resolve(const T& obj) { |
static UID resolve(const T& obj) { |
198 |
return (UID) { (ID) &obj, sizeof(obj) }; |
const UID uid = { (ID) &obj, sizeof(obj) }; |
199 |
|
return uid; |
200 |
} |
} |
201 |
}; |
}; |
202 |
|
|
204 |
template<typename T> |
template<typename T> |
205 |
struct Resolver<T*> { |
struct Resolver<T*> { |
206 |
static UID resolve(const T* const & obj) { |
static UID resolve(const T* const & obj) { |
207 |
return (UID) { (ID) obj, sizeof(*obj) }; |
const UID uid = { (ID) obj, sizeof(*obj) }; |
208 |
|
return uid; |
209 |
} |
} |
210 |
}; |
}; |
211 |
}; |
}; |
225 |
static Object _popObjectBlob(const char*& p, const char* end); |
static Object _popObjectBlob(const char*& p, const char* end); |
226 |
static void _popPrimitiveValue(const char*& p, const char* end, Object& obj); |
static void _popPrimitiveValue(const char*& p, const char* end, Object& obj); |
227 |
static String _primitiveObjectValueToString(const Object& obj); |
static String _primitiveObjectValueToString(const Object& obj); |
228 |
|
// | |
229 |
|
template<typename T> |
230 |
|
static T _primitiveObjectValueToNumber(const Object& obj); |
231 |
|
|
232 |
/** @brief Abstract reflection of a native C++ data type. |
/** @brief Abstract reflection of a native C++ data type. |
233 |
* |
* |
257 |
bool operator>(const DataType& other) const; |
bool operator>(const DataType& other) const; |
258 |
String asLongDescr() const; |
String asLongDescr() const; |
259 |
String baseTypeName() const { return m_baseTypeName; } |
String baseTypeName() const { return m_baseTypeName; } |
260 |
String customTypeName() const { return m_customTypeName; } |
String customTypeName(bool demangle = false) const; |
261 |
|
|
262 |
template<typename T> |
template<typename T> |
263 |
static DataType dataTypeOf(const T& data) { |
static DataType dataTypeOf(const T& data) { |
388 |
const UIDChain& uidChain() const { return m_uid; } |
const UIDChain& uidChain() const { return m_uid; } |
389 |
const DataType& type() const { return m_type; } |
const DataType& type() const { return m_type; } |
390 |
const RawData& rawData() const { return m_data; } |
const RawData& rawData() const { return m_data; } |
|
|
|
391 |
Version version() const { return m_version; } |
Version version() const { return m_version; } |
|
|
|
|
void setVersion(Version v) { |
|
|
m_version = v; |
|
|
} |
|
|
|
|
392 |
Version minVersion() const { return m_minVersion; } |
Version minVersion() const { return m_minVersion; } |
|
|
|
|
void setMinVersion(Version v) { |
|
|
m_minVersion = v; |
|
|
} |
|
|
|
|
393 |
bool isVersionCompatibleTo(const Object& other) const; |
bool isVersionCompatibleTo(const Object& other) const; |
|
|
|
394 |
std::vector<Member>& members() { return m_members; } |
std::vector<Member>& members() { return m_members; } |
395 |
const std::vector<Member>& members() const { return m_members; } |
const std::vector<Member>& members() const { return m_members; } |
396 |
Member memberNamed(String name) const; |
Member memberNamed(String name) const; |
407 |
|
|
408 |
protected: |
protected: |
409 |
void remove(const Member& member); |
void remove(const Member& member); |
410 |
|
void setVersion(Version v); |
411 |
|
void setMinVersion(Version v); |
412 |
|
|
413 |
private: |
private: |
414 |
DataType m_type; |
DataType m_type; |
422 |
friend Object _popObjectBlob(const char*& p, const char* end); |
friend Object _popObjectBlob(const char*& p, const char* end); |
423 |
friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj); |
friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj); |
424 |
friend String _primitiveObjectValueToString(const Object& obj); |
friend String _primitiveObjectValueToString(const Object& obj); |
425 |
|
|
426 |
|
template<typename T> |
427 |
|
friend T _primitiveObjectValueToNumber(const Object& obj); |
428 |
|
|
429 |
friend class Archive; |
friend class Archive; |
430 |
}; |
}; |
431 |
|
|
541 |
* Note that there is only one method that you need to implement. So the |
* Note that there is only one method that you need to implement. So the |
542 |
* respective serialize() method implementation of your classes/structs are |
* respective serialize() method implementation of your classes/structs are |
543 |
* both called for serialization, as well as for deserialization! |
* both called for serialization, as well as for deserialization! |
544 |
|
* |
545 |
|
* In case you need to enforce backward incompatiblity for one of your C++ |
546 |
|
* classes, you can do so by setting a version and minimum version for your |
547 |
|
* class (see @c setVersion() and @c setMinVersion() for details). |
548 |
*/ |
*/ |
549 |
class Archive { |
class Archive { |
550 |
public: |
public: |
590 |
template<typename T_classType, typename T_memberType> |
template<typename T_classType, typename T_memberType> |
591 |
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) { |
592 |
const size_t offset = |
const size_t offset = |
593 |
((const uint8_t*)(const void*)&nativeMember) - |
((const uint8_t*)(const void*)&nativeMember) - |
594 |
((const uint8_t*)(const void*)&nativeObject); |
((const uint8_t*)(const void*)&nativeObject); |
595 |
const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember); |
const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember); |
596 |
const DataType type = DataType::dataTypeOf(nativeMember); |
const DataType type = DataType::dataTypeOf(nativeMember); |
597 |
const Member member(memberName, uids[0], offset, type); |
const Member member(memberName, uids[0], offset, type); |
615 |
} |
} |
616 |
} |
} |
617 |
|
|
618 |
|
/** @brief Set version number for your C++ class. |
619 |
|
* |
620 |
|
* By calling this method you can store a version number for your |
621 |
|
* current C++ class (that is a version for its current data structure |
622 |
|
* layout and method implementations) with serialized archive. |
623 |
|
* |
624 |
|
* Along with calling @c setMinVersion() this provides a way for you |
625 |
|
* to constrain backward compatiblity regarding serialization and |
626 |
|
* deserialization of your class which the Archive class will obey to. |
627 |
|
* If required, then typically you might do so in your @c serialize() |
628 |
|
* method implementation like: |
629 |
|
* @code |
630 |
|
* #define SRLZ(member) \ |
631 |
|
* archive->serializeMember(*this, member, #member); |
632 |
|
* |
633 |
|
* void Foo::serialize(Serialization::Archive* archive) { |
634 |
|
* // when serializing: the current version of this class that is |
635 |
|
* // going to be stored with the serialized archive |
636 |
|
* archive->setVersion(*this, 6); |
637 |
|
* // when deserializing: the minimum allowed version of this class |
638 |
|
* // being serialized in the past |
639 |
|
* archive->setMinVersion(*this, 3); |
640 |
|
* // actual data mebers to serialize / deserialize |
641 |
|
* SRLZ(a); |
642 |
|
* SRLZ(b); |
643 |
|
* SRLZ(c); |
644 |
|
* } |
645 |
|
* @endcode |
646 |
|
* In this example above, the C++ clas "Foo" would be serialized along |
647 |
|
* with the version number @c 6 in the resulting archive (and its raw |
648 |
|
* data stream respectively). |
649 |
|
* |
650 |
|
* When deserializing archives with the example C++ class code above, |
651 |
|
* the Archive object would check whether your originally serialized |
652 |
|
* C++ "Foo" object had at least version number @c 3, if not the |
653 |
|
* deserialization process would automatically be stopped with a |
654 |
|
* @c Serialization::Exception, claiming that the classes are version |
655 |
|
* incompatible. |
656 |
|
* |
657 |
|
* Since this Serialization / deserialization framework is designed to |
658 |
|
* be robust on changes to your C++ classes and aims trying to |
659 |
|
* deserialize all your C++ objects correctly even if your C++ classes |
660 |
|
* have seen substantial software changes in the meantime; you might |
661 |
|
* sometimes see it as necessary to constrain backward compatiblity |
662 |
|
* this way. |
663 |
|
* |
664 |
|
* @param nativeObject - your C++ object you want to set a version for |
665 |
|
* @param v - the version number to set for your C++ class (by default, |
666 |
|
* that is if you do not explicitly call this method, then |
667 |
|
* your C++ object will be stored with version number @c 0 ). |
668 |
|
*/ |
669 |
|
template<typename T_classType> |
670 |
|
void setVersion(const T_classType& nativeObject, Version v) { |
671 |
|
const UID uid = UID::from(nativeObject); |
672 |
|
Object& obj = m_allObjects[uid]; |
673 |
|
if (!obj) { |
674 |
|
const UIDChain uids = UIDChainResolver<T_classType>(nativeObject); |
675 |
|
const DataType type = DataType::dataTypeOf(nativeObject); |
676 |
|
obj = Object(uids, type); |
677 |
|
} |
678 |
|
setVersion(obj, v); |
679 |
|
} |
680 |
|
|
681 |
|
/** @brief Set a minimum version number for your C++ class. |
682 |
|
* |
683 |
|
* Call this method to define a minimum version that your current C++ |
684 |
|
* class implementation would be compatible with when it comes to |
685 |
|
* deserialization of an archive containing an object with an older |
686 |
|
* version of your C++ class. |
687 |
|
* |
688 |
|
* @see @c setVersion() for more details about this overall topic. |
689 |
|
*/ |
690 |
|
template<typename T_classType> |
691 |
|
void setMinVersion(const T_classType& nativeObject, Version v) { |
692 |
|
const UID uid = UID::from(nativeObject); |
693 |
|
Object& obj = m_allObjects[uid]; |
694 |
|
if (!obj) { |
695 |
|
const UIDChain uids = UIDChainResolver<T_classType>(nativeObject); |
696 |
|
const DataType type = DataType::dataTypeOf(nativeObject); |
697 |
|
obj = Object(uids, type); |
698 |
|
} |
699 |
|
setMinVersion(obj, v); |
700 |
|
} |
701 |
|
|
702 |
virtual void decode(const RawData& data); |
virtual void decode(const RawData& data); |
703 |
virtual void decode(const uint8_t* data, size_t size); |
virtual void decode(const uint8_t* data, size_t size); |
704 |
void clear(); |
void clear(); |
713 |
void setBoolValue(Object& object, bool value); |
void setBoolValue(Object& object, bool value); |
714 |
void setEnumValue(Object& object, uint64_t value); |
void setEnumValue(Object& object, uint64_t value); |
715 |
String valueAsString(const Object& object); |
String valueAsString(const Object& object); |
716 |
|
int64_t valueAsInt(const Object& object); |
717 |
|
double valueAsReal(const Object& object); |
718 |
|
bool valueAsBool(const Object& object); |
719 |
|
void setVersion(Object& object, Version v); |
720 |
|
void setMinVersion(Object& object, Version v); |
721 |
String name() const; |
String name() const; |
722 |
void setName(String name); |
void setName(String name); |
723 |
String comment() const; |
String comment() const; |
747 |
class UIDChainResolver<T*> { |
class UIDChainResolver<T*> { |
748 |
public: |
public: |
749 |
UIDChainResolver(const T*& data) { |
UIDChainResolver(const T*& data) { |
750 |
m_uid.push_back((UID) { &data, sizeof(data) }); |
const UID uids[2] = { |
751 |
m_uid.push_back((UID) { data, sizeof(*data) }); |
{ &data, sizeof(data) }, |
752 |
|
{ data, sizeof(*data) } |
753 |
|
}; |
754 |
|
m_uid.push_back(uids[0]); |
755 |
|
m_uid.push_back(uids[1]); |
756 |
} |
} |
757 |
|
|
758 |
operator UIDChain() const { return m_uid; } |
operator UIDChain() const { return m_uid; } |
792 |
|
|
793 |
// Automatically handles recursion for class/struct types, while ignoring all primitive types. |
// Automatically handles recursion for class/struct types, while ignoring all primitive types. |
794 |
template<typename T> |
template<typename T> |
795 |
struct SerializationRecursion : SerializationRecursionImpl<T, __is_class(T)> { |
struct SerializationRecursion : SerializationRecursionImpl<T, LIBGIG_IS_CLASS(T)> { |
796 |
}; |
}; |
797 |
|
|
798 |
class ObjectPool : public std::map<UID,Object> { |
class ObjectPool : public std::map<UID,Object> { |
830 |
Archive& m_src; |
Archive& m_src; |
831 |
}; |
}; |
832 |
|
|
833 |
|
enum operation_t { |
834 |
|
OPERATION_NONE, |
835 |
|
OPERATION_SERIALIZE, |
836 |
|
OPERATION_DESERIALIZE |
837 |
|
}; |
838 |
|
|
839 |
virtual void encode(); |
virtual void encode(); |
840 |
|
|
841 |
ObjectPool m_allObjects; |
ObjectPool m_allObjects; |