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

Diff of /libgig/trunk/src/Serialization.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 3150 by schoenebeck, Fri May 5 18:42:06 2017 UTC revision 3480 by schoenebeck, Fri Feb 22 11:17:58 2019 UTC
# Line 1  Line 1 
1  /***************************************************************************  /***************************************************************************
2   *                                                                         *   *                                                                         *
3   *   Copyright (C) 2017 Christian Schoenebeck                              *   *   Copyright (C) 2017-2019 Christian Schoenebeck                         *
4   *                      <cuse@users.sourceforge.net>                       *   *                           <cuse@users.sourceforge.net>                  *
5   *                                                                         *   *                                                                         *
6   *   This library is part of libgig.                                       *   *   This library is part of libgig.                                       *
7   *                                                                         *   *                                                                         *
# Line 21  Line 21 
21   *   MA  02111-1307  USA                                                   *   *   MA  02111-1307  USA                                                   *
22   ***************************************************************************/   ***************************************************************************/
23    
24    // enable implementation specific declarations in Serialization.h required to
25    // build this C++ unit, which should be ignored in the public API though
26    #define LIBGIG_SERIALIZATION_INTERNAL 1
27    
28  #include "Serialization.h"  #include "Serialization.h"
29    
30  #include <iostream>  #include <iostream>
31  #include <assert.h>  #include <assert.h>
32  #include <string.h> // for memcpy()  #include <string.h> // for memcpy()
33  #include <stdlib.h> // for atof()  #include <stdlib.h> // for atof()
34    #ifdef _MSC_VER
35    # include <windows.h>
36    # include <dbghelp.h>
37    #else
38    # include <cxxabi.h>
39    #endif
40  #include "helper.h"  #include "helper.h"
41    
42    #define LIBGIG_EPOCH_TIME ((time_t)0)
43    
44  namespace Serialization {  namespace Serialization {
45    
46      // *************** DataType ***************      // *************** DataType ***************
47      // *      // *
48    
49      static UID _createNullUID() {      static UID _createNullUID() {
50          return (UID) { NULL, 0 };          const UID uid = { NULL, 0 };
51            return uid;
52      }      }
53    
54      const UID NO_UID = _createNullUID();      const UID NO_UID = _createNullUID();
55    
56        /** @brief Check whether this is a valid unique identifier.
57         *
58         * Returns @c false if this UID can be considered an invalid unique
59         * identifier. This is for example the case if this UID object was not
60         * explicitly set to some certain meaningful unique identifier value, or if
61         * this UID object was intentionally assigned the constant @c NO_UID value.
62         * Both represent essentially an UID object which is all zero.
63         *
64         * Note that this class also implements the @c bool operator, both return
65         * the same boolean result.
66         */
67      bool UID::isValid() const {      bool UID::isValid() const {
68          return id != NULL && id != (void*)-1 && size;          return id != NULL && id != (void*)-1 && size;
69      }      }
# Line 48  namespace Serialization { Line 71  namespace Serialization {
71      // *************** DataType ***************      // *************** DataType ***************
72      // *      // *
73    
74        /** @brief Default constructor.
75         *
76         * Initializes a DataType object as being an "invalid" DataType object.
77         * Thus calling isValid(), after creating a DataType object with this
78         * constructor, would return @c false.
79         *
80         * To create a valid and meaningful DataType object instead, call the static
81         * function DataType::dataTypeOf() instead.
82         */
83      DataType::DataType() {      DataType::DataType() {
84          m_size = 0;          m_size = 0;
85          m_isPointer = false;          m_isPointer = false;
# Line 60  namespace Serialization { Line 92  namespace Serialization {
92          m_customTypeName = customType;          m_customTypeName = customType;
93      }      }
94    
95        /** @brief Check if this is a valid DataType object.
96         *
97         * Returns @c true if this DataType object is reflecting a valid data type.
98         * The default constructor creates DataType objects initialized to be
99         * "invalid" DataType objects by default. That way one can detect whether
100         * a DataType object was ever assigned to something meaningful.
101         *
102         * Note that this class also implements the @c bool operator, both return
103         * the same boolean result.
104         */
105      bool DataType::isValid() const {      bool DataType::isValid() const {
106          return m_size;          return m_size;
107      }      }
108    
109        /** @brief Whether this is reflecting a C/C++ pointer type.
110         *
111         * Returns @true if the respective native C/C++ object, member or variable
112         * (this DataType instance is reflecting) is a C/C++ pointer type.
113         */
114      bool DataType::isPointer() const {      bool DataType::isPointer() const {
115          return m_isPointer;          return m_isPointer;
116      }      }
117    
118        /** @brief Whether this is reflecting a C/C++ @c struct or @c class type.
119         *
120         * Returns @c true if the respective native C/C++ object, member or variable
121         * (this DataType instance is reflecting) is a C/C++ @c struct or @c class
122         * type.
123         *
124         * Note that in the following example:
125         * @code
126         * struct Foo {
127         *     int  a;
128         *     bool b;
129         * };
130         * Foo foo;
131         * Foo* pFoo;
132         * @endcode
133         * the DataType objects of both @c foo, as well as of the C/C++ pointer
134         * @c pFoo would both return @c true for isClass() here!
135         *
136         * @see isPointer()
137         */
138      bool DataType::isClass() const {      bool DataType::isClass() const {
139          return m_baseTypeName == "class";          return m_baseTypeName == "class";
140      }      }
141    
142        /** @brief Whether this is reflecting a fundamental C/C++ data type.
143         *
144         * Returns @c true if the respective native C/C++ object, member or variable
145         * (this DataType instance is reflecting) is a primitive, fundamental C/C++
146         * data type. Those are fundamental data types which are already predefined
147         * by the C/C++ language, for example: @c char, @c int, @c float, @c double,
148         * @c bool, but also @b any pointer types like @c int*, @c double**, but
149         * including pointers to user defined types like:
150         * @code
151         * struct Foo {
152         *     int  a;
153         *     bool b;
154         * };
155         * Foo* pFoo;
156         * @endcode
157         * So the DataType object of @c pFoo in the latter example would also return
158         * @c true for isPrimitive() here!
159         *
160         * @see isPointer()
161         */
162      bool DataType::isPrimitive() const {      bool DataType::isPrimitive() const {
163          return !isClass();          return !isClass();
164      }      }
165    
166        /** @brief Whether this is an integer C/C++ data type.
167         *
168         * Returns @c true if the respective native C/C++ object, member or variable
169         * (this DataType instance is reflecting) is a (fundamental, primitive)
170         * integer data type. So these are all @c int and @c unsigned @c int types
171         * of any size. It does not include floating point ("real") types though.
172         *
173         * You may use isSigned() to further check whether this data type allows
174         * negative numbers.
175         *
176         * Note that this method also returns @c true on integer pointer types!
177         *
178         * @see isPointer()
179         */
180      bool DataType::isInteger() const {      bool DataType::isInteger() const {
181          return m_baseTypeName.substr(0, 3) == "int" ||          return m_baseTypeName.substr(0, 3) == "int" ||
182                 m_baseTypeName.substr(0, 4) == "uint";                 m_baseTypeName.substr(0, 4) == "uint";
183      }      }
184    
185        /** @brief Whether this is a floating point based C/C++ data type.
186         *
187         * Returns @c true if the respective native C/C++ object, member or variable
188         * (this DataType instance is reflecting) is a (fundamental, primitive)
189         * floating point based data type. So these are currently the C/C++ @c float
190         * and @c double types. It does not include integer types though.
191         *
192         * Note that this method also returns @c true on @c float pointer and
193         * @c double pointer types!
194         *
195         * @see isPointer()
196         */
197      bool DataType::isReal() const {      bool DataType::isReal() const {
198          return m_baseTypeName.substr(0, 4) == "real";          return m_baseTypeName.substr(0, 4) == "real";
199      }      }
200    
201        /** @brief Whether this is a boolean C/C++ data type.
202         *
203         * Returns @c true if the respective native C/C++ object, member or variable
204         * (this DataType instance is reflecting) is a (fundamental, primitive)
205         * boolean data type. So this is the case for the C++ @c bool data type.
206         * It does not include integer or floating point types though.
207         *
208         * Note that this method also returns @c true on @c bool pointer types!
209         *
210         * @see isPointer()
211         */
212      bool DataType::isBool() const {      bool DataType::isBool() const {
213          return m_baseTypeName == "bool";          return m_baseTypeName == "bool";
214      }      }
215    
216        /** @brief Whether this is a C/C++ @c enum data type.
217         *
218         * Returns @c true if the respective native C/C++ object, member or variable
219         * (this DataType instance is reflecting) is a user defined enumeration
220         * data type. So this is the case for all C/C++ @c enum data types.
221         * It does not include integer (or even floating point) types though.
222         *
223         * Note that this method also returns @c true on @c enum pointer types!
224         *
225         * @see isPointer()
226         */
227      bool DataType::isEnum() const {      bool DataType::isEnum() const {
228          return m_baseTypeName == "enum";          return m_baseTypeName == "enum";
229      }      }
230    
231        /** @brief Whether this is a signed integer C/C++ data type.
232         *
233         * Returns @c true if the respective native C/C++ object, member or variable
234         * (this DataType instance is reflecting) is a (fundamental, primitive)
235         * signed integer data type. This is the case for are all @c unsigned
236         * @c int C/C++ types of any size. For all floating point ("real") based
237         * types this method returns @c false though!
238         *
239         * Note that this method also returns @c true on signed integer pointer
240         * types!
241         *
242         * @see isInteger();
243         */
244      bool DataType::isSigned() const {      bool DataType::isSigned() const {
245          return m_baseTypeName.substr(0, 3) == "int" ||          return m_baseTypeName.substr(0, 3) == "int" ||
246                 isReal();                 isReal();
247      }      }
248    
249        /** @brief Comparison for equalness.
250         *
251         * Returns @c true if the two DataType objects being compared can be
252         * considered to be "equal" C/C++ data types. They are considered to be
253         * equal if their underlying C/C++ data types are exactly identical. For
254         * example comparing @c int and @c unsigned int data types are considere to
255         * be @b not equal, since they are differently signed. Furthermore @c short
256         * @c int and @c long @c int would also not be considered to be equal, since
257         * they do have a different memory size. Additionally pointer type
258         * characteristic is compared as well. So a @c double type and @c double*
259         * type are also considered to be not equal data types and hence this method
260         * would return @c false.
261         *
262         * As an exception here, classes and structs with the same class/struct name
263         * but different sizes are also considered to be "equal". This relaxed
264         * requirement is necessary to retain backward compatiblity to older
265         * versions of the same native C++ classes/structs.
266         */
267      bool DataType::operator==(const DataType& other) const {      bool DataType::operator==(const DataType& other) const {
268          return m_baseTypeName   == other.m_baseTypeName &&          return m_baseTypeName   == other.m_baseTypeName &&
269                 m_customTypeName == other.m_customTypeName &&                 m_customTypeName == other.m_customTypeName &&
270                 m_size           == other.m_size &&                 (m_size == other.m_size || (isClass() && other.isClass())) &&
271                 m_isPointer      == other.m_isPointer;                 m_isPointer      == other.m_isPointer;
272      }      }
273    
274        /** @brief Comparison for inequalness.
275         *
276         * Returns the inverse result of what DataType::operator==() would return.
277         * So refer to the latter for more details.
278         */
279      bool DataType::operator!=(const DataType& other) const {      bool DataType::operator!=(const DataType& other) const {
280          return !operator==(other);          return !operator==(other);
281      }      }
282    
283        /** @brief Smaller than comparison.
284         *
285         * Returns @c true if this DataType object can be consider to be "smaller"
286         * than the @a other DataType object being compared with. This operator
287         * is actually quite arbitrarily implemented and may change at any time,
288         * and thus result for the same data types may change in future at any time.
289         *
290         * This operator is basically implemented for allowing this DataType class
291         * to be used with various standard template library (STL) classes, which
292         * require sorting operators to be implemented.
293         */
294      bool DataType::operator<(const DataType& other) const {      bool DataType::operator<(const DataType& other) const {
295          return m_baseTypeName  < other.m_baseTypeName ||          return m_baseTypeName  < other.m_baseTypeName ||
296                (m_baseTypeName == other.m_baseTypeName &&                (m_baseTypeName == other.m_baseTypeName &&
297                 m_customTypeName  < other.m_customTypeName ||                (m_customTypeName  < other.m_customTypeName ||
298                (m_customTypeName == other.m_customTypeName &&                (m_customTypeName == other.m_customTypeName &&
299                 m_size  < other.m_size ||                (m_size  < other.m_size ||
300                (m_size == other.m_size &&                (m_size == other.m_size &&
301                 m_isPointer < other.m_isPointer)));                 m_isPointer < other.m_isPointer)))));
302      }      }
303    
304        /** @brief Greater than comparison.
305         *
306         * Returns @c true if this DataType object can be consider to be "greater"
307         * than the @a other DataType object being compared with. This operator
308         * is actually quite arbitrarily implemented and may change at any time,
309         * and thus result for the same data types may change in future at any time.
310         *
311         * This operator is basically implemented for allowing this DataType class
312         * to be used with various standard template library (STL) classes, which
313         * require sorting operators to be implemented.
314         */
315      bool DataType::operator>(const DataType& other) const {      bool DataType::operator>(const DataType& other) const {
316          return !(operator==(other) || operator<(other));          return !(operator==(other) || operator<(other));
317      }      }
318    
319        /** @brief Human readable long description for this data type.
320         *
321         * Returns a human readable long description for this data type, designed
322         * for the purpose for being displayed to the user. Note that the
323         * implementation for this method and thus the precise textual strings
324         * returned by this method, may change at any time. So you should not rely
325         * on precise strings for certain data types, and you should not use the
326         * return values of this method for comparing data types with each other.
327         *
328         * This class implements various comparison operators, so you should use
329         * them for comparing DataTypes objects instead.
330         *
331         * @see baseTypeName(), customTypeName()
332         */
333      String DataType::asLongDescr() const {      String DataType::asLongDescr() const {
         //TODO: Demangling of C++ raw type names  
334          String s = m_baseTypeName;          String s = m_baseTypeName;
335          if (!m_customTypeName.empty())          if (!m_customTypeName.empty())
336              s += " " + m_customTypeName;              s += " " + customTypeName(true);
337          if (isPointer())          if (isPointer())
338              s += " pointer";              s += " pointer";
339          return s;          return s;
340      }      }
341    
342        /** @brief The base type name of this data type.
343         *
344         * Returns a textual short string identifying the basic type of name of this
345         * data type. For example for a 32 bit signed integer data type this method
346         * would return @c "int32". For all user defined C/C++ @c enum types this
347         * method would return "enum". For all user defined C/C++ @c struct @b and
348         * @c class types this method would return "class" for both. Note that the
349         * precise user defined type name (of i.e. @c enum, @c struct and @c class
350         * types) is not included in the string returned by this method, use
351         * customTypeName() to retrieve that information instead.
352         *
353         * The precise textual strings returned by this method are guaranteed to
354         * retain equal with future versions of this framework. So you can rely on
355         * them for using the return values of this method for comparison tasks in
356         * your application. Note however that this class also implements various
357         * comparison operators.
358         *
359         * Further it is important to know that this method returns the same string
360         * for pointers and non-pointers of the same underlying data type. So in the
361         * following example:
362         * @code
363         * #include <stdint.h>
364         * uint64_t i;
365         * uint64_t* pi;
366         * @endcode
367         * this method would return for both @c i and @c pi the string @c "uint64" !
368         *
369         * @see isPointer(), customTypeName()
370         */
371        String DataType::baseTypeName() const {
372            return m_baseTypeName;
373        }
374    
375        /** @brief The user defined C/C++ data type name of this data type.
376         *
377         * Call this method on user defined C/C++ data types like @c enum, @c struct
378         * and @c class types to retrieve the user defined type name portion of
379         * those data types. Note that this method is only intended for such user
380         * defined data types. For all fundamental, primitive data types (like i.e.
381         * @c int) this method returns an empty string instead.
382         *
383         * This method takes an optional boolean argument @b demangle, which allows
384         * you define whether you are interested in the raw C++ type name or rather
385         * the demangled custom type name. By default this method returns the raw
386         * C++ type name. The raw C++ type name is the one that is actually used
387         * in the compiled binaries and should be preferred for comparions tasks.
388         * The demangled C++ type name is a human readable representation of the
389         * type name instead, which you may use for displaying the user defined type
390         * name portion to the user, however you should not use the demangled
391         * representation for comparison tasks.
392         *
393         * Note that in the following example:
394         * @code
395         * struct Foo {
396         *     int  a;
397         *     bool b;
398         * };
399         * Foo foo;
400         * Foo* pFoo;
401         * @endcode
402         * this method would return the same string for both @c foo and @c pFoo !
403         * In the latter example @c customTypeName(true) would return for both
404         * @c foo and @c pFoo the string @c "Foo" as return value of this method.
405         *
406         * @b Windows: please note that the current implementation of this method
407         * on Windows is @b not thread safe!
408         *
409         * @see isPointer(), baseTypeName()
410         */
411        String DataType::customTypeName(bool demangle) const {
412            if (!demangle) return m_customTypeName;
413    #ifdef _MSC_VER
414            const size_t MAXLENGTH = 1024;
415            char result[MAXLENGTH];
416    
417            //FIXME: calling UnDecorateSymbolName() is not thread safe!
418            //Skip the first char
419            size_t size = UnDecorateSymbolName(m_customTypeName.c_str() +1, result, MAXLENGTH, UNDNAME_32_BIT_DECODE | UNDNAME_NO_ARGUMENTS);
420            if (size)
421            {
422                return result;
423            }
424            return m_customTypeName;
425    #else
426            int status;
427            char* result =
428                abi::__cxa_demangle(m_customTypeName.c_str(), 0, 0, &status);
429            String sResult = result;
430            free(result);
431            return (status == 0) ? sResult : m_customTypeName;
432    #endif
433        }
434    
435      // *************** Member ***************      // *************** Member ***************
436      // *      // *
437    
438        /** @brief Default constructor.
439         *
440         * Initializes a Member object as being an "invalid" Member object.
441         * Thus calling isValid(), after creating a Member object with this
442         * constructor, would return @c false.
443         *
444         * You are currently not supposed to create (meaningful) Member objects on
445         * your own. This framework automatically create such Member objects for
446         * you instead.
447         *
448         * @see Object::members()
449         */
450      Member::Member() {      Member::Member() {
451          m_uid = NO_UID;          m_uid = NO_UID;
452          m_offset = 0;          m_offset = 0;
# Line 148  namespace Serialization { Line 459  namespace Serialization {
459          m_type = type;          m_type = type;
460      }      }
461    
462        /** @brief Unique identifier of this member instance.
463         *
464         * Returns the unique identifier of the original C/C++ member instance of
465         * your C++ class. It is important to know that this unique identifier is
466         * not meant to be unique for Member instances themselves, but it is rather
467         * meant to be unique for the original native C/C++ data these Member
468         * instances are representing. So that means no matter how many individual
469         * Member objects are created, as long as they are representing the same
470         * original native member variable of the same original native
471         * instance of your C++ class, then all those separately created Member
472         * objects return the same unique identifier here.
473         *
474         * @see UID for more details
475         */
476        UID Member::uid() const {
477            return m_uid;
478        }
479    
480        /** @brief Name of the member.
481         *
482         * Returns the name of the native C/C++ member variable as originally typed
483         * in its C++ source code. So in the following example:
484         * @code
485         * struct Foo {
486         *     int  a;
487         *     bool b;
488         *     double someValue;
489         * };
490         * @endcode
491         * this method would usually return @c "a" for the first member of object
492         * instances of your native C/C++ @c struct @c Foo, and this method would
493         * usually return @c "someValue" for its third member.
494         *
495         * Note that when you implement the @c serialize() method of your own C/C++
496         * clases or strucs, you are able to override defining the precise name of
497         * your members. In that case this method would of course return the member
498         * names as explicitly forced by you instead.
499         */
500        String Member::name() const {
501            return m_name;
502        }
503    
504        /** @brief Offset of member in its containing parent data structure.
505         *
506         * Returns the offset of this member (in bytes) within its containing parent
507         * user defined data structure or class. So in the following example:
508         * @code
509         * #include <stdint.h>
510         * struct Foo __attribute__ ((__packed__)) {
511         *     int32_t a;
512         *     bool b;
513         *     double c;
514         * };
515         * @endcode
516         * this method would typically return @c 0 for member @c a, @c 4 for member
517         * @c b and @c 5 for member @c c. As you have noted in the latter example,
518         * the structure @c Foo was declared to have "packed" data members. That
519         * means the compiler is instructed to add no memory spaces between the
520         * individual members. Because by default the compiler might add memory
521         * spaces between individual members to align them on certain memory address
522         * boundaries for increasing runtime performance while accessing the
523         * members. So if you declared the previous example without the "packed"
524         * attribute like:
525         * @code
526         * #include <stdint.h>
527         * struct Foo {
528         *     int32_t a;
529         *     bool b;
530         *     double c;
531         * };
532         * @endcode
533         * then this method would usually return a different offset for members
534         * @c b and @c c instead. For most 64 bit architectures this example would
535         * now still return @c 0 for member @c a, but @c 8 for member @c b and @c 16
536         * for member @c c.
537         */
538        size_t Member::offset() const {
539            return m_offset;
540        }
541    
542        /** @brief C/C++ Data type of this member.
543         *
544         * Returns the precise data type of the original native C/C++ member.
545         */
546        const DataType& Member::type() const {
547            return m_type;
548        }
549    
550        /** @brief Check if this is a valid Member object.
551         *
552         * Returns @c true if this Member object is reflecting a "valid" member
553         * object. The default constructor creates Member objects initialized to be
554         * "invalid" Member objects by default. That way one can detect whether
555         * a Member object was ever assigned to something meaningful.
556         *
557         * Note that this class also implements the @c bool operator, both return
558         * the same boolean result value.
559         */
560      bool Member::isValid() const {      bool Member::isValid() const {
561          return m_uid && !m_name.empty() && m_type;          return m_uid && !m_name.empty() && m_type;
562      }      }
563    
564        /** @brief Comparison for equalness.
565         *
566         * Returns @c true if the two Member objects being compared can be
567         * considered to be "equal" C/C++ members. They are considered to be
568         * equal if their data type, member name, their offset within their parent
569         * containing C/C++ data structure, as well as their original native C/C++
570         * instance were exactly identical.
571         */
572      bool Member::operator==(const Member& other) const {      bool Member::operator==(const Member& other) const {
573          return m_uid    == other.m_uid &&          return m_uid    == other.m_uid &&
574                 m_offset == other.m_offset &&                 m_offset == other.m_offset &&
# Line 159  namespace Serialization { Line 576  namespace Serialization {
576                 m_type   == other.m_type;                 m_type   == other.m_type;
577      }      }
578    
579        /** @brief Comparison for inequalness.
580         *
581         * Returns the inverse result of what Member::operator==() would return.
582         * So refer to the latter for more details.
583         */
584      bool Member::operator!=(const Member& other) const {      bool Member::operator!=(const Member& other) const {
585          return !operator==(other);          return !operator==(other);
586      }      }
587    
588        /** @brief Smaller than comparison.
589         *
590         * Returns @c true if this Member object can be consider to be "smaller"
591         * than the @a other Member object being compared with. This operator
592         * is actually quite arbitrarily implemented and may change at any time,
593         * and thus result for the same member representations may change in
594         * future at any time.
595         *
596         * This operator is basically implemented for allowing this DataType class
597         * to be used with various standard template library (STL) classes, which
598         * require sorting operators to be implemented.
599         */
600      bool Member::operator<(const Member& other) const {      bool Member::operator<(const Member& other) const {
601          return m_uid  < other.m_uid ||          return m_uid  < other.m_uid ||
602                (m_uid == other.m_uid &&                (m_uid == other.m_uid &&
603                 m_offset  < other.m_offset ||                (m_offset  < other.m_offset ||
604                (m_offset == other.m_offset &&                (m_offset == other.m_offset &&
605                 m_name  < other.m_name ||                (m_name  < other.m_name ||
606                (m_name == other.m_name &&                (m_name == other.m_name &&
607                 m_type < other.m_type)));                 m_type < other.m_type)))));
608      }      }
609    
610        /** @brief Greater than comparison.
611         *
612         * Returns @c true if this Member object can be consider to be "greater"
613         * than the @a other Member object being compared with. This operator
614         * is actually quite arbitrarily implemented and may change at any time,
615         * and thus result for the same member representations may change in
616         * future at any time.
617         *
618         * This operator is basically implemented for allowing this DataType class
619         * to be used with various standard template library (STL) classes, which
620         * require sorting operators to be implemented.
621         */
622      bool Member::operator>(const Member& other) const {      bool Member::operator>(const Member& other) const {
623          return !(operator==(other) || operator<(other));          return !(operator==(other) || operator<(other));
624      }      }
# Line 180  namespace Serialization { Line 626  namespace Serialization {
626      // *************** Object ***************      // *************** Object ***************
627      // *      // *
628    
629        /** @brief Default constructor (for an "invalid" Object).
630         *
631         * Initializes an Object instance as being an "invalid" Object.
632         * Thus calling isValid(), after creating an Object instance with this
633         * constructor, would return @c false.
634         *
635         * Usually you are not supposed to create (meaningful) Object instances on
636         * your own. They are typically constructed by the Archive class for you.
637         *
638         * @see Archive::rootObject(), Archive::objectByUID()
639         */
640      Object::Object() {      Object::Object() {
641          m_version = 0;          m_version = 0;
642          m_minVersion = 0;          m_minVersion = 0;
643      }      }
644    
645        /** @brief Constructor for a "meaningful" Object.
646         *
647         * Initializes a "meaningful" Object instance as being. Thus calling
648         * isValid(), after creating an Object instance with this constructor,
649         * should return @c true, provided that the arguments passed to this
650         * constructor construe a valid object representation.
651         *
652         * Usually you are not supposed to create (meaningful) Object instances on
653         * your own. They are typically constructed by the Archive class for you.
654         *
655         * @see Archive::rootObject(), Archive::objectByUID()
656         *
657         * @param uidChain - unique identifier chain of the object to be constructed
658         * @param type - C/C++ data type of the actual native object this abstract
659         *               Object instance should reflect after calling this
660         *               constructor
661         */
662      Object::Object(UIDChain uidChain, DataType type) {      Object::Object(UIDChain uidChain, DataType type) {
663          m_type = type;          m_type = type;
664          m_uid  = uidChain;          m_uid  = uidChain;
# Line 193  namespace Serialization { Line 667  namespace Serialization {
667          //m_data.resize(type.size());          //m_data.resize(type.size());
668      }      }
669    
670        /** @brief Check if this is a valid Object instance.
671         *
672         * Returns @c true if this Object instance is reflecting a "valid" Object.
673         * The default constructor creates Object instances initialized to be
674         * "invalid" Objects by default. That way one can detect whether an Object
675         * instance was ever assigned to something meaningful.
676         *
677         * Note that this class also implements the @c bool operator, both return
678         * the same boolean result value.
679         */
680      bool Object::isValid() const {      bool Object::isValid() const {
681          return m_type && !m_uid.empty();          return m_type && !m_uid.empty();
682      }      }
683    
684        /** @brief Unique identifier of this Object.
685         *
686         * Returns the unique identifier for the original native C/C++ data this
687         * abstract Object instance is reflecting. If this Object is representing
688         * a C/C++ pointer (of first degree) then @c uid() (or @c uid(0) ) returns
689         * the unique identifier of the pointer itself, whereas @c uid(1) returns
690         * the unique identifier of the original C/C++ data that pointer was
691         * actually pointing to.
692         *
693         * @see UIDChain for more details about this overall topic.
694         */
695        UID Object::uid(int index) const {
696            return (index < m_uid.size()) ? m_uid[index] : NO_UID;
697        }
698    
699        /** @brief Unique identifier chain of this Object.
700         *
701         * Returns the entire unique identifier chain of this Object.
702         *
703         * @see uid() and UIDChain for more details about this overall topic.
704         */
705        const UIDChain& Object::uidChain() const {
706            return m_uid;
707        }
708    
709        /** @brief C/C++ data type this Object is reflecting.
710         *
711         * Returns the precise original C/C++ data type of the original native
712         * C/C++ object or data this Object instance is reflecting.
713         */
714        const DataType& Object::type() const {
715            return m_type;
716        }
717    
718        /** @brief Raw data of the original native C/C++ data.
719         *
720         * Returns the raw data value of the original C/C++ data this Object is
721         * reflecting. So the precise raw data value, layout and size is dependent
722         * to the precise C/C++ data type of the original native C/C++ data. However
723         * potentially required endian correction is already automatically applied
724         * for you. That means you can safely, directly C-cast the raw data returned
725         * by this method to the respective native C/C++ data type in order to
726         * access and use the value for some purpose, at least if the respective
727         * data is of any fundamental, primitive C/C++ data type, or also to a
728         * certain extent if the type is user defined @c enum type.
729         *
730         * However directly C-casting this raw data for user defined @c struct or
731         * @c class types is not possible. For those user defined data structures
732         * this method always returns empty raw data instead.
733         *
734         * Note however that there are more convenient methods in the Archive class
735         * to get the right value for the individual data types instead.
736         *
737         * @see Archive::valueAsInt(), Archive::valueAsReal(), Archive::valueAsBool(),
738         *      Archive::valueAsString()
739         */
740        const RawData& Object::rawData() const {
741            return m_data;
742        }
743    
744        /** @brief Version of original user defined C/C++ @c struct or @c class.
745         *
746         * In case this Object is reflecting a native C/C++ @c struct or @c class
747         * type, then this method returns the version of that native C/C++ @c struct
748         * or @c class layout or implementation. For primitive, fundamental C/C++
749         * data types the return value of this method has no meaning.
750         *
751         * @see Archive::setVersion() for more details about this overall topic.
752         */
753        Version Object::version() const {
754            return m_version;
755        }
756    
757        /** @brief Minimum version of original user defined C/C++ @c struct or @c class.
758         *
759         * In case this Object is reflecting a native C/C++ @c struct or @c class
760         * type, then this method returns the "minimum" version of that native C/C++
761         * @c struct or @c class layout or implementation which it may be compatible
762         * with. For primitive, fundamental C/C++ data types the return value of
763         * this method has no meaning.
764         *
765         * @see Archive::setVersion() and Archive::setMinVersion() for more details
766         *      about this overall topic.
767         */
768        Version Object::minVersion() const {
769            return m_minVersion;
770        }
771    
772        /** @brief All members of the original native C/C++ @c struct or @c class instance.
773         *
774         * In case this Object is reflecting a native C/C++ @c struct or @c class
775         * type, then this method returns all member variables of that original
776         * native C/C++ @c struct or @c class instance. For primitive, fundamental
777         * C/C++ data types this method returns an empty vector instead.
778         *
779         * Example:
780         * @code
781         * struct Foo {
782         *     int  a;
783         *     bool b;
784         *     double someValue;
785         * };
786         * @endcode
787         * Considering above's C++ code, a serialized Object representation of such
788         * a native @c Foo class would have 3 members @c a, @c b and @c someValue.
789         *
790         * Note that the respective serialize() method implementation of that
791         * fictional C++ @c struct @c Foo actually defines which members are going
792         * to be serialized and deserialized for instances of class @c Foo. So in
793         * practice the members returned by method members() here might return a
794         * different set of members as actually defined in the original C/C++ struct
795         * header declaration.
796         *
797         * The precise sequence of the members returned by this method here depends
798         * on the actual serialize() implementation of the user defined C/C++
799         * @c struct or @c class.
800         *
801         * @see Object::sequenceIndexOf() for more details about the precise order
802         *      of members returned by this method in the same way.
803         */
804        std::vector<Member>& Object::members() {
805            return m_members;
806        }
807    
808        /** @brief All members of the original native C/C++ @c struct or @c class instance (read only).
809         *
810         * Returns the same result as overridden members() method above, it just
811         * returns a read-only result instead. See above's method description for
812         * details for the return value of this method instead.
813         */
814        const std::vector<Member>& Object::members() const {
815            return m_members;
816        }
817    
818        /** @brief Comparison for equalness.
819         *
820         * Returns @c true if the two Object instances being compared can be
821         * considered to be "equal" native C/C++ object instances. They are
822         * considered to be equal if they are representing the same original
823         * C/C++ data instance, which is essentially the case if the original
824         * reflecting native C/C++ data are sharing the same memory address and
825         * memory size (thus the exact same memory space) and originally had the
826         * exact same native C/C++ types.
827         */
828      bool Object::operator==(const Object& other) const {      bool Object::operator==(const Object& other) const {
829          // ignoring all other member variables here          // ignoring all other member variables here
830          // (since UID stands for "unique" ;-) )          // (since UID stands for "unique" ;-) )
# Line 204  namespace Serialization { Line 832  namespace Serialization {
832                 m_type == other.m_type;                 m_type == other.m_type;
833      }      }
834    
835        /** @brief Comparison for inequalness.
836         *
837         * Returns the inverse result of what Object::operator==() would return.
838         * So refer to the latter for more details.
839         */
840      bool Object::operator!=(const Object& other) const {      bool Object::operator!=(const Object& other) const {
841          return !operator==(other);          return !operator==(other);
842      }      }
843    
844        /** @brief Smaller than comparison.
845         *
846         * Returns @c true if this Object instance can be consider to be "smaller"
847         * than the @a other Object instance being compared with. This operator
848         * is actually quite arbitrarily implemented and may change at any time,
849         * and thus result for the same Object representations may change in future
850         * at any time.
851         *
852         * This operator is basically implemented for allowing this DataType class
853         * to be used with various standard template library (STL) classes, which
854         * require sorting operators to be implemented.
855         */
856      bool Object::operator<(const Object& other) const {      bool Object::operator<(const Object& other) const {
857          // ignoring all other member variables here          // ignoring all other member variables here
858          // (since UID stands for "unique" ;-) )          // (since UID stands for "unique" ;-) )
# Line 216  namespace Serialization { Line 861  namespace Serialization {
861                 m_type < other.m_type);                 m_type < other.m_type);
862      }      }
863    
864        /** @brief Greater than comparison.
865         *
866         * Returns @c true if this Object instance can be consider to be "greater"
867         * than the @a other Object instance being compared with. This operator
868         * is actually quite arbitrarily implemented and may change at any time,
869         * and thus result for the same Object representations may change in future
870         * at any time.
871         *
872         * This operator is basically implemented for allowing this DataType class
873         * to be used with various standard template library (STL) classes, which
874         * require sorting operators to be implemented.
875         */
876      bool Object::operator>(const Object& other) const {      bool Object::operator>(const Object& other) const {
877          return !(operator==(other) || operator<(other));          return !(operator==(other) || operator<(other));
878      }      }
879    
880        /** @brief Check version compatibility between Object instances.
881         *
882         * Use this method to check whether the two original C/C++ instances those
883         * two Objects are reflecting, were using a C/C++ data type which are version
884         * compatible with each other. By default all C/C++ Objects are considered
885         * to be version compatible. They might only be version incompatible if you
886         * enforced a certain backward compatibility constraint with your
887         * serialize() method implementation of your custom C/C++ @c struct or
888         * @c class types.
889         *
890         * You must only call this method on two Object instances which are
891         * representing the same data type, for example if both Objects reflect
892         * instances of the same user defined C++ class. Calling this method on
893         * completely different data types does not cause an error or exception, but
894         * its result would simply be useless for any purpose.
895         *
896         * @see Archive::setVersion() for more details about this overall topic.
897         */
898      bool Object::isVersionCompatibleTo(const Object& other) const {      bool Object::isVersionCompatibleTo(const Object& other) const {
899          if (this->version() == other.version())          if (this->version() == other.version())
900              return true;              return true;
# Line 229  namespace Serialization { Line 904  namespace Serialization {
904              return other.minVersion() <= this->version();              return other.minVersion() <= this->version();
905      }      }
906    
907        void Object::setVersion(Version v) {
908            m_version = v;
909        }
910    
911        void Object::setMinVersion(Version v) {
912            m_minVersion = v;
913        }
914    
915        /** @brief Get the member of this Object with given name.
916         *
917         * In case this Object is reflecting a native C/C++ @c struct or @c class
918         * type, then this method returns the abstract reflection of the requested
919         * member variable of the original native C/C++ @c struct or @c class
920         * instance. For primitive, fundamental C/C++ data types this method always
921         * returns an "invalid" Member instance instead.
922         *
923         * Example:
924         * @code
925         * struct Foo {
926         *     int  a;
927         *     bool b;
928         *     double someValue;
929         * };
930         * @endcode
931         * Consider that you serialized the native C/C++ @c struct as shown in this
932         * example, and assuming that you implemented the respective serialize()
933         * method of this C++ @c struct to serialize all its members, then you might
934         * call memberNamed("someValue") to get the details of the third member in
935         * this example for instance. In case the passed @a name is an unknown
936         * member name, then this method will return an "invalid" Member object
937         * instead.
938         *
939         * @param name - original name of the sought serialized member variable of
940         *               this Object reflection
941         * @returns abstract reflection of the sought member variable
942         * @see Member::isValid(), Object::members()
943         */
944      Member Object::memberNamed(String name) const {      Member Object::memberNamed(String name) const {
945          for (int i = 0; i < m_members.size(); ++i)          for (int i = 0; i < m_members.size(); ++i)
946              if (m_members[i].name() == name)              if (m_members[i].name() == name)
# Line 236  namespace Serialization { Line 948  namespace Serialization {
948          return Member();          return Member();
949      }      }
950    
951        /** @brief Get the member of this Object with given unique identifier.
952         *
953         * This method behaves similar like method memberNamed() described above,
954         * but instead of searching for a member variable by name, it searches for
955         * a member with an abstract unique identifier instead. For primitive,
956         * fundamental C/C++ data types, for invalid or unknown unique identifiers,
957         * and for members which are actually not member instances of the original
958         * C/C++ @c struct or @c class instance this Object is reflecting, this
959         * method returns an "invalid" Member instance instead.
960         *
961         * @param uid - unique identifier of the member variable being sought
962         * @returns abstract reflection of the sought member variable
963         * @see Member::isValid(), Object::members(), Object::memberNamed()
964         */
965        Member Object::memberByUID(const UID& uid) const {
966            if (!uid) return Member();
967            for (int i = 0; i < m_members.size(); ++i)
968                if (m_members[i].uid() == uid)
969                    return m_members[i];
970            return Member();
971        }
972    
973      void Object::remove(const Member& member) {      void Object::remove(const Member& member) {
974          for (int i = 0; i < m_members.size(); ++i) {          for (int i = 0; i < m_members.size(); ++i) {
975              if (m_members[i] == member) {              if (m_members[i] == member) {
# Line 245  namespace Serialization { Line 979  namespace Serialization {
979          }          }
980      }      }
981    
982        /** @brief Get all members of this Object with given data type.
983         *
984         * In case this Object is reflecting a native C/C++ @c struct or @c class
985         * type, then this method returns all member variables of that original
986         * native C/C++ @c struct or @c class instance which are matching the given
987         * requested data @a type. If this Object is reflecting a primitive,
988         * fundamental data type, or if there are no members of this Object with the
989         * requested precise C/C++ data type, then this method returns an empty
990         * vector instead.
991         *
992         * @param type - the precise C/C++ data type of the sought member variables
993         *               of this Object
994         * @returns vector with abstract reflections of the sought member variables
995         * @see Object::members(), Object::memberNamed()
996         */
997      std::vector<Member> Object::membersOfType(const DataType& type) const {      std::vector<Member> Object::membersOfType(const DataType& type) const {
998          std::vector<Member> v;          std::vector<Member> v;
999          for (int i = 0; i < m_members.size(); ++i) {          for (int i = 0; i < m_members.size(); ++i) {
# Line 255  namespace Serialization { Line 1004  namespace Serialization {
1004          return v;          return v;
1005      }      }
1006    
1007        /** @brief Serialization/deserialization sequence number of the requested member.
1008         *
1009         * Returns the precise serialization/deserialization sequence number of the
1010         * requested @a member variable.
1011         *
1012         * Example:
1013         * @code
1014         * struct Foo {
1015         *     int  a;
1016         *     bool b;
1017         *     double c;
1018         *
1019         *     void serialize(Serialization::Archive* archive);
1020         * };
1021         * @endcode
1022         * Assuming the declaration of the user defined native C/C++ @c struct
1023         * @c Foo above, and assuming the following implementation of serialize():
1024         * @code
1025         * #define SRLZ(member) \
1026         *   archive->serializeMember(*this, member, #member);
1027         *
1028         * void Foo::serialize(Serialization::Archive* archive) {
1029         *     SRLZ(c);
1030         *     SRLZ(a);
1031         *     SRLZ(b);
1032         * }
1033         * @endcode
1034         * then @c sequenceIndexOf(obj.memberNamed("a")) returns 1,
1035         * @c sequenceIndexOf(obj.memberNamed("b")) returns 2, and
1036         * @c sequenceIndexOf(obj.memberNamed("c")) returns 0.
1037         */
1038      int Object::sequenceIndexOf(const Member& member) const {      int Object::sequenceIndexOf(const Member& member) const {
1039          for (int i = 0; i < m_members.size(); ++i)          for (int i = 0; i < m_members.size(); ++i)
1040              if (m_members[i] == member)              if (m_members[i] == member)
# Line 265  namespace Serialization { Line 1045  namespace Serialization {
1045      // *************** Archive ***************      // *************** Archive ***************
1046      // *      // *
1047    
1048        /** @brief Create an "empty" archive.
1049         *
1050         * This default constructor creates an "empty" archive which you then
1051         * subsequently for example might fill with serialized data like:
1052         * @code
1053         * Archive a;
1054         * a.serialize(&myRootObject);
1055         * @endcode
1056         * Or:
1057         * @code
1058         * Archive a;
1059         * a << myRootObject;
1060         * @endcode
1061         * Or you might also subsequently assign an already existing non-empty
1062         * to this empty archive, which effectively clones the other
1063         * archive (deep copy) or call decode() later on to assign a previously
1064         * serialized raw data stream.
1065         */
1066      Archive::Archive() {      Archive::Archive() {
1067          m_operation = OPERATION_NONE;          m_operation = OPERATION_NONE;
1068          m_root = NO_UID;          m_root = NO_UID;
1069          m_isModified = false;          m_isModified = false;
1070            m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1071      }      }
1072    
1073        /** @brief Create and fill the archive with the given serialized raw data.
1074         *
1075         * This constructor decodes the given raw @a data and constructs a
1076         * (non-empty) Archive object according to that given serialized data
1077         * stream.
1078         *
1079         * After this constructor returned, you may then traverse the individual
1080         * objects by starting with accessing the rootObject() for example. Finally
1081         * you might call deserialize() to restore your native C++ objects with the
1082         * content of this archive.
1083         *
1084         * @param data - the previously serialized raw data stream to be decoded
1085         * @throws Exception if the provided raw @a data uses an invalid, unknown,
1086         *         incompatible or corrupt data stream or format.
1087         */
1088      Archive::Archive(const RawData& data) {      Archive::Archive(const RawData& data) {
1089          m_operation = OPERATION_NONE;          m_operation = OPERATION_NONE;
1090          m_root = NO_UID;          m_root = NO_UID;
1091          m_isModified = false;          m_isModified = false;
1092            m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1093          decode(m_rawData);          decode(m_rawData);
1094      }      }
1095    
1096        /** @brief Create and fill the archive with the given serialized raw C-buffer data.
1097         *
1098         * This constructor essentially works like the constructor above, but just
1099         * uses another data type for the serialized raw data stream being passed to
1100         * this class.
1101         *
1102         * This constructor decodes the given raw @a data and constructs a
1103         * (non-empty) Archive object according to that given serialized data
1104         * stream.
1105         *
1106         * After this constructor returned, you may then traverse the individual
1107         * objects by starting with accessing the rootObject() for example. Finally
1108         * you might call deserialize() to restore your native C++ objects with the
1109         * content of this archive.
1110         *
1111         * @param data - the previously serialized raw data stream to be decoded
1112         * @param size - size of @a data in bytes
1113         * @throws Exception if the provided raw @a data uses an invalid, unknown,
1114         *         incompatible or corrupt data stream or format.
1115         */
1116      Archive::Archive(const uint8_t* data, size_t size) {      Archive::Archive(const uint8_t* data, size_t size) {
1117          m_operation = OPERATION_NONE;          m_operation = OPERATION_NONE;
1118          m_root = NO_UID;          m_root = NO_UID;
1119          m_isModified = false;          m_isModified = false;
1120            m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1121          decode(data, size);          decode(data, size);
1122      }      }
1123    
1124      Archive::~Archive() {      Archive::~Archive() {
1125      }      }
1126    
1127        /** @brief Root C++ object of this archive.
1128         *
1129         * In case this is a non-empty Archive, then this method returns the so
1130         * called "root" C++ object. If this is an empty archive, then this method
1131         * returns an "invalid" Object instance instead.
1132         *
1133         * @see Archive::serialize() for more details about the "root" object concept.
1134         * @see Object for more details about the overall object reflection concept.
1135         * @returns reflection of the original native C++ root object
1136         */
1137      Object& Archive::rootObject() {      Object& Archive::rootObject() {
1138          return m_allObjects[m_root];          return m_allObjects[m_root];
1139      }      }
# Line 303  namespace Serialization { Line 1149  namespace Serialization {
1149          return _encodeBlob(s);          return _encodeBlob(s);
1150      }      }
1151    
1152        static String _encode(const time_t& time) {
1153            return _encodeBlob(ToString(time));
1154        }
1155    
1156      static String _encode(const DataType& type) {      static String _encode(const DataType& type) {
1157          String s;          String s;
1158          s += _encodeBlob(type.baseTypeName());          s += _encodeBlob(type.baseTypeName());
# Line 384  namespace Serialization { Line 1234  namespace Serialization {
1234          return s;          return s;
1235      }      }
1236    
1237        template<typename T>
1238        static T _primitiveObjectValueToNumber(const Object& obj) {
1239            T value = 0;
1240            const DataType& type = obj.type();
1241            const ID& id = obj.uid().id;
1242            void* ptr = obj.m_data.empty() ? (void*)id : (void*)&obj.m_data[0];
1243            if (!obj.m_data.empty())
1244                assert(type.size() == obj.m_data.size());
1245            if (type.isPrimitive() && !type.isPointer()) {
1246                if (type.isInteger() || type.isEnum()) {
1247                    if (type.isSigned()) {
1248                        if (type.size() == 1)
1249                            value = (T)*(int8_t*)ptr;
1250                        else if (type.size() == 2)
1251                            value = (T)*(int16_t*)ptr;
1252                        else if (type.size() == 4)
1253                            value = (T)*(int32_t*)ptr;
1254                        else if (type.size() == 8)
1255                            value = (T)*(int64_t*)ptr;
1256                        else
1257                            assert(false /* unknown signed int type size */);
1258                    } else {
1259                        if (type.size() == 1)
1260                            value = (T)*(uint8_t*)ptr;
1261                        else if (type.size() == 2)
1262                            value = (T)*(uint16_t*)ptr;
1263                        else if (type.size() == 4)
1264                            value = (T)*(uint32_t*)ptr;
1265                        else if (type.size() == 8)
1266                            value = (T)*(uint64_t*)ptr;
1267                        else
1268                            assert(false /* unknown unsigned int type size */);
1269                    }
1270                } else if (type.isReal()) {
1271                    if (type.size() == sizeof(float))
1272                        value = (T)*(float*)ptr;
1273                    else if (type.size() == sizeof(double))
1274                        value = (T)*(double*)ptr;
1275                    else
1276                        assert(false /* unknown floating point type */);
1277                } else if (type.isBool()) {
1278                    value = (T)*(bool*)ptr;
1279                } else {
1280                    assert(false /* unknown primitive type */);
1281                }
1282            }
1283            return value;
1284        }
1285    
1286      static String _encodePrimitiveValue(const Object& obj) {      static String _encodePrimitiveValue(const Object& obj) {
1287          return _encodeBlob( _primitiveObjectValueToString(obj) );          return _encodeBlob( _primitiveObjectValueToString(obj) );
1288      }      }
# Line 418  namespace Serialization { Line 1317  namespace Serialization {
1317          s += _encodeBlob(ToString(ENCODING_FORMAT_MINOR_VERSION));          s += _encodeBlob(ToString(ENCODING_FORMAT_MINOR_VERSION));
1318          s += _encode(m_root);          s += _encode(m_root);
1319          s += _encode(m_allObjects);          s += _encode(m_allObjects);
1320            s += _encodeBlob(m_name);
1321            s += _encodeBlob(m_comment);
1322            s += _encode(m_timeCreated);
1323            s += _encode(m_timeModified);
1324          return _encodeBlob(s);          return _encodeBlob(s);
1325      }      }
1326    
1327      void Archive::encode() {      void Archive::encode() {
1328          m_rawData.clear();          m_rawData.clear();
1329          String s = MAGIC_START;          String s = MAGIC_START;
1330            m_timeModified = time(NULL);
1331            if (m_timeCreated == LIBGIG_EPOCH_TIME)
1332                m_timeCreated = m_timeModified;
1333          s += _encodeRootBlob();          s += _encodeRootBlob();
1334          m_rawData.resize(s.length() + 1);          m_rawData.resize(s.length() + 1);
1335          memcpy(&m_rawData[0], &s[0], s.length() + 1);          memcpy(&m_rawData[0], &s[0], s.length() + 1);
# Line 436  namespace Serialization { Line 1342  namespace Serialization {
1342      };      };
1343    
1344      static _Blob _decodeBlob(const char* p, const char* end, bool bThrow = true) {      static _Blob _decodeBlob(const char* p, const char* end, bool bThrow = true) {
1345          if (!bThrow && p >= end)          if (!bThrow && p >= end) {
1346              return (_Blob) { p, end };              const _Blob blob =  { p, end };
1347                return blob;
1348            }
1349          size_t sz = 0;          size_t sz = 0;
1350          for (; true; ++p) {          for (; true; ++p) {
1351              if (p >= end)              if (p >= end)
# Line 452  namespace Serialization { Line 1360  namespace Serialization {
1360          ++p;          ++p;
1361          if (p + sz > end)          if (p + sz > end)
1362              throw Exception("Decode Error: Premature end of blob");              throw Exception("Decode Error: Premature end of blob");
1363          return (_Blob) { p, p + sz };          const _Blob blob = { p, p + sz };
1364            return blob;
1365      }      }
1366    
1367      template<typename T_int>      template<typename T_int>
# Line 527  namespace Serialization { Line 1436  namespace Serialization {
1436          return s;          return s;
1437      }      }
1438    
1439      DataType _popDataTypeBlob(const char*& p, const char* end) {      static time_t _popTimeBlob(const char*& p, const char* end) {
1440            const uint64_t i = _popIntBlob<uint64_t>(p, end);
1441            return (time_t) i;
1442        }
1443    
1444        static DataType _popDataTypeBlob(const char*& p, const char* end) {
1445          _Blob blob = _decodeBlob(p, end);          _Blob blob = _decodeBlob(p, end);
1446          p   = blob.p;          p   = blob.p;
1447          end = blob.end;          end = blob.end;
# Line 551  namespace Serialization { Line 1465  namespace Serialization {
1465          const ID id = (ID) _popIntBlob<size_t>(p, end);          const ID id = (ID) _popIntBlob<size_t>(p, end);
1466          const size_t size = _popIntBlob<size_t>(p, end);          const size_t size = _popIntBlob<size_t>(p, end);
1467    
1468          return (UID) { id, size };          const UID uid = { id, size };
1469            return uid;
1470      }      }
1471    
1472      static UIDChain _popUIDChainBlob(const char*& p, const char* end) {      static UIDChain _popUIDChainBlob(const char*& p, const char* end) {
# Line 703  namespace Serialization { Line 1618  namespace Serialization {
1618          _popObjectsBlob(p, end);          _popObjectsBlob(p, end);
1619          if (!m_allObjects[m_root])          if (!m_allObjects[m_root])
1620              throw Exception("Decode Error: Missing declared root object");              throw Exception("Decode Error: Missing declared root object");
     }  
1621    
1622            m_name = _popStringBlob(p, end);
1623            m_comment = _popStringBlob(p, end);
1624            m_timeCreated = _popTimeBlob(p, end);
1625            m_timeModified = _popTimeBlob(p, end);
1626        }
1627    
1628        /** @brief Fill this archive with the given serialized raw data.
1629         *
1630         * Calling this method will decode the given raw @a data and constructs a
1631         * (non-empty) Archive object according to that given serialized @a data
1632         * stream.
1633         *
1634         * After this method returned, you may then traverse the individual
1635         * objects by starting with accessing the rootObject() for example. Finally
1636         * you might call deserialize() to restore your native C++ objects with the
1637         * content of this archive.
1638         *
1639         * @param data - the previously serialized raw data stream to be decoded
1640         * @throws Exception if the provided raw @a data uses an invalid, unknown,
1641         *         incompatible or corrupt data stream or format.
1642         */
1643      void Archive::decode(const RawData& data) {      void Archive::decode(const RawData& data) {
1644          m_rawData = data;          m_rawData = data;
1645          m_allObjects.clear();          m_allObjects.clear();
1646          m_isModified = false;          m_isModified = false;
1647            m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1648          const char* p   = (const char*) &data[0];          const char* p   = (const char*) &data[0];
1649          const char* end = p + data.size();          const char* end = p + data.size();
1650          if (memcmp(p, MAGIC_START, std::min(strlen(MAGIC_START), data.size())))          if (memcmp(p, MAGIC_START, std::min(strlen(MAGIC_START), data.size())))
# Line 717  namespace Serialization { Line 1653  namespace Serialization {
1653          _popRootBlob(p, end);          _popRootBlob(p, end);
1654      }      }
1655    
1656        /** @brief Fill this archive with the given serialized raw C-buffer data.
1657         *
1658         * This method essentially works like the decode() method above, but just
1659         * uses another data type for the serialized raw data stream being passed to
1660         * this method.
1661         *
1662         * Calling this method will decode the given raw @a data and constructs a
1663         * (non-empty) Archive object according to that given serialized @a data
1664         * stream.
1665         *
1666         * After this method returned, you may then traverse the individual
1667         * objects by starting with accessing the rootObject() for example. Finally
1668         * you might call deserialize() to restore your native C++ objects with the
1669         * content of this archive.
1670         *
1671         * @param data - the previously serialized raw data stream to be decoded
1672         * @param size - size of @a data in bytes
1673         * @throws Exception if the provided raw @a data uses an invalid, unknown,
1674         *         incompatible or corrupt data stream or format.
1675         */
1676      void Archive::decode(const uint8_t* data, size_t size) {      void Archive::decode(const uint8_t* data, size_t size) {
1677          RawData rawData;          RawData rawData;
1678          rawData.resize(size);          rawData.resize(size);
# Line 724  namespace Serialization { Line 1680  namespace Serialization {
1680          decode(rawData);          decode(rawData);
1681      }      }
1682    
1683        /** @brief Raw data stream of this archive content.
1684         *
1685         * Call this method to get a raw data stream for the current content of this
1686         * archive, which you may use to i.e. store on disk or send vie network to
1687         * another machine for deserializing there. This method only returns a
1688         * meaningful content if this is a non-empty archive, that is if you either
1689         * serialized with this Archive object or decoded a raw data stream to this
1690         * Archive object before. If this is an empty archive instead, then this
1691         * method simply returns an empty raw data stream (of size 0) instead.
1692         *
1693         * Note that whenever you call this method, the "modified" state of this
1694         * archive will be reset to @c false.
1695         *
1696         * @see isModified()
1697         */
1698      const RawData& Archive::rawData() {      const RawData& Archive::rawData() {
1699          if (m_isModified) encode();          if (m_isModified) encode();
1700          return m_rawData;          return m_rawData;
1701      }      }
1702    
1703        /** @brief Name of the encoding format used by this Archive class.
1704         *
1705         * This method returns the name of the encoding format used to encode
1706         * serialized raw data streams.
1707         */
1708      String Archive::rawDataFormat() const {      String Archive::rawDataFormat() const {
1709          return MAGIC_START;          return MAGIC_START;
1710      }      }
1711    
1712        /** @brief Whether this archive was modified.
1713         *
1714         * This method returns the current "modified" state of this archive. When
1715         * either decoding a previously serialized raw data stream or after
1716         * serializing native C++ objects to this archive the modified state will
1717         * initially be set to @c false. However whenever you are modifying the
1718         * abstract data model of this archive afterwards, for example by removing
1719         * objects from this archive by calling remove() or removeMember(), or by
1720         * altering object values for example by calling setIntValue(), then the
1721         * "modified" state of this archive will automatically be set to @c true.
1722         *
1723         * You can reset the "modified" state explicitly at any time, by calling
1724         * rawData().
1725         */
1726      bool Archive::isModified() const {      bool Archive::isModified() const {
1727          return m_isModified;          return m_isModified;
1728      }      }
1729    
1730        /** @brief Clear content of this archive.
1731         *
1732         * Drops the entire content of this archive and thus resets this archive
1733         * back to become an empty archive.
1734         */
1735      void Archive::clear() {      void Archive::clear() {
1736          m_allObjects.clear();          m_allObjects.clear();
1737          m_operation = OPERATION_NONE;          m_operation = OPERATION_NONE;
1738          m_root = NO_UID;          m_root = NO_UID;
1739          m_rawData.clear();          m_rawData.clear();
1740          m_isModified = false;          m_isModified = false;
1741            m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1742        }
1743    
1744        /** @brief Optional name of this archive.
1745         *
1746         * Returns the optional name of this archive that you might have assigned
1747         * to this archive before by calling setName(). If you haven't assigned any
1748         * name to this archive before, then this method simply returns an empty
1749         * string instead.
1750         */
1751        String Archive::name() const {
1752            return m_name;
1753        }
1754    
1755        /** @brief Assign a name to this archive.
1756         *
1757         * You may optionally assign an arbitrary name to this archive. The name
1758         * will be stored along with the archive, that is it will encoded with the
1759         * resulting raw data stream, and accordingly it will be decoded from the
1760         * raw data stream later on.
1761         *
1762         * @param name - arbitrary new name for this archive
1763         */
1764        void Archive::setName(String name) {
1765            if (m_name == name) return;
1766            m_name = name;
1767            m_isModified = true;
1768        }
1769    
1770        /** @brief Optional comments for this archive.
1771         *
1772         * Returns the optional comments for this archive that you might have
1773         * assigned to this archive before by calling setComment(). If you haven't
1774         * assigned any comment to this archive before, then this method simply
1775         * returns an empty string instead.
1776         */
1777        String Archive::comment() const {
1778            return m_comment;
1779        }
1780    
1781        /** @brief Assign a comment to this archive.
1782         *
1783         * You may optionally assign arbitrary comments to this archive. The comment
1784         * will be stored along with the archive, that is it will encoded with the
1785         * resulting raw data stream, and accordingly it will be decoded from the
1786         * raw data stream later on.
1787         *
1788         * @param comment - arbitrary new comment for this archive
1789         */
1790        void Archive::setComment(String comment) {
1791            if (m_comment == comment) return;
1792            m_comment = comment;
1793            m_isModified = true;
1794      }      }
1795    
1796        static tm _convertTimeStamp(const time_t& time, time_base_t base) {
1797            tm* pTm;
1798            switch (base) {
1799                case LOCAL_TIME:
1800                    pTm = localtime(&time);
1801                    break;
1802                case UTC_TIME:
1803                    pTm = gmtime(&time);
1804                    break;
1805                default:
1806                    throw Exception("Time stamp with unknown time base (" + ToString((int64_t)base) + ") requested");
1807            }
1808            if (!pTm)
1809                throw Exception("Failed assembling time stamp structure");
1810            return *pTm;
1811        }
1812    
1813        /** @brief Date and time when this archive was initially created.
1814         *
1815         * Returns a UTC time stamp (date and time) when this archive was initially
1816         * created.
1817         */
1818        time_t Archive::timeStampCreated() const {
1819            return m_timeCreated;
1820        }
1821    
1822        /** @brief Date and time when this archive was modified for the last time.
1823         *
1824         * Returns a UTC time stamp (date and time) when this archive was modified
1825         * for the last time.
1826         */
1827        time_t Archive::timeStampModified() const {
1828            return m_timeModified;
1829        }
1830    
1831        /** @brief Date and time when this archive was initially created.
1832         *
1833         * Returns a calendar time information representing the date and time when
1834         * this archive was initially created. The optional @a base parameter may
1835         * be used to define to which time zone the returned data and time shall be
1836         * related to.
1837         *
1838         * @param base - (optional) time zone the result shall relate to, by default
1839         *               UTC time (Greenwhich Mean Time) is assumed instead
1840         */
1841        tm Archive::dateTimeCreated(time_base_t base) const {
1842            return _convertTimeStamp(m_timeCreated, base);
1843        }
1844    
1845        /** @brief Date and time when this archive was modified for the last time.
1846         *
1847         * Returns a calendar time information representing the date and time when
1848         * this archive has been modified for the last time. The optional @a base
1849         * parameter may be used to define to which time zone the returned date and
1850         * time shall be related to.
1851         *
1852         * @param base - (optional) time zone the result shall relate to, by default
1853         *               UTC time (Greenwhich Mean Time) is assumed instead
1854         */
1855        tm Archive::dateTimeModified(time_base_t base) const {
1856            return _convertTimeStamp(m_timeModified, base);
1857        }
1858    
1859        /** @brief Remove a member variable from the given object.
1860         *
1861         * Removes the member variable @a member from its containing object
1862         * @a parent and sets the modified state of this archive to @c true.
1863         * If the given @a parent object does not contain the given @a member then
1864         * this method does nothing.
1865         *
1866         * This method provides a means of "partial" deserialization. By removing
1867         * either objects or members from this archive before calling deserialize(),
1868         * only the remaining objects and remaining members will be restored by this
1869         * framework, all other data of your C++ classes remain untouched.
1870         *
1871         * @param parent - Object which contains @a member
1872         * @param member - member to be removed
1873         * @see isModified() for details about the modified state.
1874         * @see Object for more details about the overall object reflection concept.
1875         */
1876        void Archive::removeMember(Object& parent, const Member& member) {
1877            parent.remove(member);
1878            m_isModified = true;
1879        }
1880    
1881        /** @brief Remove an object from this archive.
1882         *
1883         * Removes the object @obj from this archive and sets the modified state of
1884         * this archive to @c true. If the passed object is either invalid, or does
1885         * not exist in this archive, then this method does nothing.
1886         *
1887         * This method provides a means of "partial" deserialization. By removing
1888         * either objects or members from this archive before calling deserialize(),
1889         * only the remaining objects and remaining members will be restored by this
1890         * framework, all other data of your C++ classes remain untouched.
1891         *
1892         * @param obj - the object to be removed from this archive
1893         * @see isModified() for details about the modified state.
1894         * @see Object for more details about the overall object reflection concept.
1895         */
1896      void Archive::remove(const Object& obj) {      void Archive::remove(const Object& obj) {
1897            //FIXME: Should traverse from root object and remove all members associated with this object
1898          if (!obj.uid()) return;          if (!obj.uid()) return;
1899          m_allObjects.erase(obj.uid());          m_allObjects.erase(obj.uid());
1900          m_isModified = true;          m_isModified = true;
1901      }      }
1902    
1903        /** @brief Access object by its unique identifier.
1904         *
1905         * Returns the object of this archive with the given unique identifier
1906         * @a uid. If the given @a uid is invalid, or if this archive does not
1907         * contain an object with the given unique identifier, then this method
1908         * returns an invalid object instead.
1909         *
1910         * @param uid - unique identifier of sought object
1911         * @see Object for more details about the overall object reflection concept.
1912         * @see Object::isValid() for valid/invalid objects
1913         */
1914      Object& Archive::objectByUID(const UID& uid) {      Object& Archive::objectByUID(const UID& uid) {
1915          return m_allObjects[uid];          return m_allObjects[uid];
1916      }      }
1917    
1918        /** @brief Set the current version for the given object.
1919         *
1920         * Essentially behaves like above's setVersion() method, it just uses the
1921         * abstract reflection data type instead for the respective @a object being
1922         * passed to this method. Refer to above's setVersion() documentation about
1923         * the precise behavior details of setVersion().
1924         *
1925         * @param object - object to set the current version for
1926         * @param v - new current version to set for @a object
1927         */
1928        void Archive::setVersion(Object& object, Version v) {
1929            if (!object) return;
1930            object.setVersion(v);
1931            m_isModified = true;
1932        }
1933    
1934        /** @brief Set the minimum version for the given object.
1935         *
1936         * Essentially behaves like above's setMinVersion() method, it just uses the
1937         * abstract reflection data type instead for the respective @a object being
1938         * passed to this method. Refer to above's setMinVersion() documentation
1939         * about the precise behavior details of setMinVersion().
1940         *
1941         * @param object - object to set the minimum version for
1942         * @param v - new minimum version to set for @a object
1943         */
1944        void Archive::setMinVersion(Object& object, Version v) {
1945            if (!object) return;
1946            object.setMinVersion(v);
1947            m_isModified = true;
1948        }
1949    
1950        /** @brief Set new value for given @c enum object.
1951         *
1952         * Sets the new @a value to the given @c enum @a object.
1953         *
1954         * @param object - the @c enum object to be changed
1955         * @param value - the new value to be assigned to the @a object
1956         * @throws Exception if @a object is not an @c enum type.
1957         */
1958      void Archive::setEnumValue(Object& object, uint64_t value) {      void Archive::setEnumValue(Object& object, uint64_t value) {
1959          if (!object) return;          if (!object) return;
1960          if (!object.type().isEnum())          if (!object.type().isEnum())
# Line 787  namespace Serialization { Line 1987  namespace Serialization {
1987          m_isModified = true;          m_isModified = true;
1988      }      }
1989    
1990        /** @brief Set new integer value for given integer object.
1991         *
1992         * Sets the new integer @a value to the given integer @a object. Currently
1993         * this framework handles any integer data type up to 64 bit. For larger
1994         * integer types an assertion failure will be raised.
1995         *
1996         * @param object - the integer object to be changed
1997         * @param value - the new value to be assigned to the @a object
1998         * @throws Exception if @a object is not an integer type.
1999         */
2000      void Archive::setIntValue(Object& object, int64_t value) {      void Archive::setIntValue(Object& object, int64_t value) {
2001          if (!object) return;          if (!object) return;
2002          if (!object.type().isInteger())          if (!object.type().isInteger())
# Line 826  namespace Serialization { Line 2036  namespace Serialization {
2036          m_isModified = true;          m_isModified = true;
2037      }      }
2038    
2039        /** @brief Set new floating point value for given floating point object.
2040         *
2041         * Sets the new floating point @a value to the given floating point
2042         * @a object. Currently this framework supports single precision @c float
2043         * and double precision @c double floating point data types. For all other
2044         * floating point types this method will raise an assertion failure.
2045         *
2046         * @param object - the floating point object to be changed
2047         * @param value - the new value to be assigned to the @a object
2048         * @throws Exception if @a object is not a floating point based type.
2049         */
2050      void Archive::setRealValue(Object& object, double value) {      void Archive::setRealValue(Object& object, double value) {
2051          if (!object) return;          if (!object) return;
2052          if (!object.type().isReal())          if (!object.type().isReal())
# Line 848  namespace Serialization { Line 2069  namespace Serialization {
2069          m_isModified = true;          m_isModified = true;
2070      }      }
2071    
2072        /** @brief Set new boolean value for given boolean object.
2073         *
2074         * Sets the new boolean @a value to the given boolean @a object.
2075         *
2076         * @param object - the boolean object to be changed
2077         * @param value - the new value to be assigned to the @a object
2078         * @throws Exception if @a object is not a boolean type.
2079         */
2080      void Archive::setBoolValue(Object& object, bool value) {      void Archive::setBoolValue(Object& object, bool value) {
2081          if (!object) return;          if (!object) return;
2082          if (!object.type().isBool())          if (!object.type().isBool())
# Line 865  namespace Serialization { Line 2094  namespace Serialization {
2094          m_isModified = true;          m_isModified = true;
2095      }      }
2096    
2097        /** @brief Automatically cast and assign appropriate value to object.
2098         *
2099         * This method automatically converts the given @a value from textual string
2100         * representation into the appropriate data format of the requested
2101         * @a object. So this method is a convenient way to change values of objects
2102         * in this archive with your applications in automated way, i.e. for
2103         * implementing an editor where the user is able to edit values of objects
2104         * in this archive by entering the values as text with a keyboard.
2105         *
2106         * @throws Exception if the passed @a object is not a fundamental, primitive
2107         *         data type or if the provided textual value cannot be converted
2108         *         into an appropriate value for the requested object.
2109         */
2110      void Archive::setAutoValue(Object& object, String value) {      void Archive::setAutoValue(Object& object, String value) {
2111          if (!object) return;          if (!object) return;
2112          const DataType& type = object.type();          const DataType& type = object.type();
# Line 872  namespace Serialization { Line 2114  namespace Serialization {
2114              setIntValue(object, atoll(value.c_str()));              setIntValue(object, atoll(value.c_str()));
2115          else if (type.isReal())          else if (type.isReal())
2116              setRealValue(object, atof(value.c_str()));              setRealValue(object, atof(value.c_str()));
2117          else if (type.isBool())          else if (type.isBool()) {
2118              setBoolValue(object, atof(value.c_str()));              String val = toLowerCase(value);
2119          else if (type.isEnum())              if (val == "true" || val == "yes" || val == "1")
2120                    setBoolValue(object, true);
2121                else if (val == "false" || val == "no" || val == "0")
2122                    setBoolValue(object, false);
2123                else
2124                    setBoolValue(object, atof(value.c_str()));
2125            } else if (type.isEnum())
2126              setEnumValue(object, atoll(value.c_str()));              setEnumValue(object, atoll(value.c_str()));
2127          else          else
2128              throw Exception("Not a primitive data type");              throw Exception("Not a primitive data type");
2129      }      }
2130    
2131        /** @brief Get value of object as string.
2132         *
2133         * Converts the current value of the given @a object into a textual string
2134         * and returns that string.
2135         *
2136         * @param object - object whose value shall be retrieved
2137         * @throws Exception if the given object is either invalid, or if the object
2138         *         is not a fundamental, primitive data type.
2139         */
2140      String Archive::valueAsString(const Object& object) {      String Archive::valueAsString(const Object& object) {
2141          if (!object)          if (!object)
2142              throw Exception("Invalid object");              throw Exception("Invalid object");
# Line 894  namespace Serialization { Line 2151  namespace Serialization {
2151          return _primitiveObjectValueToString(*pObject);          return _primitiveObjectValueToString(*pObject);
2152      }      }
2153    
2154        /** @brief Get integer value of object.
2155         *
2156         * Returns the current integer value of the requested integer @a object or
2157         * @c enum object.
2158         *
2159         * @param object - object whose value shall be retrieved
2160         * @throws Exception if the given object is either invalid, or if the object
2161         *         is neither an integer nor @c enum data type.
2162         */
2163        int64_t Archive::valueAsInt(const Object& object) {
2164            if (!object)
2165                throw Exception("Invalid object");
2166            if (!object.type().isInteger() && !object.type().isEnum())
2167                throw Exception("Object is neither an integer nor an enum");
2168            const Object* pObject = &object;
2169            if (object.type().isPointer()) {
2170                const Object& obj = objectByUID(object.uid(1));
2171                if (!obj) return 0;
2172                pObject = &obj;
2173            }
2174            return _primitiveObjectValueToNumber<int64_t>(*pObject);
2175        }
2176    
2177        /** @brief Get floating point value of object.
2178         *
2179         * Returns the current floating point value of the requested floating point
2180         * @a object.
2181         *
2182         * @param object - object whose value shall be retrieved
2183         * @throws Exception if the given object is either invalid, or if the object
2184         *         is not a floating point based type.
2185         */
2186        double Archive::valueAsReal(const Object& object) {
2187            if (!object)
2188                throw Exception("Invalid object");
2189            if (!object.type().isReal())
2190                throw Exception("Object is not an real type");
2191            const Object* pObject = &object;
2192            if (object.type().isPointer()) {
2193                const Object& obj = objectByUID(object.uid(1));
2194                if (!obj) return 0;
2195                pObject = &obj;
2196            }
2197            return _primitiveObjectValueToNumber<double>(*pObject);
2198        }
2199    
2200        /** @brief Get boolean value of object.
2201         *
2202         * Returns the current boolean value of the requested boolean @a object.
2203         *
2204         * @param object - object whose value shall be retrieved
2205         * @throws Exception if the given object is either invalid, or if the object
2206         *         is not a boolean data type.
2207         */
2208        bool Archive::valueAsBool(const Object& object) {
2209            if (!object)
2210                throw Exception("Invalid object");
2211            if (!object.type().isBool())
2212                throw Exception("Object is not a bool");
2213            const Object* pObject = &object;
2214            if (object.type().isPointer()) {
2215                const Object& obj = objectByUID(object.uid(1));
2216                if (!obj) return 0;
2217                pObject = &obj;
2218            }
2219            return _primitiveObjectValueToNumber<bool>(*pObject);
2220        }
2221    
2222      // *************** Archive::Syncer ***************      // *************** Archive::Syncer ***************
2223      // *      // *
2224    
# Line 993  namespace Serialization { Line 2318  namespace Serialization {
2318      // *************** Exception ***************      // *************** Exception ***************
2319      // *      // *
2320    
2321        Exception::Exception() {
2322        }
2323    
2324        Exception::Exception(String format, ...) {
2325            va_list arg;
2326            va_start(arg, format);
2327            Message = assemble(format, arg);
2328            va_end(arg);
2329        }
2330    
2331        Exception::Exception(String format, va_list arg) {
2332            Message = assemble(format, arg);
2333        }
2334    
2335        /** @brief Print exception message to stdout.
2336         *
2337         * Prints the message of this Exception to the currently defined standard
2338         * output (that is to the terminal console for example).
2339         */
2340      void Exception::PrintMessage() {      void Exception::PrintMessage() {
2341          std::cout << "Serialization::Exception: " << Message << std::endl;          std::cout << "Serialization::Exception: " << Message << std::endl;
2342      }      }
2343    
2344        String Exception::assemble(String format, va_list arg) {
2345            char* buf = NULL;
2346            vasprintf(&buf, format.c_str(), arg);
2347            String s = buf;
2348            free(buf);
2349            return s;
2350        }
2351    
2352  } // namespace Serialization  } // namespace Serialization

Legend:
Removed from v.3150  
changed lines
  Added in v.3480

  ViewVC Help
Powered by ViewVC