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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3150 - (show annotations) (download) (as text)
Fri May 5 18:42:06 2017 UTC (6 years, 10 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 27792 byte(s)
* Serialization.cpp/.h: Added new methods isModified(),
  setAutoValue(), setIntValue(), setRealValue(),
  setBoolValue(), setEnumValue(), valueAsString() to
  class Archive.
* Serialization.cpp/.h: Archive::rawData(): Automatically
  re-encode new raw data stream if archive had been
  modified (i.e. by remove(), setAutoValue(), etc.).
* Bumped version (4.0.0.svn16).

1 /***************************************************************************
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 // just symbol prototyping
84 class DataType;
85 class Object;
86 class Member;
87 class Archive;
88 class ObjectPool;
89 class Exception;
90
91 typedef std::string String;
92
93 typedef std::vector<uint8_t> RawData;
94
95 typedef void* ID;
96
97 typedef uint32_t Version;
98
99 enum operation_t {
100 OPERATION_NONE,
101 OPERATION_SERIALIZE,
102 OPERATION_DESERIALIZE
103 };
104
105 template<typename T>
106 bool IsEnum(const T& data) {
107 return __is_enum(T);
108 }
109
110 template<typename T>
111 bool IsUnion(const T& data) {
112 return __is_union(T);
113 }
114
115 template<typename T>
116 bool IsClass(const T& data) {
117 return __is_class(T);
118 }
119
120 /*template<typename T>
121 bool IsTrivial(T data) {
122 return __is_trivial(T);
123 }*/
124
125 /*template<typename T>
126 bool IsPOD(T data) {
127 return __is_pod(T);
128 }*/
129
130 /** @brief Unique identifier for one specific C++ object, member or fundamental variable.
131 *
132 * Reflects a unique identifier for one specific serialized C++ class
133 * instance, struct instance, member, primitive pointer, or fundamental
134 * variables.
135 */
136 class UID {
137 public:
138 ID id;
139 size_t size;
140
141 bool isValid() const;
142 operator bool() const { return isValid(); }
143 //bool operator()() const { return isValid(); }
144 bool operator==(const UID& other) const { return id == other.id && size == other.size; }
145 bool operator!=(const UID& other) const { return id != other.id || size != other.size; }
146 bool operator<(const UID& other) const { return id < other.id || (id == other.id && size < other.size); }
147 bool operator>(const UID& other) const { return id > other.id || (id == other.id && size > other.size); }
148
149 template<typename T>
150 static UID from(const T& obj) {
151 return Resolver<T>::resolve(obj);
152 }
153
154 protected:
155 // UID resolver for non-pointer types
156 template<typename T>
157 struct Resolver {
158 static UID resolve(const T& obj) {
159 return (UID) { (ID) &obj, sizeof(obj) };
160 }
161 };
162
163 // UID resolver for pointer types (of 1st degree)
164 template<typename T>
165 struct Resolver<T*> {
166 static UID resolve(const T* const & obj) {
167 return (UID) { (ID) obj, sizeof(*obj) };
168 }
169 };
170 };
171
172 /**
173 * Reflects an invalid UID and behaves similar to NULL as invalid value for
174 * pointer types.
175 */
176 extern const UID NO_UID;
177
178 typedef std::vector<UID> UIDChain;
179
180 // prototyping of private internal friend functions
181 static String _encodePrimitiveValue(const Object& obj);
182 static DataType _popDataTypeBlob(const char*& p, const char* end);
183 static Member _popMemberBlob(const char*& p, const char* end);
184 static Object _popObjectBlob(const char*& p, const char* end);
185 static void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
186 static String _primitiveObjectValueToString(const Object& obj);
187
188 /** @brief Abstract reflection of a native C++ data type.
189 *
190 * Provides detailed information about a C++ data type, whether it is a
191 * fundamental C/C++ data type (like int, float, char, etc.) or custom
192 * defined data type like a C++ class, struct, enum, as well as other
193 * features of the data type like its native memory size and more.
194 */
195 class DataType {
196 public:
197 DataType();
198 size_t size() const { return m_size; }
199 bool isValid() const;
200 bool isPointer() const;
201 bool isClass() const;
202 bool isPrimitive() const;
203 bool isInteger() const;
204 bool isReal() const;
205 bool isBool() const;
206 bool isEnum() const;
207 bool isSigned() const;
208 operator bool() const { return isValid(); }
209 //bool operator()() const { return isValid(); }
210 bool operator==(const DataType& other) const;
211 bool operator!=(const DataType& other) const;
212 bool operator<(const DataType& other) const;
213 bool operator>(const DataType& other) const;
214 String asLongDescr() const;
215 String baseTypeName() const { return m_baseTypeName; }
216 String customTypeName() const { return m_customTypeName; }
217
218 template<typename T>
219 static DataType dataTypeOf(const T& data) {
220 return Resolver<T>::resolve(data);
221 }
222
223 protected:
224 DataType(bool isPointer, int size, String baseType, String customType = "");
225
226 template<typename T, bool T_isPointer>
227 struct ResolverBase {
228 static DataType resolve(const T& data) {
229 const std::type_info& type = typeid(data);
230 const int sz = sizeof(data);
231
232 // for primitive types we are using our own type names instead of
233 // using std:::type_info::name(), because the precise output of the
234 // latter may vary between compilers
235 if (type == typeid(int8_t)) return DataType(T_isPointer, sz, "int8");
236 if (type == typeid(uint8_t)) return DataType(T_isPointer, sz, "uint8");
237 if (type == typeid(int16_t)) return DataType(T_isPointer, sz, "int16");
238 if (type == typeid(uint16_t)) return DataType(T_isPointer, sz, "uint16");
239 if (type == typeid(int32_t)) return DataType(T_isPointer, sz, "int32");
240 if (type == typeid(uint32_t)) return DataType(T_isPointer, sz, "uint32");
241 if (type == typeid(int64_t)) return DataType(T_isPointer, sz, "int64");
242 if (type == typeid(uint64_t)) return DataType(T_isPointer, sz, "uint64");
243 if (type == typeid(bool)) return DataType(T_isPointer, sz, "bool");
244 if (type == typeid(float)) return DataType(T_isPointer, sz, "real32");
245 if (type == typeid(double)) return DataType(T_isPointer, sz, "real64");
246
247 if (IsEnum(data)) return DataType(T_isPointer, sz, "enum", rawCppTypeNameOf(data));
248 if (IsUnion(data)) return DataType(T_isPointer, sz, "union", rawCppTypeNameOf(data));
249 if (IsClass(data)) return DataType(T_isPointer, sz, "class", rawCppTypeNameOf(data));
250
251 return DataType();
252 }
253 };
254
255 // DataType resolver for non-pointer types
256 template<typename T>
257 struct Resolver : ResolverBase<T,false> {
258 static DataType resolve(const T& data) {
259 return ResolverBase<T,false>::resolve(data);
260 }
261 };
262
263 // DataType resolver for pointer types (of 1st degree)
264 template<typename T>
265 struct Resolver<T*> : ResolverBase<T,true> {
266 static DataType resolve(const T*& data) {
267 return ResolverBase<T,true>::resolve(*data);
268 }
269 };
270
271 template<typename T>
272 static String rawCppTypeNameOf(const T& data) {
273 #if defined _MSC_VER // Microsoft compiler ...
274 # warning type_info::raw_name() demangling has not been tested yet with Microsoft compiler! Feedback appreciated!
275 String name = typeid(data).raw_name(); //NOTE: I haven't checked yet what MSC actually outputs here exactly
276 #else // i.e. especially GCC and clang ...
277 String name = typeid(data).name();
278 #endif
279 //while (!name.empty() && name[0] >= 0 && name[0] <= 9)
280 // name = name.substr(1);
281 return name;
282 }
283
284 private:
285 String m_baseTypeName;
286 String m_customTypeName;
287 int m_size;
288 bool m_isPointer;
289
290 friend DataType _popDataTypeBlob(const char*& p, const char* end);
291 friend class Archive;
292 };
293
294 /** @brief Abstract reflection of a native C++ class/struct's member variable.
295 *
296 * Provides detailed information about a specific C++ member variable of
297 * serialized C++ object, like its C++ data type, offset of this member
298 * within its containing data structure/class, its C++ member variable name
299 * and more.
300 */
301 class Member {
302 public:
303 Member();
304 UID uid() const { return m_uid; }
305 String name() const { return m_name; }
306 size_t offset() const { return m_offset; }
307 const DataType& type() const { return m_type; }
308 bool isValid() const;
309 operator bool() const { return isValid(); }
310 //bool operator()() const { return isValid(); }
311 bool operator==(const Member& other) const;
312 bool operator!=(const Member& other) const;
313 bool operator<(const Member& other) const;
314 bool operator>(const Member& other) const;
315
316 protected:
317 Member(String name, UID uid, size_t offset, DataType type);
318 friend class Archive;
319
320 private:
321 UID m_uid;
322 size_t m_offset;
323 String m_name;
324 DataType m_type;
325
326 friend Member _popMemberBlob(const char*& p, const char* end);
327 };
328
329 /** @brief Abstract reflection of a native C++ class/struct instance.
330 *
331 * Provides detailed information about a specific serialized C++ object,
332 * like its C++ member variables, its C++ class/struct name, its native
333 * memory size and more.
334 */
335 class Object {
336 public:
337 Object();
338 Object(UIDChain uidChain, DataType type);
339
340 UID uid(int index = 0) const {
341 return (index < m_uid.size()) ? m_uid[index] : NO_UID;
342 }
343
344 const UIDChain& uidChain() const { return m_uid; }
345 const DataType& type() const { return m_type; }
346 const RawData& rawData() const { return m_data; }
347
348 Version version() const { return m_version; }
349
350 void setVersion(Version v) {
351 m_version = v;
352 }
353
354 Version minVersion() const { return m_minVersion; }
355
356 void setMinVersion(Version v) {
357 m_minVersion = v;
358 }
359
360 bool isVersionCompatibleTo(const Object& other) const;
361
362 std::vector<Member>& members() { return m_members; }
363 const std::vector<Member>& members() const { return m_members; }
364 Member memberNamed(String name) const;
365 void remove(const Member& member);
366 std::vector<Member> membersOfType(const DataType& type) const;
367 int sequenceIndexOf(const Member& member) const;
368 bool isValid() const;
369 operator bool() const { return isValid(); }
370 //bool operator()() const { return isValid(); }
371 bool operator==(const Object& other) const;
372 bool operator!=(const Object& other) const;
373 bool operator<(const Object& other) const;
374 bool operator>(const Object& other) const;
375
376 private:
377 DataType m_type;
378 UIDChain m_uid;
379 Version m_version;
380 Version m_minVersion;
381 RawData m_data;
382 std::vector<Member> m_members;
383
384 friend String _encodePrimitiveValue(const Object& obj);
385 friend Object _popObjectBlob(const char*& p, const char* end);
386 friend void _popPrimitiveValue(const char*& p, const char* end, Object& obj);
387 friend String _primitiveObjectValueToString(const Object& obj);
388 friend class Archive;
389 };
390
391 /** @brief Destination container for serialization, and source container for deserialization.
392 *
393 * This is the main class for implementing serialization and deserialization
394 * with your C++ application. This framework does not require a a tree
395 * structured layout of your C++ objects being serialized/deserialized, it
396 * uses a concept of a "root" object though. So to start serialization
397 * construct an empty Archive object and then instruct it to serialize your
398 * C++ objects by pointing it to your "root" object:
399 * @code
400 * Archive a;
401 * a.serialize(&myRootObject);
402 * @endcode
403 * Or if you prefer the look of operator based code:
404 * @code
405 * Archive a;
406 * a << myRootObject;
407 * @endcode
408 * The Archive object will then serialize all members of the passed C++
409 * object, and will recursively serialize all other C++ objects which it
410 * contains or points to. So the root object is the starting point for the
411 * overall serialization. After the serialize() method returned, you can
412 * then access the serialized data stream by calling rawData() and send that
413 * data stream over "wire", or store it on disk or whatever you may intend
414 * to do with it.
415 *
416 * Then on receiver side likewise, you create a new Archive object, pass the
417 * received data stream i.e. via constructor to the Archive object and call
418 * deserialize() by pointing it to the root object on receiver side:
419 * @code
420 * Archive a(rawDataStream);
421 * a.deserialize(&myRootObject);
422 * @endcode
423 * Or with operator instead:
424 * @code
425 * Archive a(rawDataStream);
426 * a >> myRootObject;
427 * @endcode
428 * Now this framework automatically handles serialization and
429 * deserialization of fundamental data types automatically for you (like
430 * i.e. char, int, long int, float, double, etc.). However for your own
431 * custom C++ classes and structs you must implement one method which
432 * defines which members of your class should actually be serialized and
433 * deserialized. That method to be added must have the following signature:
434 * @code
435 * void serialize(Serialization::Archive* archive);
436 * @endcode
437 * So let's say you have the following simple data structures:
438 * @code
439 * struct Foo {
440 * int a;
441 * bool b;
442 * double c;
443 * };
444 *
445 * struct Bar {
446 * char one;
447 * float two;
448 * Foo foo1;
449 * Foo* pFoo2;
450 * Foo* pFoo3DontTouchMe; // shall not be serialized/deserialized
451 * };
452 * @endcode
453 * So in order to be able to serialize and deserialize objects of those two
454 * structures you would first add the mentioned method to each struct
455 * definition (i.e. in your header file):
456 * @code
457 * struct Foo {
458 * int a;
459 * bool b;
460 * double c;
461 *
462 * void serialize(Serialization::Archive* archive);
463 * };
464 *
465 * struct Bar {
466 * char one;
467 * float two;
468 * Foo foo1;
469 * Foo* pFoo2;
470 * Foo* pFoo3DontTouchMe; // shall not be serialized/deserialized
471 *
472 * void serialize(Serialization::Archive* archive);
473 * };
474 * @endcode
475 * And then you would implement those two new methods like this (i.e. in
476 * your .cpp file):
477 * @code
478 * #define SRLZ(member) \
479 * archive->serializeMember(*this, member, #member);
480 *
481 * void Foo::serialize(Serialization::Archive* archive) {
482 * SRLZ(a);
483 * SRLZ(b);
484 * SRLZ(c);
485 * }
486 *
487 * void Bar::serialize(Serialization::Archive* archive) {
488 * SRLZ(one);
489 * SRLZ(two);
490 * SRLZ(foo1);
491 * SRLZ(pFoo2);
492 * // leaving out pFoo3DontTouchMe here
493 * }
494 * @endcode
495 * Now when you serialize such a Bar object, this framework will also
496 * automatically serialize the respective Foo object(s) accordingly, also
497 * for the pFoo2 pointer for instance (as long as it is not a NULL pointer
498 * that is).
499 *
500 * Note that there is only one method that you need to implement. So the
501 * respective serialize() method implementation of your classes/structs are
502 * both called for serialization, as well as for deserialization!
503 */
504 class Archive {
505 public:
506 Archive();
507 Archive(const RawData& data);
508 Archive(const uint8_t* data, size_t size);
509 virtual ~Archive();
510
511 template<typename T>
512 void serialize(const T* obj) {
513 m_operation = OPERATION_SERIALIZE;
514 m_allObjects.clear();
515 m_rawData.clear();
516 m_root = UID::from(obj);
517 const_cast<T*>(obj)->serialize(this);
518 encode();
519 m_operation = OPERATION_NONE;
520 }
521
522 template<typename T>
523 void deserialize(T* obj) {
524 Archive a;
525 m_operation = OPERATION_DESERIALIZE;
526 obj->serialize(&a);
527 a.m_root = UID::from(obj);
528 Syncer s(a, *this);
529 m_operation = OPERATION_NONE;
530 }
531
532 template<typename T>
533 void operator<<(const T& obj) {
534 serialize(&obj);
535 }
536
537 template<typename T>
538 void operator>>(T& obj) {
539 deserialize(&obj);
540 }
541
542 const RawData& rawData();
543 virtual String rawDataFormat() const;
544
545 template<typename T_classType, typename T_memberType>
546 void serializeMember(const T_classType& nativeObject, const T_memberType& nativeMember, const char* memberName) {
547 const size_t offset =
548 ((const uint8_t*)(const void*)&nativeMember) -
549 ((const uint8_t*)(const void*)&nativeObject);
550 const UIDChain uids = UIDChainResolver<T_memberType>(nativeMember);
551 const DataType type = DataType::dataTypeOf(nativeMember);
552 const Member member(memberName, uids[0], offset, type);
553 const UID parentUID = UID::from(nativeObject);
554 Object& parent = m_allObjects[parentUID];
555 if (!parent) {
556 const UIDChain uids = UIDChainResolver<T_classType>(nativeObject);
557 const DataType type = DataType::dataTypeOf(nativeObject);
558 parent = Object(uids, type);
559 }
560 parent.members().push_back(member);
561 const Object obj(uids, type);
562 const bool bExistsAlready = m_allObjects.count(uids[0]);
563 const bool isValidObject = obj;
564 const bool bExistingObjectIsInvalid = !m_allObjects[uids[0]];
565 if (!bExistsAlready || (bExistingObjectIsInvalid && isValidObject)) {
566 m_allObjects[uids[0]] = obj;
567 // recurse serialization for all members of this member
568 // (only for struct/class types, noop for primitive types)
569 SerializationRecursion<T_memberType>::serializeObject(this, nativeMember);
570 }
571 }
572
573 virtual void decode(const RawData& data);
574 virtual void decode(const uint8_t* data, size_t size);
575 void clear();
576 bool isModified() const;
577 void remove(const Object& obj);
578 Object& rootObject();
579 Object& objectByUID(const UID& uid);
580 void setAutoValue(Object& object, String value);
581 void setIntValue(Object& object, int64_t value);
582 void setRealValue(Object& object, double value);
583 void setBoolValue(Object& object, bool value);
584 void setEnumValue(Object& object, uint64_t value);
585 String valueAsString(const Object& object);
586
587 protected:
588 // UID resolver for non-pointer types
589 template<typename T>
590 class UIDChainResolver {
591 public:
592 UIDChainResolver(const T& data) {
593 m_uid.push_back(UID::from(data));
594 }
595
596 operator UIDChain() const { return m_uid; }
597 UIDChain operator()() const { return m_uid; }
598 private:
599 UIDChain m_uid;
600 };
601
602 // UID resolver for pointer types (of 1st degree)
603 template<typename T>
604 class UIDChainResolver<T*> {
605 public:
606 UIDChainResolver(const T*& data) {
607 m_uid.push_back((UID) { &data, sizeof(data) });
608 m_uid.push_back((UID) { data, sizeof(*data) });
609 }
610
611 operator UIDChain() const { return m_uid; }
612 UIDChain operator()() const { return m_uid; }
613 private:
614 UIDChain m_uid;
615 };
616
617 // SerializationRecursion for non-pointer class/struct types.
618 template<typename T, bool T_isRecursive>
619 struct SerializationRecursionImpl {
620 static void serializeObject(Archive* archive, const T& obj) {
621 const_cast<T&>(obj).serialize(archive);
622 }
623 };
624
625 // SerializationRecursion for pointers (of 1st degree) to class/structs.
626 template<typename T, bool T_isRecursive>
627 struct SerializationRecursionImpl<T*,T_isRecursive> {
628 static void serializeObject(Archive* archive, const T*& obj) {
629 if (!obj) return;
630 const_cast<T*&>(obj)->serialize(archive);
631 }
632 };
633
634 // NOOP SerializationRecursion for primitive types.
635 template<typename T>
636 struct SerializationRecursionImpl<T,false> {
637 static void serializeObject(Archive* archive, const T& obj) {}
638 };
639
640 // NOOP SerializationRecursion for pointers (of 1st degree) to primitive types.
641 template<typename T>
642 struct SerializationRecursionImpl<T*,false> {
643 static void serializeObject(Archive* archive, const T*& obj) {}
644 };
645
646 // Automatically handles recursion for class/struct types, while ignoring all primitive types.
647 template<typename T>
648 struct SerializationRecursion : SerializationRecursionImpl<T, __is_class(T)> {
649 };
650
651 class ObjectPool : public std::map<UID,Object> {
652 public:
653 // prevent passing obvious invalid UID values from creating a new pair entry
654 Object& operator[](const UID& k) {
655 static Object invalid;
656 if (!k.isValid()) {
657 invalid = Object();
658 return invalid;
659 }
660 return std::map<UID,Object>::operator[](k);
661 }
662 };
663
664 friend String _encode(const ObjectPool& objects);
665
666 private:
667 String _encodeRootBlob();
668 void _popRootBlob(const char*& p, const char* end);
669 void _popObjectsBlob(const char*& p, const char* end);
670
671 protected:
672 class Syncer {
673 public:
674 Syncer(Archive& dst, Archive& src);
675 protected:
676 void syncObject(const Object& dst, const Object& src);
677 void syncPrimitive(const Object& dst, const Object& src);
678 void syncPointer(const Object& dst, const Object& src);
679 void syncMember(const Member& dstMember, const Member& srcMember);
680 static Member dstMemberMatching(const Object& dstObj, const Object& srcObj, const Member& srcMember);
681 private:
682 Archive& m_dst;
683 Archive& m_src;
684 };
685
686 virtual void encode();
687
688 ObjectPool m_allObjects;
689 operation_t m_operation;
690 UID m_root;
691 RawData m_rawData;
692 bool m_isModified;
693 };
694
695 /**
696 * Will be thrown whenever an error occurs during an serialization or
697 * deserialization process.
698 */
699 class Exception {
700 public:
701 String Message;
702
703 Exception(String Message) { Exception::Message = Message; }
704 void PrintMessage();
705 virtual ~Exception() {}
706 };
707
708 } // namespace Serialization
709
710 #endif // LIBGIG_SERIALIZATION_H

  ViewVC Help
Powered by ViewVC