/[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 3138 by schoenebeck, Wed May 3 14:41:58 2017 UTC revision 3777 by schoenebeck, Sat May 23 19:55:32 2020 UTC
# Line 1  Line 1 
1  /***************************************************************************  /***************************************************************************
2   *                                                                         *   *                                                                         *
3   *   Copyright (C) 2017 Christian Schoenebeck                              *   *   Copyright (C) 2017-2020 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>
 #include <assert.h>  
31  #include <string.h> // for memcpy()  #include <string.h> // for memcpy()
32  #include <stdlib.h> // for atof() and atof_l()  #include <stdlib.h> // for atof()
33  #include <xlocale.h> // for locale passed to atof_l()  #ifdef _MSC_VER
34    # include <windows.h>
35    # include <dbghelp.h>
36    #else
37    # include <cxxabi.h>
38    #endif
39  #include "helper.h"  #include "helper.h"
40    
41    #define LIBGIG_EPOCH_TIME ((time_t)0)
42    
43  namespace Serialization {  namespace Serialization {
44    
45      // *************** DataType ***************      // *************** DataType ***************
46      // *      // *
47    
48      static UID _createNullUID() {      static UID _createNullUID() {
49          return (UID) { NULL, 0 };          const UID uid = { NULL, 0 };
50            return uid;
51      }      }
52    
53      const UID NO_UID = _createNullUID();      const UID NO_UID = _createNullUID();
54    
55        /** @brief Check whether this is a valid unique identifier.
56         *
57         * Returns @c false if this UID can be considered an invalid unique
58         * identifier. This is for example the case if this UID object was not
59         * explicitly set to some certain meaningful unique identifier value, or if
60         * this UID object was intentionally assigned the constant @c NO_UID value.
61         * Both represent essentially an UID object which is all zero.
62         *
63         * Note that this class also implements the @c bool operator, both return
64         * the same boolean result.
65         */
66      bool UID::isValid() const {      bool UID::isValid() const {
67          return id != NULL && id != (void*)-1 && size;          return id != NULL && id != (void*)-1 && size;
68      }      }
# Line 49  namespace Serialization { Line 70  namespace Serialization {
70      // *************** DataType ***************      // *************** DataType ***************
71      // *      // *
72    
73        /** @brief Default constructor (as "invalid" DataType).
74         *
75         * Initializes a DataType object as being an "invalid" DataType object.
76         * Thus calling isValid(), after creating a DataType object with this
77         * constructor, would return @c false.
78         *
79         * To create a valid and meaningful DataType object instead, call the static
80         * function DataType::dataTypeOf() instead.
81         */
82      DataType::DataType() {      DataType::DataType() {
83          m_size = 0;          m_size = 0;
84          m_isPointer = false;          m_isPointer = false;
85      }      }
86    
87      DataType::DataType(bool isPointer, int size, String baseType, String customType) {      /** @brief Constructs a valid DataType object.
88         *
89         * Initializes this object as "valid" DataType object, with specific and
90         * useful data type information.
91         *
92         * This is a protected constructor which should not be called directly by
93         * applications, as its argument list is somewhat implementation specific
94         * and might change at any time. Applications should call the static
95         * function DataType::dataTypeOf() instead.
96         *
97         * @param isPointer - whether pointer type (i.e. a simple memory address)
98         * @param size - native size of data type in bytes (i.e. according to
99         *               @c sizeof() C/C++ keyword)
100         * @param baseType - this framework's internal name for specifying the base
101         *                   type in a coarse way, which must be either one of:
102         *                   "int8", "uint8", "int16", "uint16", "int32", "uint32",
103         *                   "int64", "uint64", "bool", "real32", "real64",
104         *                   "String", "Array", "Set", "enum", "union" or "class"
105         * @param customType1 - this is only used for base types "enum", "union",
106         *                     "class", "Array", "Set" or "Map", in which case this
107         *                      identifies the user defined type name (e.g. "Foo" for
108         *                      @c class @c Foo or e.g. "Bar" for @c Array<Bar>
109         *                      respectively), for all other types this is empty
110         * @param customType2 - this is only used for @c Map<> objects in which case
111         *                      it identifies the map's value type (i.e. 2nd
112         *                      template parameter of map)
113         */
114        DataType::DataType(bool isPointer, int size, String baseType,
115                           String customType1, String customType2)
116        {
117          m_size = size;          m_size = size;
118          m_isPointer = isPointer;          m_isPointer = isPointer;
119          m_baseTypeName = baseType;          m_baseTypeName = baseType;
120          m_customTypeName = customType;          m_customTypeName = customType1;
121            m_customTypeName2 = customType2;
122      }      }
123    
124        /** @brief Check if this is a valid DataType object.
125         *
126         * Returns @c true if this DataType object is reflecting a valid data type.
127         * The default constructor creates DataType objects initialized to be
128         * "invalid" DataType objects by default. That way one can detect whether
129         * a DataType object was ever assigned to something meaningful.
130         *
131         * Note that this class also implements the @c bool operator, both return
132         * the same boolean result.
133         */
134      bool DataType::isValid() const {      bool DataType::isValid() const {
135          return m_size;          return m_size;
136      }      }
137    
138        /** @brief Whether this is reflecting a C/C++ pointer type.
139         *
140         * Returns @true if the respective native C/C++ object, member or variable
141         * (this DataType instance is reflecting) is a C/C++ pointer type.
142         */
143      bool DataType::isPointer() const {      bool DataType::isPointer() const {
144          return m_isPointer;          return m_isPointer;
145      }      }
146    
147        /** @brief Whether this is reflecting a C/C++ @c struct or @c class type.
148         *
149         * Returns @c true if the respective native C/C++ object, member or variable
150         * (this DataType instance is reflecting) is a C/C++ @c struct or @c class
151         * type.
152         *
153         * @note: Data types which enjoy out of the box serialization support by
154         * this framework, like @c String and @c Array<> are @b NOT handled as class
155         * data types by this framwork. So @c isClass() returns @c false for e.g.
156         * @c String and any @c Array<> based data type.
157         *
158         * Note that in the following example:
159         * @code
160         * struct Foo {
161         *     int  a;
162         *     bool b;
163         * };
164         * Foo foo;
165         * Foo* pFoo;
166         * @endcode
167         * the DataType objects of both @c foo, as well as of the C/C++ pointer
168         * @c pFoo would both return @c true for isClass() here!
169         *
170         * @see isPointer()
171         */
172      bool DataType::isClass() const {      bool DataType::isClass() const {
173          return m_baseTypeName == "class";          return m_baseTypeName == "class";
174      }      }
175    
176        /** @brief Whether this is reflecting a fundamental C/C++ data type.
177         *
178         * Returns @c true if the respective native C/C++ object, member or variable
179         * (this DataType instance is reflecting) is a primitive, fundamental C/C++
180         * data type. Those are fundamental data types which are already predefined
181         * by the C/C++ language, for example: @c char, @c int, @c float, @c double,
182         * @c bool, but also @c String objects and @b any pointer types like
183         * @c int*, @c double**, but including pointers to user defined types like:
184         * @code
185         * struct Foo {
186         *     int  a;
187         *     bool b;
188         * };
189         * Foo* pFoo;
190         * @endcode
191         * So the DataType object of @c pFoo in the latter example would also return
192         * @c true for isPrimitive() here!
193         *
194         * @see isPointer()
195         */
196      bool DataType::isPrimitive() const {      bool DataType::isPrimitive() const {
197          return !isClass();          return !isClass() && !isArray() && !isSet() && !isMap();
198      }      }
199    
200        /** @brief Whether this is a C++ @c String data type.
201         *
202         * Returns @c true if the respective native C/C++ object, member or variable
203         * (this DataType instance is reflecting) is a C++ @c String object (a.k.a.
204         * @c std::string from the C++ STL).
205         *
206         * Note that this framework handles @c String objects as if they were a
207         * fundamental, primitive C/C++ data type, so @c isPrimitive() returns
208         * @c true for strings.
209         */
210        bool DataType::isString() const {
211            return m_baseTypeName == "String";
212        }
213    
214        /** @brief Whether this is an integer C/C++ data type.
215         *
216         * Returns @c true if the respective native C/C++ object, member or variable
217         * (this DataType instance is reflecting) is a (fundamental, primitive)
218         * integer data type. So these are all @c int and @c unsigned @c int types
219         * of any size. It does not include floating point ("real") types though.
220         *
221         * You may use isSigned() to further check whether this data type allows
222         * negative numbers.
223         *
224         * Note that this method also returns @c true on integer pointer types!
225         *
226         * @see isPointer()
227         */
228      bool DataType::isInteger() const {      bool DataType::isInteger() const {
229          return m_baseTypeName.substr(0, 3) == "int" ||          return m_baseTypeName.substr(0, 3) == "int" ||
230                 m_baseTypeName.substr(0, 4) == "uint";                 m_baseTypeName.substr(0, 4) == "uint";
231      }      }
232    
233        /** @brief Whether this is a floating point based C/C++ data type.
234         *
235         * Returns @c true if the respective native C/C++ object, member or variable
236         * (this DataType instance is reflecting) is a (fundamental, primitive)
237         * floating point based data type. So these are currently the C/C++ @c float
238         * and @c double types. It does not include integer types though.
239         *
240         * Note that this method also returns @c true on @c float pointer and
241         * @c double pointer types!
242         *
243         * @see isPointer()
244         */
245      bool DataType::isReal() const {      bool DataType::isReal() const {
246          return m_baseTypeName.substr(0, 4) == "real";          return m_baseTypeName.substr(0, 4) == "real";
247      }      }
248    
249        /** @brief Whether this is a boolean C/C++ data type.
250         *
251         * Returns @c true if the respective native C/C++ object, member or variable
252         * (this DataType instance is reflecting) is a (fundamental, primitive)
253         * boolean data type. So this is the case for the C++ @c bool data type.
254         * It does not include integer or floating point types though.
255         *
256         * Note that this method also returns @c true on @c bool pointer types!
257         *
258         * @see isPointer()
259         */
260      bool DataType::isBool() const {      bool DataType::isBool() const {
261          return m_baseTypeName == "bool";          return m_baseTypeName == "bool";
262      }      }
263    
264        /** @brief Whether this is a C/C++ @c enum data type.
265         *
266         * Returns @c true if the respective native C/C++ object, member or variable
267         * (this DataType instance is reflecting) is a user defined enumeration
268         * data type. So this is the case for all C/C++ @c enum data types.
269         * It does not include integer (or even floating point) types though.
270         *
271         * Note that this method also returns @c true on @c enum pointer types!
272         *
273         * @see isPointer()
274         */
275      bool DataType::isEnum() const {      bool DataType::isEnum() const {
276          return m_baseTypeName == "enum";          return m_baseTypeName == "enum";
277      }      }
278    
279        /** @brief Whether this is a C++ @c Array<> object type.
280         *
281         * Returns @c true if the respective native C/C++ object, member or variable
282         * (this DataType instance is reflecting) is a C++ @c Array<> container
283         * object type.
284         *
285         * @note: This framework handles @c Array<> types neither as primitive
286         * types, nor as class types. So @c isPrimitive() and @c isClass() both
287         * return @c false for arrays.
288         *
289         * @see isPointer()
290         */
291        bool DataType::isArray() const {
292            return m_baseTypeName == "Array";
293        }
294    
295        /** @brief Whether this is a C++ @c Set<> object type.
296         *
297         * Returns @c true if the respective native C/C++ object, member or variable
298         * (this DataType instance is reflecting) is a C++ @c Set<> unique container
299         * object type.
300         *
301         * @note: This framework handles @c Set<> types neither as primitive
302         * types, nor as class types. So @c isPrimitive() and @c isClass() both
303         * return @c false for sets.
304         *
305         * @see isPointer()
306         */
307        bool DataType::isSet() const {
308            return m_baseTypeName == "Set";
309        }
310    
311        /** @brief Whether this is a C++ @c Map<> object type.
312         *
313         * Returns @c true if the respective native C/C++ object, member or variable
314         * (this DataType instance is reflecting) is an associative sorted C++
315         * @c Map<> container object type.
316         *
317         * @note: This framework handles @c Map<> types neither as primitive
318         * types, nor as class types. So @c isPrimitive() and @c isClass() both
319         * return @c false for maps.
320         *
321         * @see isPointer()
322         */
323        bool DataType::isMap() const {
324            return m_baseTypeName == "Map";
325        }
326    
327        /** @brief Whether this is a signed integer C/C++ data type.
328         *
329         * Returns @c true if the respective native C/C++ object, member or variable
330         * (this DataType instance is reflecting) is a (fundamental, primitive)
331         * signed integer data type. This is the case for are all @c unsigned
332         * @c int C/C++ types of any size. For all floating point ("real") based
333         * types this method returns @c false though!
334         *
335         * Note that this method also returns @c true on signed integer pointer
336         * types!
337         *
338         * @see isInteger();
339         */
340      bool DataType::isSigned() const {      bool DataType::isSigned() const {
341          return m_baseTypeName.substr(0, 3) == "int" ||          return m_baseTypeName.substr(0, 3) == "int" ||
342                 isReal();                 isReal();
343      }      }
344    
345        /** @brief Comparison for equalness.
346         *
347         * Returns @c true if the two DataType objects being compared can be
348         * considered to be "equal" C/C++ data types. They are considered to be
349         * equal if their underlying C/C++ data types are exactly identical. For
350         * example comparing @c int and @c unsigned int data types are considere to
351         * be @b not equal, since they are differently signed. Furthermore @c short
352         * @c int and @c long @c int would also not be considered to be equal, since
353         * they do have a different memory size. Additionally pointer type
354         * characteristic is compared as well. So a @c double type and @c double*
355         * type are also considered to be not equal data types and hence this method
356         * would return @c false.
357         *
358         * As an exception here, classes and structs with the same class/struct name
359         * but different sizes are also considered to be "equal". This relaxed
360         * requirement is necessary to retain backward compatiblity to older
361         * versions of the same native C++ classes/structs.
362         */
363      bool DataType::operator==(const DataType& other) const {      bool DataType::operator==(const DataType& other) const {
364          return m_baseTypeName   == other.m_baseTypeName &&          return m_baseTypeName   == other.m_baseTypeName &&
365                 m_customTypeName == other.m_customTypeName &&                 m_customTypeName == other.m_customTypeName &&
366                 m_size           == other.m_size &&                 m_customTypeName2 == other.m_customTypeName2 &&
367                   (m_size == other.m_size || (isClass() && other.isClass())) &&
368                 m_isPointer      == other.m_isPointer;                 m_isPointer      == other.m_isPointer;
369      }      }
370    
371        /** @brief Comparison for inequalness.
372         *
373         * Returns the inverse result of what DataType::operator==() would return.
374         * So refer to the latter for more details.
375         */
376      bool DataType::operator!=(const DataType& other) const {      bool DataType::operator!=(const DataType& other) const {
377          return !operator==(other);          return !operator==(other);
378      }      }
379    
380        /** @brief Smaller than comparison.
381         *
382         * Returns @c true if this DataType object can be consider to be "smaller"
383         * than the @a other DataType object being compared with. This operator
384         * is actually quite arbitrarily implemented and may change at any time,
385         * and thus result for the same data types may change in future at any time.
386         *
387         * This operator is basically implemented for allowing this DataType class
388         * to be used with various standard template library (STL) classes, which
389         * require sorting operators to be implemented.
390         */
391      bool DataType::operator<(const DataType& other) const {      bool DataType::operator<(const DataType& other) const {
392          return m_baseTypeName  < other.m_baseTypeName ||          return m_baseTypeName  < other.m_baseTypeName ||
393                (m_baseTypeName == other.m_baseTypeName &&                (m_baseTypeName == other.m_baseTypeName &&
394                 m_customTypeName  < other.m_customTypeName ||                (m_customTypeName  < other.m_customTypeName ||
395                (m_customTypeName == other.m_customTypeName &&                (m_customTypeName == other.m_customTypeName &&
396                 m_size  < other.m_size ||                (m_customTypeName2  < other.m_customTypeName2 ||
397                  (m_customTypeName2 == other.m_customTypeName2 &&
398                  (m_size  < other.m_size ||
399                (m_size == other.m_size &&                (m_size == other.m_size &&
400                 m_isPointer < other.m_isPointer)));                 m_isPointer < other.m_isPointer)))))));
401      }      }
402    
403        /** @brief Greater than comparison.
404         *
405         * Returns @c true if this DataType object can be consider to be "greater"
406         * than the @a other DataType object being compared with. This operator
407         * is actually quite arbitrarily implemented and may change at any time,
408         * and thus result for the same data types may change in future at any time.
409         *
410         * This operator is basically implemented for allowing this DataType class
411         * to be used with various standard template library (STL) classes, which
412         * require sorting operators to be implemented.
413         */
414      bool DataType::operator>(const DataType& other) const {      bool DataType::operator>(const DataType& other) const {
415          return !(operator==(other) || operator<(other));          return !(operator==(other) || operator<(other));
416      }      }
417    
418        /** @brief Human readable long description for this data type.
419         *
420         * Returns a human readable long description for this data type, designed
421         * for the purpose for being displayed to the user. Note that the
422         * implementation for this method and thus the precise textual strings
423         * returned by this method, may change at any time. So you should not rely
424         * on precise strings for certain data types, and you should not use the
425         * return values of this method for comparing data types with each other.
426         *
427         * This class implements various comparison operators, so you should use
428         * them for comparing DataTypes objects instead.
429         *
430         * @see baseTypeName(), customTypeName()
431         */
432      String DataType::asLongDescr() const {      String DataType::asLongDescr() const {
         //TODO: Demangling of C++ raw type names  
433          String s = m_baseTypeName;          String s = m_baseTypeName;
434          if (!m_customTypeName.empty())          if (!m_customTypeName.empty())
435              s += " " + m_customTypeName;              s += " " + customTypeName(true);
436            if (!m_customTypeName2.empty())
437                s += " " + customTypeName2(true);
438          if (isPointer())          if (isPointer())
439              s += " pointer";              s += " pointer";
440          return s;          return s;
441      }      }
442    
443        /** @brief The base type name of this data type.
444         *
445         * Returns a textual short string identifying the basic type of name of this
446         * data type. For example for a 32 bit signed integer data type this method
447         * would return @c "int32". For all user defined C/C++ @c enum types this
448         * method would return "enum". For all user defined C/C++ @c struct @b and
449         * @c class types this method would return "class" for both. Note that the
450         * precise user defined type name (of i.e. @c enum, @c struct and @c class
451         * types) is not included in the string returned by this method, use
452         * customTypeName() to retrieve that information instead.
453         *
454         * The precise textual strings returned by this method are guaranteed to
455         * retain equal with future versions of this framework. So you can rely on
456         * them for using the return values of this method for comparison tasks in
457         * your application. Note however that this class also implements various
458         * comparison operators.
459         *
460         * Further it is important to know that this method returns the same string
461         * for pointers and non-pointers of the same underlying data type. So in the
462         * following example:
463         * @code
464         * #include <stdint.h>
465         * uint64_t i;
466         * uint64_t* pi;
467         * @endcode
468         * this method would return for both @c i and @c pi the string @c "uint64" !
469         *
470         * @see isPointer(), customTypeName(), customTypeName2()
471         */
472        String DataType::baseTypeName() const {
473            return m_baseTypeName;
474        }
475    
476        static String _demangleTypeName(const char* name) {
477    #ifdef _MSC_VER
478            const size_t MAXLENGTH = 1024;
479            char result[MAXLENGTH];
480    
481            //FIXME: calling UnDecorateSymbolName() is not thread safe!
482            //Skip the first char
483            size_t size = UnDecorateSymbolName(name + 1, result, MAXLENGTH, UNDNAME_32_BIT_DECODE | UNDNAME_NO_ARGUMENTS);
484            if (size)
485            {
486                return result;
487            }
488            return name;
489    #else
490            int status;
491            char* result =
492                abi::__cxa_demangle(name, 0, 0, &status);
493            String sResult = result;
494            free(result);
495            return (status == 0) ? sResult : name;
496    #endif
497        }
498    
499        /** @brief The 1st user defined C/C++ data type name of this data type.
500         *
501         * Call this method on user defined C/C++ data types like @c enum,
502         * @c struct, @c class or @c Array<> types to retrieve the user defined type
503         * name portion of those data types. Note that this method is only intended
504         * for such user defined data types. For all fundamental, primitive data
505         * types (like i.e. @c int) this method returns an empty string instead.
506         *
507         * This method takes an optional boolean argument @b demangle, which allows
508         * you define whether you are interested in the raw C++ type name or rather
509         * the demangled custom type name. By default this method returns the raw
510         * C++ type name. The raw C++ type name is the one that is actually used
511         * in the compiled binaries and should be preferred for comparions tasks.
512         * The demangled C++ type name is a human readable representation of the
513         * type name instead, which you may use for displaying the user defined type
514         * name portion to the user, however you should not use the demangled
515         * representation for comparison tasks.
516         *
517         * Note that in the following example:
518         * @code
519         * struct Foo {
520         *     int  a;
521         *     bool b;
522         * };
523         * Foo foo;
524         * Foo* pFoo;
525         * @endcode
526         * this method would return the same string for both @c foo and @c pFoo !
527         * In the latter example @c customTypeName(true) would return for both
528         * @c foo and @c pFoo the string @c "Foo" as return value of this method.
529         *
530         * @b Windows: please note that the current implementation of this method
531         * on Windows is @b not thread safe!
532         *
533         * @see baseTypeName(), customTypeName2(), isPointer()
534         */
535        String DataType::customTypeName(bool demangle) const {
536            if (!demangle) return m_customTypeName;
537            return _demangleTypeName(m_customTypeName.c_str());
538        }
539    
540        /** @brief The 2nd user defined C/C++ data type name of this data type.
541         *
542         * This is currently only used for @c Map<> data types in which case this
543         * method returns the map's value type (i.e. map's 2nd template parameter).
544         *
545         * @see baseTypeName(), customTypeName()
546         */
547        String DataType::customTypeName2(bool demangle) const {
548            if (!demangle) return m_customTypeName2;
549            return _demangleTypeName(m_customTypeName2.c_str());
550        }
551    
552      // *************** Member ***************      // *************** Member ***************
553      // *      // *
554    
555        /** @brief Default constructor.
556         *
557         * Initializes a Member object as being an "invalid" Member object.
558         * Thus calling isValid(), after creating a Member object with this
559         * constructor, would return @c false.
560         *
561         * You are currently not supposed to create (meaningful) Member objects on
562         * your own. This framework automatically create such Member objects for
563         * you instead.
564         *
565         * @see Object::members()
566         */
567      Member::Member() {      Member::Member() {
568          m_uid = NO_UID;          m_uid = NO_UID;
569          m_offset = 0;          m_offset = 0;
570      }      }
571    
572      Member::Member(String name, UID uid, size_t offset, DataType type) {      Member::Member(String name, UID uid, ssize_t offset, DataType type) {
573          m_name = name;          m_name = name;
574          m_uid  = uid;          m_uid  = uid;
575          m_offset = offset;          m_offset = offset;
576          m_type = type;          m_type = type;
577      }      }
578    
579        /** @brief Unique identifier of this member instance.
580         *
581         * Returns the unique identifier of the original C/C++ member instance of
582         * your C++ class. It is important to know that this unique identifier is
583         * not meant to be unique for Member instances themselves, but it is rather
584         * meant to be unique for the original native C/C++ data these Member
585         * instances are representing. So that means no matter how many individual
586         * Member objects are created, as long as they are representing the same
587         * original native member variable of the same original native
588         * instance of your C++ class, then all those separately created Member
589         * objects return the same unique identifier here.
590         *
591         * @see UID for more details
592         */
593        UID Member::uid() const {
594            return m_uid;
595        }
596    
597        /** @brief Name of the member.
598         *
599         * Returns the name of the native C/C++ member variable as originally typed
600         * in its C++ source code. So in the following example:
601         * @code
602         * struct Foo {
603         *     int  a;
604         *     bool b;
605         *     double someValue;
606         * };
607         * @endcode
608         * this method would usually return @c "a" for the first member of object
609         * instances of your native C/C++ @c struct @c Foo, and this method would
610         * usually return @c "someValue" for its third member.
611         *
612         * Note that when you implement the @c serialize() method of your own C/C++
613         * clases or strucs, you are able to override defining the precise name of
614         * your members. In that case this method would of course return the member
615         * names as explicitly forced by you instead.
616         */
617        String Member::name() const {
618            return m_name;
619        }
620    
621        /** @brief Offset of member in its containing parent data structure.
622         *
623         * Returns the offset of this member (in bytes) within its containing parent
624         * user defined data structure or class. So in the following example:
625         * @code
626         * #include <stdint.h>
627         * struct Foo __attribute__ ((__packed__)) {
628         *     int32_t a;
629         *     bool b;
630         *     double c;
631         * };
632         * @endcode
633         * this method would typically return @c 0 for member @c a, @c 4 for member
634         * @c b and @c 5 for member @c c. As you have noted in the latter example,
635         * the structure @c Foo was declared to have "packed" data members. That
636         * means the compiler is instructed to add no memory spaces between the
637         * individual members. Because by default the compiler might add memory
638         * spaces between individual members to align them on certain memory address
639         * boundaries for increasing runtime performance while accessing the
640         * members. So if you declared the previous example without the "packed"
641         * attribute like:
642         * @code
643         * #include <stdint.h>
644         * struct Foo {
645         *     int32_t a;
646         *     bool b;
647         *     double c;
648         * };
649         * @endcode
650         * then this method would usually return a different offset for members
651         * @c b and @c c instead. For most 64 bit architectures this example would
652         * now still return @c 0 for member @c a, but @c 8 for member @c b and @c 16
653         * for member @c c.
654         *
655         * @note Offset is intended for native members only, that is member
656         * variables which are memory located directly within the associated parent
657         * data structure. For members allocated on the heap @c offset() always
658         * returns @c -1 instead since there is no constant, static offset
659         * relationship between data on the heap and the parent structure owning
660         * their life-time control.
661         */
662        ssize_t Member::offset() const {
663            return m_offset;
664        }
665    
666        /** @brief C/C++ Data type of this member.
667         *
668         * Returns the precise data type of the original native C/C++ member.
669         */
670        const DataType& Member::type() const {
671            return m_type;
672        }
673    
674        /** @brief Check if this is a valid Member object.
675         *
676         * Returns @c true if this Member object is reflecting a "valid" member
677         * object. The default constructor creates Member objects initialized to be
678         * "invalid" Member objects by default. That way one can detect whether
679         * a Member object was ever assigned to something meaningful.
680         *
681         * Note that this class also implements the @c bool operator, both return
682         * the same boolean result value.
683         */
684      bool Member::isValid() const {      bool Member::isValid() const {
685          return m_uid && !m_name.empty() && m_type;          return m_uid && !m_name.empty() && m_type;
686      }      }
687    
688        /** @brief Comparison for equalness.
689         *
690         * Returns @c true if the two Member objects being compared can be
691         * considered to be "equal" C/C++ members. They are considered to be
692         * equal if their data type, member name, their offset within their parent
693         * containing C/C++ data structure, as well as their original native C/C++
694         * instance were exactly identical.
695         */
696      bool Member::operator==(const Member& other) const {      bool Member::operator==(const Member& other) const {
697          return m_uid    == other.m_uid &&          return m_uid    == other.m_uid &&
698                 m_offset == other.m_offset &&                 m_offset == other.m_offset &&
# Line 160  namespace Serialization { Line 700  namespace Serialization {
700                 m_type   == other.m_type;                 m_type   == other.m_type;
701      }      }
702    
703        /** @brief Comparison for inequalness.
704         *
705         * Returns the inverse result of what Member::operator==() would return.
706         * So refer to the latter for more details.
707         */
708      bool Member::operator!=(const Member& other) const {      bool Member::operator!=(const Member& other) const {
709          return !operator==(other);          return !operator==(other);
710      }      }
711    
712        /** @brief Smaller than comparison.
713         *
714         * Returns @c true if this Member object can be consider to be "smaller"
715         * than the @a other Member object being compared with. This operator
716         * is actually quite arbitrarily implemented and may change at any time,
717         * and thus result for the same member representations may change in
718         * future at any time.
719         *
720         * This operator is basically implemented for allowing this DataType class
721         * to be used with various standard template library (STL) classes, which
722         * require sorting operators to be implemented.
723         */
724      bool Member::operator<(const Member& other) const {      bool Member::operator<(const Member& other) const {
725          return m_uid  < other.m_uid ||          return m_uid  < other.m_uid ||
726                (m_uid == other.m_uid &&                (m_uid == other.m_uid &&
727                 m_offset  < other.m_offset ||                (m_offset  < other.m_offset ||
728                (m_offset == other.m_offset &&                (m_offset == other.m_offset &&
729                 m_name  < other.m_name ||                (m_name  < other.m_name ||
730                (m_name == other.m_name &&                (m_name == other.m_name &&
731                 m_type < other.m_type)));                 m_type < other.m_type)))));
732      }      }
733    
734        /** @brief Greater than comparison.
735         *
736         * Returns @c true if this Member object can be consider to be "greater"
737         * than the @a other Member object being compared with. This operator
738         * is actually quite arbitrarily implemented and may change at any time,
739         * and thus result for the same member representations may change in
740         * future at any time.
741         *
742         * This operator is basically implemented for allowing this DataType class
743         * to be used with various standard template library (STL) classes, which
744         * require sorting operators to be implemented.
745         */
746      bool Member::operator>(const Member& other) const {      bool Member::operator>(const Member& other) const {
747          return !(operator==(other) || operator<(other));          return !(operator==(other) || operator<(other));
748      }      }
# Line 181  namespace Serialization { Line 750  namespace Serialization {
750      // *************** Object ***************      // *************** Object ***************
751      // *      // *
752    
753        /** @brief Default constructor (for an "invalid" Object).
754         *
755         * Initializes an Object instance as being an "invalid" Object.
756         * Thus calling isValid(), after creating an Object instance with this
757         * constructor, would return @c false.
758         *
759         * Usually you are not supposed to create (meaningful) Object instances on
760         * your own. They are typically constructed by the Archive class for you.
761         *
762         * @see Archive::rootObject(), Archive::objectByUID()
763         */
764      Object::Object() {      Object::Object() {
765          m_version = 0;          m_version = 0;
766          m_minVersion = 0;          m_minVersion = 0;
767      }      }
768    
769        /** @brief Constructor for a "meaningful" Object.
770         *
771         * Initializes a "meaningful" Object instance as being. Thus calling
772         * isValid(), after creating an Object instance with this constructor,
773         * should return @c true, provided that the arguments passed to this
774         * constructor construe a valid object representation.
775         *
776         * Usually you are not supposed to create (meaningful) Object instances on
777         * your own. They are typically constructed by the Archive class for you.
778         *
779         * @see Archive::rootObject(), Archive::objectByUID()
780         *
781         * @param uidChain - unique identifier chain of the object to be constructed
782         * @param type - C/C++ data type of the actual native object this abstract
783         *               Object instance should reflect after calling this
784         *               constructor
785         */
786      Object::Object(UIDChain uidChain, DataType type) {      Object::Object(UIDChain uidChain, DataType type) {
787          m_type = type;          m_type = type;
788          m_uid  = uidChain;          m_uid  = uidChain;
789          m_version = 0;          m_version = 0;
790          m_minVersion = 0;          m_minVersion = 0;
791          m_data.resize(type.size());          //m_data.resize(type.size());
792      }      }
793    
794        /** @brief Check if this is a valid Object instance.
795         *
796         * Returns @c true if this Object instance is reflecting a "valid" Object.
797         * The default constructor creates Object instances initialized to be
798         * "invalid" Objects by default. That way one can detect whether an Object
799         * instance was ever assigned to something meaningful.
800         *
801         * Note that this class also implements the @c bool operator, both return
802         * the same boolean result value.
803         */
804      bool Object::isValid() const {      bool Object::isValid() const {
805          return m_type && !m_uid.empty();          return m_type && !m_uid.empty();
806      }      }
807    
808        /** @brief Unique identifier of this Object.
809         *
810         * Returns the unique identifier for the original native C/C++ data this
811         * abstract Object instance is reflecting. If this Object is representing
812         * a C/C++ pointer (of first degree) then @c uid() (or @c uid(0) ) returns
813         * the unique identifier of the pointer itself, whereas @c uid(1) returns
814         * the unique identifier of the original C/C++ data that pointer was
815         * actually pointing to.
816         *
817         * @see UIDChain for more details about this overall topic.
818         */
819        UID Object::uid(int index) const {
820            return (index < m_uid.size()) ? m_uid[index] : NO_UID;
821        }
822    
823        static void _setNativeValueFromString(void* ptr, const DataType& type, const char* s) {
824            if (type.isPrimitive() && !type.isPointer()) {
825                if (type.isInteger() || type.isEnum()) {
826                    if (type.isSigned()) {
827                        if (type.size() == 1)
828                            *(int8_t*)ptr = (int8_t) atoll(s);
829                        else if (type.size() == 2)
830                            *(int16_t*)ptr = (int16_t) atoll(s);
831                        else if (type.size() == 4)
832                            *(int32_t*)ptr = (int32_t) atoll(s);
833                        else if (type.size() == 8)
834                            *(int64_t*)ptr = (int64_t) atoll(s);
835                        else
836                            assert(false /* unknown signed int type size */);
837                    } else {
838                        if (type.size() == 1)
839                            *(uint8_t*)ptr = (uint8_t) atoll(s);
840                        else if (type.size() == 2)
841                            *(uint16_t*)ptr = (uint16_t) atoll(s);
842                        else if (type.size() == 4)
843                            *(uint32_t*)ptr = (uint32_t) atoll(s);
844                        else if (type.size() == 8)
845                            *(uint64_t*)ptr = (uint64_t) atoll(s);
846                        else
847                            assert(false /* unknown unsigned int type size */);
848                    }
849                } else if (type.isReal()) {
850                    if (type.size() == sizeof(float))
851                        *(float*)ptr = (float) atof(s);
852                    else if (type.size() == sizeof(double))
853                        *(double*)ptr = (double) atof(s);
854                    else
855                        assert(false /* unknown floating point type */);
856                } else if (type.isBool()) {
857                    String lower = toLowerCase(s);
858                    const bool b = lower != "0" && lower != "false" && lower != "no";
859                    *(bool*)ptr = b;
860                } else if (type.isString()) {
861                    *(String*)ptr = s;
862                } else {
863                    assert(false /* no built-in cast from string support for this data type */);
864                }
865            }
866        }
867    
868        /** @brief Cast from string to object's data type and assign value natively.
869         *
870         * The passed String @a s is decoded from its string representation to this
871         * object's corresponding native data type, then that casted value is
872         * assigned to the native memory location this Object is referring to.
873         *
874         * Note: This method may only be called for data types which enjoy built-in
875         * support for casting from string to their native data type, which are
876         * basically primitive data types (e.g. @c int, @c bool, @c double, etc.) or
877         * @c String objects. For all other data types calling this method will
878         * cause an assertion fault at runtime.
879         *
880         * @param s - textual string representation of the value to be assigned to
881         *            this object
882         */
883        void Object::setNativeValueFromString(const String& s) {
884            const ID& id = uid().id;
885            void* ptr = (void*)id;
886            _setNativeValueFromString(ptr, m_type, s.c_str());
887        }
888    
889        /** @brief Unique identifier chain of this Object.
890         *
891         * Returns the entire unique identifier chain of this Object.
892         *
893         * @see uid() and UIDChain for more details about this overall topic.
894         */
895        const UIDChain& Object::uidChain() const {
896            return m_uid;
897        }
898    
899        /** @brief C/C++ data type this Object is reflecting.
900         *
901         * Returns the precise original C/C++ data type of the original native
902         * C/C++ object or data this Object instance is reflecting.
903         */
904        const DataType& Object::type() const {
905            return m_type;
906        }
907    
908        /** @brief Raw data of the original native C/C++ data.
909         *
910         * Returns the raw data value of the original C/C++ data this Object is
911         * reflecting. So the precise raw data value, layout and size is dependent
912         * to the precise C/C++ data type of the original native C/C++ data. However
913         * potentially required endian correction is already automatically applied
914         * for you. That means you can safely, directly C-cast the raw data returned
915         * by this method to the respective native C/C++ data type in order to
916         * access and use the value for some purpose, at least if the respective
917         * data is of any fundamental, primitive C/C++ data type, or also to a
918         * certain extent if the type is user defined @c enum type.
919         *
920         * However directly C-casting this raw data for user defined @c struct or
921         * @c class types is not possible. For those user defined data structures
922         * this method always returns empty raw data instead.
923         *
924         * Note however that there are more convenient methods in the Archive class
925         * to get the right value for the individual data types instead.
926         *
927         * @see Archive::valueAsInt(), Archive::valueAsReal(), Archive::valueAsBool(),
928         *      Archive::valueAsString()
929         */
930        const RawData& Object::rawData() const {
931            return m_data;
932        }
933    
934        /** @brief Version of original user defined C/C++ @c struct or @c class.
935         *
936         * In case this Object is reflecting a native C/C++ @c struct or @c class
937         * type, then this method returns the version of that native C/C++ @c struct
938         * or @c class layout or implementation. For primitive, fundamental C/C++
939         * data types (including @c String objects) the return value of this method
940         * has no meaning.
941         *
942         * @see Archive::setVersion() for more details about this overall topic.
943         */
944        Version Object::version() const {
945            return m_version;
946        }
947    
948        /** @brief Minimum version of original user defined C/C++ @c struct or @c class.
949         *
950         * In case this Object is reflecting a native C/C++ @c struct or @c class
951         * type, then this method returns the "minimum" version of that native C/C++
952         * @c struct or @c class layout or implementation which it may be compatible
953         * with. For primitive, fundamental C/C++ data types (including @c String
954         * objects) the return value of this method has no meaning.
955         *
956         * @see Archive::setVersion() and Archive::setMinVersion() for more details
957         *      about this overall topic.
958         */
959        Version Object::minVersion() const {
960            return m_minVersion;
961        }
962    
963        /** @brief All members of the original native C/C++ @c struct or @c class instance.
964         *
965         * In case this Object is reflecting a native C/C++ @c struct or @c class
966         * type, then this method returns all member variables of that original
967         * native C/C++ @c struct or @c class instance. For primitive, fundamental
968         * C/C++ data types this method returns an empty vector instead.
969         *
970         * Example:
971         * @code
972         * struct Foo {
973         *     int  a;
974         *     bool b;
975         *     double someValue;
976         * };
977         * @endcode
978         * Considering above's C++ code, a serialized Object representation of such
979         * a native @c Foo class would have 3 members @c a, @c b and @c someValue.
980         *
981         * Note that the respective serialize() method implementation of that
982         * fictional C++ @c struct @c Foo actually defines which members are going
983         * to be serialized and deserialized for instances of class @c Foo. So in
984         * practice the members returned by method members() here might return a
985         * different set of members as actually defined in the original C/C++ struct
986         * header declaration.
987         *
988         * The precise sequence of the members returned by this method here depends
989         * on the actual serialize() implementation of the user defined C/C++
990         * @c struct or @c class.
991         *
992         * @see Object::sequenceIndexOf() for more details about the precise order
993         *      of members returned by this method in the same way.
994         */
995        std::vector<Member>& Object::members() {
996            return m_members;
997        }
998    
999        /** @brief All members of the original native C/C++ @c struct or @c class instance (read only).
1000         *
1001         * Returns the same result as overridden members() method above, it just
1002         * returns a read-only result instead. See above's method description for
1003         * details for the return value of this method instead.
1004         */
1005        const std::vector<Member>& Object::members() const {
1006            return m_members;
1007        }
1008    
1009        /** @brief Comparison for equalness.
1010         *
1011         * Returns @c true if the two Object instances being compared can be
1012         * considered to be "equal" native C/C++ object instances. They are
1013         * considered to be equal if they are representing the same original
1014         * C/C++ data instance, which is essentially the case if the original
1015         * reflecting native C/C++ data are sharing the same memory address and
1016         * memory size (thus the exact same memory space) and originally had the
1017         * exact same native C/C++ types.
1018         */
1019      bool Object::operator==(const Object& other) const {      bool Object::operator==(const Object& other) const {
1020          // ignoring all other member variables here          // ignoring all other member variables here
1021          // (since UID stands for "unique" ;-) )          // (since UID stands for "unique" ;-) )
# Line 205  namespace Serialization { Line 1023  namespace Serialization {
1023                 m_type == other.m_type;                 m_type == other.m_type;
1024      }      }
1025    
1026        /** @brief Comparison for inequalness.
1027         *
1028         * Returns the inverse result of what Object::operator==() would return.
1029         * So refer to the latter for more details.
1030         */
1031      bool Object::operator!=(const Object& other) const {      bool Object::operator!=(const Object& other) const {
1032          return !operator==(other);          return !operator==(other);
1033      }      }
1034    
1035        /** @brief Smaller than comparison.
1036         *
1037         * Returns @c true if this Object instance can be consider to be "smaller"
1038         * than the @a other Object instance being compared with. This operator
1039         * is actually quite arbitrarily implemented and may change at any time,
1040         * and thus result for the same Object representations may change in future
1041         * at any time.
1042         *
1043         * This operator is basically implemented for allowing this DataType class
1044         * to be used with various standard template library (STL) classes, which
1045         * require sorting operators to be implemented.
1046         */
1047      bool Object::operator<(const Object& other) const {      bool Object::operator<(const Object& other) const {
1048          // ignoring all other member variables here          // ignoring all other member variables here
1049          // (since UID stands for "unique" ;-) )          // (since UID stands for "unique" ;-) )
# Line 217  namespace Serialization { Line 1052  namespace Serialization {
1052                 m_type < other.m_type);                 m_type < other.m_type);
1053      }      }
1054    
1055        /** @brief Greater than comparison.
1056         *
1057         * Returns @c true if this Object instance can be consider to be "greater"
1058         * than the @a other Object instance being compared with. This operator
1059         * is actually quite arbitrarily implemented and may change at any time,
1060         * and thus result for the same Object representations may change in future
1061         * at any time.
1062         *
1063         * This operator is basically implemented for allowing this DataType class
1064         * to be used with various standard template library (STL) classes, which
1065         * require sorting operators to be implemented.
1066         */
1067      bool Object::operator>(const Object& other) const {      bool Object::operator>(const Object& other) const {
1068          return !(operator==(other) || operator<(other));          return !(operator==(other) || operator<(other));
1069      }      }
1070    
1071        /** @brief Check version compatibility between Object instances.
1072         *
1073         * Use this method to check whether the two original C/C++ instances those
1074         * two Objects are reflecting, were using a C/C++ data type which are version
1075         * compatible with each other. By default all C/C++ Objects are considered
1076         * to be version compatible. They might only be version incompatible if you
1077         * enforced a certain backward compatibility constraint with your
1078         * serialize() method implementation of your custom C/C++ @c struct or
1079         * @c class types.
1080         *
1081         * You must only call this method on two Object instances which are
1082         * representing the same data type, for example if both Objects reflect
1083         * instances of the same user defined C++ class. Calling this method on
1084         * completely different data types does not cause an error or exception, but
1085         * its result would simply be useless for any purpose.
1086         *
1087         * @see Archive::setVersion() for more details about this overall topic.
1088         */
1089      bool Object::isVersionCompatibleTo(const Object& other) const {      bool Object::isVersionCompatibleTo(const Object& other) const {
1090          if (this->version() == other.version())          if (this->version() == other.version())
1091              return true;              return true;
# Line 230  namespace Serialization { Line 1095  namespace Serialization {
1095              return other.minVersion() <= this->version();              return other.minVersion() <= this->version();
1096      }      }
1097    
1098        void Object::setVersion(Version v) {
1099            m_version = v;
1100        }
1101    
1102        void Object::setMinVersion(Version v) {
1103            m_minVersion = v;
1104        }
1105    
1106        /** @brief Get the member of this Object with given name.
1107         *
1108         * In case this Object is reflecting a native C/C++ @c struct or @c class
1109         * type, then this method returns the abstract reflection of the requested
1110         * member variable of the original native C/C++ @c struct or @c class
1111         * instance. For primitive, fundamental C/C++ data types this method always
1112         * returns an "invalid" Member instance instead.
1113         *
1114         * Example:
1115         * @code
1116         * struct Foo {
1117         *     int  a;
1118         *     bool b;
1119         *     double someValue;
1120         * };
1121         * @endcode
1122         * Consider that you serialized the native C/C++ @c struct as shown in this
1123         * example, and assuming that you implemented the respective serialize()
1124         * method of this C++ @c struct to serialize all its members, then you might
1125         * call memberNamed("someValue") to get the details of the third member in
1126         * this example for instance. In case the passed @a name is an unknown
1127         * member name, then this method will return an "invalid" Member object
1128         * instead.
1129         *
1130         * @param name - original name of the sought serialized member variable of
1131         *               this Object reflection
1132         * @returns abstract reflection of the sought member variable
1133         * @see Member::isValid(), Object::members()
1134         */
1135      Member Object::memberNamed(String name) const {      Member Object::memberNamed(String name) const {
1136          for (int i = 0; i < m_members.size(); ++i)          for (int i = 0; i < m_members.size(); ++i)
1137              if (m_members[i].name() == name)              if (m_members[i].name() == name)
# Line 237  namespace Serialization { Line 1139  namespace Serialization {
1139          return Member();          return Member();
1140      }      }
1141    
1142        /** @brief Get the member of this Object with given unique identifier.
1143         *
1144         * This method behaves similar like method memberNamed() described above,
1145         * but instead of searching for a member variable by name, it searches for
1146         * a member with an abstract unique identifier instead. For primitive,
1147         * fundamental C/C++ data types, for invalid or unknown unique identifiers,
1148         * and for members which are actually not member instances of the original
1149         * C/C++ @c struct or @c class instance this Object is reflecting, this
1150         * method returns an "invalid" Member instance instead.
1151         *
1152         * @param uid - unique identifier of the member variable being sought
1153         * @returns abstract reflection of the sought member variable
1154         * @see Member::isValid(), Object::members(), Object::memberNamed()
1155         */
1156        Member Object::memberByUID(const UID& uid) const {
1157            if (!uid) return Member();
1158            for (int i = 0; i < m_members.size(); ++i)
1159                if (m_members[i].uid() == uid)
1160                    return m_members[i];
1161            return Member();
1162        }
1163    
1164      void Object::remove(const Member& member) {      void Object::remove(const Member& member) {
1165          for (int i = 0; i < m_members.size(); ++i) {          for (int i = 0; i < m_members.size(); ++i) {
1166              if (m_members[i] == member) {              if (m_members[i] == member) {
# Line 246  namespace Serialization { Line 1170  namespace Serialization {
1170          }          }
1171      }      }
1172    
1173        /** @brief Get all members of this Object with given data type.
1174         *
1175         * In case this Object is reflecting a native C/C++ @c struct or @c class
1176         * type, then this method returns all member variables of that original
1177         * native C/C++ @c struct or @c class instance which are matching the given
1178         * requested data @a type. If this Object is reflecting a primitive,
1179         * fundamental data type, or if there are no members of this Object with the
1180         * requested precise C/C++ data type, then this method returns an empty
1181         * vector instead.
1182         *
1183         * @param type - the precise C/C++ data type of the sought member variables
1184         *               of this Object
1185         * @returns vector with abstract reflections of the sought member variables
1186         * @see Object::members(), Object::memberNamed()
1187         */
1188      std::vector<Member> Object::membersOfType(const DataType& type) const {      std::vector<Member> Object::membersOfType(const DataType& type) const {
1189          std::vector<Member> v;          std::vector<Member> v;
1190          for (int i = 0; i < m_members.size(); ++i) {          for (int i = 0; i < m_members.size(); ++i) {
# Line 256  namespace Serialization { Line 1195  namespace Serialization {
1195          return v;          return v;
1196      }      }
1197    
1198        /** @brief Serialization/deserialization sequence number of the requested member.
1199         *
1200         * Returns the precise serialization/deserialization sequence number of the
1201         * requested @a member variable.
1202         *
1203         * Example:
1204         * @code
1205         * struct Foo {
1206         *     int  a;
1207         *     bool b;
1208         *     double c;
1209         *
1210         *     void serialize(Serialization::Archive* archive);
1211         * };
1212         * @endcode
1213         * Assuming the declaration of the user defined native C/C++ @c struct
1214         * @c Foo above, and assuming the following implementation of serialize():
1215         * @code
1216         * #define SRLZ(member) \
1217         *   archive->serializeMember(*this, member, #member);
1218         *
1219         * void Foo::serialize(Serialization::Archive* archive) {
1220         *     SRLZ(c);
1221         *     SRLZ(a);
1222         *     SRLZ(b);
1223         * }
1224         * @endcode
1225         * then @c sequenceIndexOf(obj.memberNamed("a")) returns 1,
1226         * @c sequenceIndexOf(obj.memberNamed("b")) returns 2, and
1227         * @c sequenceIndexOf(obj.memberNamed("c")) returns 0.
1228         */
1229      int Object::sequenceIndexOf(const Member& member) const {      int Object::sequenceIndexOf(const Member& member) const {
1230          for (int i = 0; i < m_members.size(); ++i)          for (int i = 0; i < m_members.size(); ++i)
1231              if (m_members[i] == member)              if (m_members[i] == member)
# Line 266  namespace Serialization { Line 1236  namespace Serialization {
1236      // *************** Archive ***************      // *************** Archive ***************
1237      // *      // *
1238    
1239        /** @brief Create an "empty" archive.
1240         *
1241         * This default constructor creates an "empty" archive which you then
1242         * subsequently for example might fill with serialized data like:
1243         * @code
1244         * Archive a;
1245         * a.serialize(&myRootObject);
1246         * @endcode
1247         * Or:
1248         * @code
1249         * Archive a;
1250         * a << myRootObject;
1251         * @endcode
1252         * Or you might also subsequently assign an already existing non-empty
1253         * to this empty archive, which effectively clones the other
1254         * archive (deep copy) or call decode() later on to assign a previously
1255         * serialized raw data stream.
1256         */
1257      Archive::Archive() {      Archive::Archive() {
1258          m_operation = OPERATION_NONE;          m_operation = OPERATION_NONE;
1259          m_root = NO_UID;          m_root = NO_UID;
1260            m_isModified = false;
1261            m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1262      }      }
1263    
1264        /** @brief Create and fill the archive with the given serialized raw data.
1265         *
1266         * This constructor decodes the given raw @a data and constructs a
1267         * (non-empty) Archive object according to that given serialized data
1268         * stream.
1269         *
1270         * After this constructor returned, you may then traverse the individual
1271         * objects by starting with accessing the rootObject() for example. Finally
1272         * you might call deserialize() to restore your native C++ objects with the
1273         * content of this archive.
1274         *
1275         * @param data - the previously serialized raw data stream to be decoded
1276         * @throws Exception if the provided raw @a data uses an invalid, unknown,
1277         *         incompatible or corrupt data stream or format.
1278         */
1279      Archive::Archive(const RawData& data) {      Archive::Archive(const RawData& data) {
1280          m_operation = OPERATION_NONE;          m_operation = OPERATION_NONE;
1281          m_root = NO_UID;          m_root = NO_UID;
1282          decode(m_rawData);          m_isModified = false;
1283      }          m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1284            decode(data);
1285        }
1286    
1287        /** @brief Create and fill the archive with the given serialized raw C-buffer data.
1288         *
1289         * This constructor essentially works like the constructor above, but just
1290         * uses another data type for the serialized raw data stream being passed to
1291         * this class.
1292         *
1293         * This constructor decodes the given raw @a data and constructs a
1294         * (non-empty) Archive object according to that given serialized data
1295         * stream.
1296         *
1297         * After this constructor returned, you may then traverse the individual
1298         * objects by starting with accessing the rootObject() for example. Finally
1299         * you might call deserialize() to restore your native C++ objects with the
1300         * content of this archive.
1301         *
1302         * @param data - the previously serialized raw data stream to be decoded
1303         * @param size - size of @a data in bytes
1304         * @throws Exception if the provided raw @a data uses an invalid, unknown,
1305         *         incompatible or corrupt data stream or format.
1306         */
1307      Archive::Archive(const uint8_t* data, size_t size) {      Archive::Archive(const uint8_t* data, size_t size) {
1308          m_operation = OPERATION_NONE;          m_operation = OPERATION_NONE;
1309          m_root = NO_UID;          m_root = NO_UID;
1310            m_isModified = false;
1311            m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1312          decode(data, size);          decode(data, size);
1313      }      }
1314    
1315      Archive::~Archive() {      Archive::~Archive() {
1316      }      }
1317    
1318        /** @brief Root C++ object of this archive.
1319         *
1320         * In case this is a non-empty Archive, then this method returns the so
1321         * called "root" C++ object. If this is an empty archive, then this method
1322         * returns an "invalid" Object instance instead.
1323         *
1324         * @see Archive::serialize() for more details about the "root" object concept.
1325         * @see Object for more details about the overall object reflection concept.
1326         * @returns reflection of the original native C++ root object
1327         */
1328      Object& Archive::rootObject() {      Object& Archive::rootObject() {
1329          return m_allObjects[m_root];          return m_allObjects[m_root];
1330      }      }
# Line 301  namespace Serialization { Line 1340  namespace Serialization {
1340          return _encodeBlob(s);          return _encodeBlob(s);
1341      }      }
1342    
1343        static String _encode(const time_t& time) {
1344            return _encodeBlob(ToString(time));
1345        }
1346    
1347      static String _encode(const DataType& type) {      static String _encode(const DataType& type) {
1348          String s;          String s;
1349          s += _encodeBlob(type.baseTypeName());          s += _encodeBlob(type.baseTypeName());
1350          s += _encodeBlob(type.customTypeName());          s += _encodeBlob(type.customTypeName());
1351            if (!type.customTypeName2().empty())
1352                s += _encodeBlob(type.customTypeName2());
1353          s += _encodeBlob(ToString(type.size()));          s += _encodeBlob(ToString(type.size()));
1354          s += _encodeBlob(ToString(type.isPointer()));          s += _encodeBlob(ToString(type.isPointer()));
1355          return _encodeBlob(s);          return _encodeBlob(s);
# Line 333  namespace Serialization { Line 1378  namespace Serialization {
1378          return _encodeBlob(s);          return _encodeBlob(s);
1379      }      }
1380    
1381      static String _encodePrimitiveValue(const Object& obj) {      static String _primitiveObjectValueToString(const Object& obj) {
1382          String s;          String s;
1383          const DataType& type = obj.type();          const DataType& type = obj.type();
1384          const ID& id = obj.uid().id;          const ID& id = obj.uid().id;
1385            void* ptr = obj.m_data.empty() ? (void*)id : (void*)&obj.m_data[0];
1386            if (!obj.m_data.empty())
1387                assert(type.size() == obj.m_data.size());
1388          if (type.isPrimitive() && !type.isPointer()) {          if (type.isPrimitive() && !type.isPointer()) {
1389              if (type.isInteger() || type.isEnum()) {              if (type.isInteger() || type.isEnum()) {
1390                  if (type.isSigned()) {                  if (type.isSigned()) {
1391                      if (type.size() == 1)                      if (type.size() == 1)
1392                          s = ToString((int16_t)*(int8_t*)id); // int16_t: prevent ToString() to render an ASCII character                          s = ToString((int16_t)*(int8_t*)ptr); // int16_t: prevent ToString() to render an ASCII character
1393                      else if (type.size() == 2)                      else if (type.size() == 2)
1394                          s = ToString(*(int16_t*)id);                          s = ToString(*(int16_t*)ptr);
1395                      else if (type.size() == 4)                      else if (type.size() == 4)
1396                          s = ToString(*(int32_t*)id);                          s = ToString(*(int32_t*)ptr);
1397                      else if (type.size() == 8)                      else if (type.size() == 8)
1398                          s = ToString(*(int64_t*)id);                          s = ToString(*(int64_t*)ptr);
1399                      else                      else
1400                          assert(false /* unknown signed int type size */);                          assert(false /* unknown signed int type size */);
1401                  } else {                  } else {
1402                      if (type.size() == 1)                      if (type.size() == 1)
1403                          s = ToString((uint16_t)*(uint8_t*)id); // uint16_t: prevent ToString() to render an ASCII character                          s = ToString((uint16_t)*(uint8_t*)ptr); // uint16_t: prevent ToString() to render an ASCII character
1404                      else if (type.size() == 2)                      else if (type.size() == 2)
1405                          s = ToString(*(uint16_t*)id);                          s = ToString(*(uint16_t*)ptr);
1406                      else if (type.size() == 4)                      else if (type.size() == 4)
1407                          s = ToString(*(uint32_t*)id);                          s = ToString(*(uint32_t*)ptr);
1408                      else if (type.size() == 8)                      else if (type.size() == 8)
1409                          s = ToString(*(uint64_t*)id);                          s = ToString(*(uint64_t*)ptr);
1410                      else                      else
1411                          assert(false /* unknown unsigned int type size */);                          assert(false /* unknown unsigned int type size */);
1412                  }                  }
1413              } else if (type.isReal()) {              } else if (type.isReal()) {
1414                  if (type.size() == sizeof(float))                  if (type.size() == sizeof(float))
1415                      s = ToString(*(float*)id);                      s = ToString(*(float*)ptr);
1416                  else if (type.size() == sizeof(double))                  else if (type.size() == sizeof(double))
1417                      s = ToString(*(double*)id);                      s = ToString(*(double*)ptr);
1418                  else                  else
1419                      assert(false /* unknown floating point type */);                      assert(false /* unknown floating point type */);
1420              } else if (type.isBool()) {              } else if (type.isBool()) {
1421                  s = ToString(*(bool*)id);                  s = ToString(*(bool*)ptr);
1422                } else if (type.isString()) {
1423                    s = obj.m_data.empty() ? *(String*)ptr : String((const char*)ptr);
1424              } else {              } else {
1425                  assert(false /* unknown primitive type */);                  assert(false /* unknown primitive type */);
1426              }              }
1427            }
1428            return s;
1429        }
1430    
1431        template<typename T>
1432        inline T _stringToNumber(const String& s) {
1433            assert(false /* String cast to unknown primitive number type */);
1434        }
1435    
1436        template<>
1437        inline int64_t _stringToNumber(const String& s) {
1438            return atoll(s.c_str());
1439        }
1440    
1441        template<>
1442        inline double _stringToNumber(const String& s) {
1443            return atof(s.c_str());
1444        }
1445    
1446        template<>
1447        inline bool _stringToNumber(const String& s) {
1448            return (bool) atoll(s.c_str());
1449        }
1450    
1451        template<typename T>
1452        static T _primitiveObjectValueToNumber(const Object& obj) {
1453            T value = 0;
1454            const DataType& type = obj.type();
1455            const ID& id = obj.uid().id;
1456            void* ptr = obj.m_data.empty() ? (void*)id : (void*)&obj.m_data[0];
1457            if (!obj.m_data.empty())
1458                assert(type.size() == obj.m_data.size());
1459            if (type.isPrimitive() && !type.isPointer()) {
1460                if (type.isInteger() || type.isEnum()) {
1461                    if (type.isSigned()) {
1462                        if (type.size() == 1)
1463                            value = (T)*(int8_t*)ptr;
1464                        else if (type.size() == 2)
1465                            value = (T)*(int16_t*)ptr;
1466                        else if (type.size() == 4)
1467                            value = (T)*(int32_t*)ptr;
1468                        else if (type.size() == 8)
1469                            value = (T)*(int64_t*)ptr;
1470                        else
1471                            assert(false /* unknown signed int type size */);
1472                    } else {
1473                        if (type.size() == 1)
1474                            value = (T)*(uint8_t*)ptr;
1475                        else if (type.size() == 2)
1476                            value = (T)*(uint16_t*)ptr;
1477                        else if (type.size() == 4)
1478                            value = (T)*(uint32_t*)ptr;
1479                        else if (type.size() == 8)
1480                            value = (T)*(uint64_t*)ptr;
1481                        else
1482                            assert(false /* unknown unsigned int type size */);
1483                    }
1484                } else if (type.isReal()) {
1485                    if (type.size() == sizeof(float))
1486                        value = (T)*(float*)ptr;
1487                    else if (type.size() == sizeof(double))
1488                        value = (T)*(double*)ptr;
1489                    else
1490                        assert(false /* unknown floating point type */);
1491                } else if (type.isBool()) {
1492                    value = (T)*(bool*)ptr;
1493                } else if (type.isString()) {
1494                    value = _stringToNumber<T>(
1495                        obj.m_data.empty() ? *(String*)ptr : String((const char*)ptr)
1496                    );
1497                } else {
1498                    assert(false /* unknown primitive type */);
1499                }
1500          }          }
1501          return _encodeBlob(s);          return value;
1502        }
1503    
1504        static String _encodePrimitiveValue(const Object& obj) {
1505            return _encodeBlob( _primitiveObjectValueToString(obj) );
1506      }      }
1507    
1508      static String _encode(const Object& obj) {      static String _encode(const Object& obj) {
# Line 401  namespace Serialization { Line 1527  namespace Serialization {
1527          return _encodeBlob(s);          return _encodeBlob(s);
1528      }      }
1529    
1530        /*
1531         * Srx format history:
1532         * - 1.0: Initial version.
1533         * - 1.1: Adds "String", "Array", "Set" and "Map" data types and an optional
1534         *        2nd custom type name (e.g. "Map" types which always contain two
1535         *        user defined types).
1536         */
1537      #define MAGIC_START "Srx1v"      #define MAGIC_START "Srx1v"
1538      #define ENCODING_FORMAT_MINOR_VERSION 0      #define ENCODING_FORMAT_MINOR_VERSION 1
1539    
1540      String Archive::_encodeRootBlob() {      String Archive::_encodeRootBlob() {
1541          String s;          String s;
1542          s += _encodeBlob(ToString(ENCODING_FORMAT_MINOR_VERSION));          s += _encodeBlob(ToString(ENCODING_FORMAT_MINOR_VERSION));
1543          s += _encode(m_root);          s += _encode(m_root);
1544          s += _encode(m_allObjects);          s += _encode(m_allObjects);
1545            s += _encodeBlob(m_name);
1546            s += _encodeBlob(m_comment);
1547            s += _encode(m_timeCreated);
1548            s += _encode(m_timeModified);
1549          return _encodeBlob(s);          return _encodeBlob(s);
1550      }      }
1551    
1552      void Archive::encode() {      void Archive::encode() {
1553          m_rawData.clear();          m_rawData.clear();
1554          String s = MAGIC_START;          String s = MAGIC_START;
1555            m_timeModified = time(NULL);
1556            if (m_timeCreated == LIBGIG_EPOCH_TIME)
1557                m_timeCreated = m_timeModified;
1558          s += _encodeRootBlob();          s += _encodeRootBlob();
1559          m_rawData.resize(s.length() + 1);          m_rawData.resize(s.length() + 1);
1560          memcpy(&m_rawData[0], &s[0], s.length() + 1);          memcpy(&m_rawData[0], &s[0], s.length() + 1);
1561            m_isModified = false;
1562      }      }
1563    
1564      struct _Blob {      struct _Blob {
# Line 426  namespace Serialization { Line 1567  namespace Serialization {
1567      };      };
1568    
1569      static _Blob _decodeBlob(const char* p, const char* end, bool bThrow = true) {      static _Blob _decodeBlob(const char* p, const char* end, bool bThrow = true) {
1570          if (!bThrow && p >= end)          if (!bThrow && p >= end) {
1571              return (_Blob) { p, end };              const _Blob blob =  { p, end };
1572                return blob;
1573            }
1574          size_t sz = 0;          size_t sz = 0;
1575          for (; true; ++p) {          for (; true; ++p) {
1576              if (p >= end)              if (p >= end)
# Line 442  namespace Serialization { Line 1585  namespace Serialization {
1585          ++p;          ++p;
1586          if (p + sz > end)          if (p + sz > end)
1587              throw Exception("Decode Error: Premature end of blob");              throw Exception("Decode Error: Premature end of blob");
1588          return (_Blob) { p, p + sz };          const _Blob blob = { p, p + sz };
1589            return blob;
1590      }      }
1591    
1592      template<typename T_int>      template<typename T_int>
# Line 487  namespace Serialization { Line 1631  namespace Serialization {
1631          String s(p, size_t(end - p));          String s(p, size_t(end - p));
1632    
1633          T_real r;          T_real r;
1634          if (sizeof(T_real) == sizeof(float))          if (sizeof(T_real) <= sizeof(double))
1635              r = atof(s.c_str());              r = atof(s.c_str());
         else if (sizeof(T_real) == sizeof(double))  
             r = atof_l(s.c_str(), _c_locale);  
1636          else          else
1637              assert(false /* unknown real type */);              assert(false /* unknown real type */);
1638    
# Line 519  namespace Serialization { Line 1661  namespace Serialization {
1661          return s;          return s;
1662      }      }
1663    
1664      DataType _popDataTypeBlob(const char*& p, const char* end) {      static void _popStringBlob(const char*& p, const char* end, RawData& rawData) {
1665            String s = _popStringBlob(p, end);
1666            rawData.resize(s.length() + 1);
1667            strcpy((char*)&rawData[0], &s[0]);
1668        }
1669    
1670        static time_t _popTimeBlob(const char*& p, const char* end) {
1671            const uint64_t i = _popIntBlob<uint64_t>(p, end);
1672            return (time_t) i;
1673        }
1674    
1675        static DataType _popDataTypeBlob(const char*& p, const char* end) {
1676          _Blob blob = _decodeBlob(p, end);          _Blob blob = _decodeBlob(p, end);
1677          p   = blob.p;          p   = blob.p;
1678          end = blob.end;          end = blob.end;
# Line 527  namespace Serialization { Line 1680  namespace Serialization {
1680          DataType type;          DataType type;
1681          type.m_baseTypeName   = _popStringBlob(p, end);          type.m_baseTypeName   = _popStringBlob(p, end);
1682          type.m_customTypeName = _popStringBlob(p, end);          type.m_customTypeName = _popStringBlob(p, end);
1683            if (type.isMap())
1684                type.m_customTypeName2 = _popStringBlob(p, end);
1685          type.m_size           = _popIntBlob<int>(p, end);          type.m_size           = _popIntBlob<int>(p, end);
1686          type.m_isPointer      = _popIntBlob<bool>(p, end);          type.m_isPointer      = _popIntBlob<bool>(p, end);
1687          return type;          return type;
# Line 543  namespace Serialization { Line 1698  namespace Serialization {
1698          const ID id = (ID) _popIntBlob<size_t>(p, end);          const ID id = (ID) _popIntBlob<size_t>(p, end);
1699          const size_t size = _popIntBlob<size_t>(p, end);          const size_t size = _popIntBlob<size_t>(p, end);
1700    
1701          return (UID) { id, size };          const UID uid = { id, size };
1702            return uid;
1703      }      }
1704    
1705      static UIDChain _popUIDChainBlob(const char*& p, const char* end) {      static UIDChain _popUIDChainBlob(const char*& p, const char* end) {
# Line 560  namespace Serialization { Line 1716  namespace Serialization {
1716          return chain;          return chain;
1717      }      }
1718    
1719      Member _popMemberBlob(const char*& p, const char* end) {      static Member _popMemberBlob(const char*& p, const char* end) {
1720          _Blob blob = _decodeBlob(p, end, false);          _Blob blob = _decodeBlob(p, end, false);
1721          p   = blob.p;          p   = blob.p;
1722          end = blob.end;          end = blob.end;
# Line 569  namespace Serialization { Line 1725  namespace Serialization {
1725          if (p >= end) return m;          if (p >= end) return m;
1726    
1727          m.m_uid    = _popUIDBlob(p, end);          m.m_uid    = _popUIDBlob(p, end);
1728          m.m_offset = _popIntBlob<size_t>(p, end);          m.m_offset = _popIntBlob<ssize_t>(p, end);
1729          m.m_name   = _popStringBlob(p, end);          m.m_name   = _popStringBlob(p, end);
1730          m.m_type   = _popDataTypeBlob(p, end);          m.m_type   = _popDataTypeBlob(p, end);
1731          assert(m.type());          assert(m.type());
1732          assert(!m.name().empty());          assert(!m.name().empty());
1733          assert(m.uid() != NULL);          assert(m.uid().isValid());
1734          return m;          return m;
1735      }      }
1736    
# Line 594  namespace Serialization { Line 1750  namespace Serialization {
1750          return members;          return members;
1751      }      }
1752    
1753      void _popPrimitiveValue(const char*& p, const char* end, Object& obj) {      static void _popPrimitiveValue(const char*& p, const char* end, Object& obj) {
1754          const DataType& type = obj.type();          const DataType& type = obj.type();
1755          if (type.isPrimitive() && !type.isPointer()) {          if (type.isPrimitive() && !type.isPointer()) {
1756              obj.m_data.resize(type.size());              obj.m_data.resize(type.size());
# Line 631  namespace Serialization { Line 1787  namespace Serialization {
1787                      assert(false /* unknown floating point type */);                      assert(false /* unknown floating point type */);
1788              } else if (type.isBool()) {              } else if (type.isBool()) {
1789                  _popIntBlob<uint8_t>(p, end, obj.m_data);                  _popIntBlob<uint8_t>(p, end, obj.m_data);
1790                } else if (type.isString()) {
1791                    _popStringBlob(p, end, obj.m_data);
1792              } else {              } else {
1793                  assert(false /* unknown primitive type */);                  assert(false /* unknown primitive type */);
1794              }              }
# Line 643  namespace Serialization { Line 1801  namespace Serialization {
1801          }          }
1802      }      }
1803    
1804      Object _popObjectBlob(const char*& p, const char* end) {      static Object _popObjectBlob(const char*& p, const char* end) {
1805          _Blob blob = _decodeBlob(p, end, false);          _Blob blob = _decodeBlob(p, end, false);
1806          p   = blob.p;          p   = blob.p;
1807          end = blob.end;          end = blob.end;
# Line 695  namespace Serialization { Line 1853  namespace Serialization {
1853          _popObjectsBlob(p, end);          _popObjectsBlob(p, end);
1854          if (!m_allObjects[m_root])          if (!m_allObjects[m_root])
1855              throw Exception("Decode Error: Missing declared root object");              throw Exception("Decode Error: Missing declared root object");
     }  
1856    
1857            m_name = _popStringBlob(p, end);
1858            m_comment = _popStringBlob(p, end);
1859            m_timeCreated = _popTimeBlob(p, end);
1860            m_timeModified = _popTimeBlob(p, end);
1861        }
1862    
1863        /** @brief Fill this archive with the given serialized raw data.
1864         *
1865         * Calling this method will decode the given raw @a data and constructs a
1866         * (non-empty) Archive object according to that given serialized @a data
1867         * stream.
1868         *
1869         * After this method returned, you may then traverse the individual
1870         * objects by starting with accessing the rootObject() for example. Finally
1871         * you might call deserialize() to restore your native C++ objects with the
1872         * content of this archive.
1873         *
1874         * @param data - the previously serialized raw data stream to be decoded
1875         * @throws Exception if the provided raw @a data uses an invalid, unknown,
1876         *         incompatible or corrupt data stream or format.
1877         */
1878      void Archive::decode(const RawData& data) {      void Archive::decode(const RawData& data) {
1879          m_rawData = data;          m_rawData = data;
1880          m_allObjects.clear();          m_allObjects.clear();
1881            m_isModified = false;
1882            m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1883          const char* p   = (const char*) &data[0];          const char* p   = (const char*) &data[0];
1884          const char* end = p + data.size();          const char* end = p + data.size();
1885          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 708  namespace Serialization { Line 1888  namespace Serialization {
1888          _popRootBlob(p, end);          _popRootBlob(p, end);
1889      }      }
1890    
1891        /** @brief Fill this archive with the given serialized raw C-buffer data.
1892         *
1893         * This method essentially works like the decode() method above, but just
1894         * uses another data type for the serialized raw data stream being passed to
1895         * this method.
1896         *
1897         * Calling this method will decode the given raw @a data and constructs a
1898         * (non-empty) Archive object according to that given serialized @a data
1899         * stream.
1900         *
1901         * After this method returned, you may then traverse the individual
1902         * objects by starting with accessing the rootObject() for example. Finally
1903         * you might call deserialize() to restore your native C++ objects with the
1904         * content of this archive.
1905         *
1906         * @param data - the previously serialized raw data stream to be decoded
1907         * @param size - size of @a data in bytes
1908         * @throws Exception if the provided raw @a data uses an invalid, unknown,
1909         *         incompatible or corrupt data stream or format.
1910         */
1911      void Archive::decode(const uint8_t* data, size_t size) {      void Archive::decode(const uint8_t* data, size_t size) {
1912          RawData rawData;          RawData rawData;
1913          rawData.resize(size);          rawData.resize(size);
# Line 715  namespace Serialization { Line 1915  namespace Serialization {
1915          decode(rawData);          decode(rawData);
1916      }      }
1917    
1918        /** @brief Raw data stream of this archive content.
1919         *
1920         * Call this method to get a raw data stream for the current content of this
1921         * archive, which you may use to i.e. store on disk or send vie network to
1922         * another machine for deserializing there. This method only returns a
1923         * meaningful content if this is a non-empty archive, that is if you either
1924         * serialized with this Archive object or decoded a raw data stream to this
1925         * Archive object before. If this is an empty archive instead, then this
1926         * method simply returns an empty raw data stream (of size 0) instead.
1927         *
1928         * Note that whenever you call this method, the "modified" state of this
1929         * archive will be reset to @c false.
1930         *
1931         * @see isModified()
1932         */
1933        const RawData& Archive::rawData() {
1934            if (m_isModified) encode();
1935            return m_rawData;
1936        }
1937    
1938        /** @brief Name of the encoding format used by this Archive class.
1939         *
1940         * This method returns the name of the encoding format used to encode
1941         * serialized raw data streams.
1942         */
1943      String Archive::rawDataFormat() const {      String Archive::rawDataFormat() const {
1944          return MAGIC_START;          return MAGIC_START;
1945      }      }
1946    
1947        /** @brief Whether this archive was modified.
1948         *
1949         * This method returns the current "modified" state of this archive. When
1950         * either decoding a previously serialized raw data stream or after
1951         * serializing native C++ objects to this archive the modified state will
1952         * initially be set to @c false. However whenever you are modifying the
1953         * abstract data model of this archive afterwards, for example by removing
1954         * objects from this archive by calling remove() or removeMember(), or by
1955         * altering object values for example by calling setIntValue(), then the
1956         * "modified" state of this archive will automatically be set to @c true.
1957         *
1958         * You can reset the "modified" state explicitly at any time, by calling
1959         * rawData().
1960         */
1961        bool Archive::isModified() const {
1962            return m_isModified;
1963        }
1964    
1965        /** @brief Clear content of this archive.
1966         *
1967         * Drops the entire content of this archive and thus resets this archive
1968         * back to become an empty archive.
1969         */
1970      void Archive::clear() {      void Archive::clear() {
1971          m_allObjects.clear();          m_allObjects.clear();
1972          m_operation = OPERATION_NONE;          m_operation = OPERATION_NONE;
1973          m_root = NO_UID;          m_root = NO_UID;
1974          m_rawData.clear();          m_rawData.clear();
1975            m_isModified = false;
1976            m_timeCreated = m_timeModified = LIBGIG_EPOCH_TIME;
1977        }
1978    
1979        /** @brief Optional name of this archive.
1980         *
1981         * Returns the optional name of this archive that you might have assigned
1982         * to this archive before by calling setName(). If you haven't assigned any
1983         * name to this archive before, then this method simply returns an empty
1984         * string instead.
1985         */
1986        String Archive::name() const {
1987            return m_name;
1988        }
1989    
1990        /** @brief Assign a name to this archive.
1991         *
1992         * You may optionally assign an arbitrary name to this archive. The name
1993         * will be stored along with the archive, that is it will encoded with the
1994         * resulting raw data stream, and accordingly it will be decoded from the
1995         * raw data stream later on.
1996         *
1997         * @param name - arbitrary new name for this archive
1998         */
1999        void Archive::setName(String name) {
2000            if (m_name == name) return;
2001            m_name = name;
2002            m_isModified = true;
2003      }      }
2004    
2005        /** @brief Optional comments for this archive.
2006         *
2007         * Returns the optional comments for this archive that you might have
2008         * assigned to this archive before by calling setComment(). If you haven't
2009         * assigned any comment to this archive before, then this method simply
2010         * returns an empty string instead.
2011         */
2012        String Archive::comment() const {
2013            return m_comment;
2014        }
2015    
2016        /** @brief Assign a comment to this archive.
2017         *
2018         * You may optionally assign arbitrary comments to this archive. The comment
2019         * will be stored along with the archive, that is it will encoded with the
2020         * resulting raw data stream, and accordingly it will be decoded from the
2021         * raw data stream later on.
2022         *
2023         * @param comment - arbitrary new comment for this archive
2024         */
2025        void Archive::setComment(String comment) {
2026            if (m_comment == comment) return;
2027            m_comment = comment;
2028            m_isModified = true;
2029        }
2030    
2031        static tm _convertTimeStamp(const time_t& time, time_base_t base) {
2032            tm* pTm;
2033            switch (base) {
2034                case LOCAL_TIME:
2035                    pTm = localtime(&time);
2036                    break;
2037                case UTC_TIME:
2038                    pTm = gmtime(&time);
2039                    break;
2040                default:
2041                    throw Exception("Time stamp with unknown time base (" + ToString((int64_t)base) + ") requested");
2042            }
2043            if (!pTm)
2044                throw Exception("Failed assembling time stamp structure");
2045            return *pTm;
2046        }
2047    
2048        /** @brief Date and time when this archive was initially created.
2049         *
2050         * Returns a UTC time stamp (date and time) when this archive was initially
2051         * created.
2052         */
2053        time_t Archive::timeStampCreated() const {
2054            return m_timeCreated;
2055        }
2056    
2057        /** @brief Date and time when this archive was modified for the last time.
2058         *
2059         * Returns a UTC time stamp (date and time) when this archive was modified
2060         * for the last time.
2061         */
2062        time_t Archive::timeStampModified() const {
2063            return m_timeModified;
2064        }
2065    
2066        /** @brief Date and time when this archive was initially created.
2067         *
2068         * Returns a calendar time information representing the date and time when
2069         * this archive was initially created. The optional @a base parameter may
2070         * be used to define to which time zone the returned data and time shall be
2071         * related to.
2072         *
2073         * @param base - (optional) time zone the result shall relate to, by default
2074         *               UTC time (Greenwhich Mean Time) is assumed instead
2075         */
2076        tm Archive::dateTimeCreated(time_base_t base) const {
2077            return _convertTimeStamp(m_timeCreated, base);
2078        }
2079    
2080        /** @brief Date and time when this archive was modified for the last time.
2081         *
2082         * Returns a calendar time information representing the date and time when
2083         * this archive has been modified for the last time. The optional @a base
2084         * parameter may be used to define to which time zone the returned date and
2085         * time shall be related to.
2086         *
2087         * @param base - (optional) time zone the result shall relate to, by default
2088         *               UTC time (Greenwhich Mean Time) is assumed instead
2089         */
2090        tm Archive::dateTimeModified(time_base_t base) const {
2091            return _convertTimeStamp(m_timeModified, base);
2092        }
2093    
2094        /** @brief Remove a member variable from the given object.
2095         *
2096         * Removes the member variable @a member from its containing object
2097         * @a parent and sets the modified state of this archive to @c true.
2098         * If the given @a parent object does not contain the given @a member then
2099         * this method does nothing.
2100         *
2101         * This method provides a means of "partial" deserialization. By removing
2102         * either objects or members from this archive before calling deserialize(),
2103         * only the remaining objects and remaining members will be restored by this
2104         * framework, all other data of your C++ classes remain untouched.
2105         *
2106         * @param parent - Object which contains @a member
2107         * @param member - member to be removed
2108         * @see isModified() for details about the modified state.
2109         * @see Object for more details about the overall object reflection concept.
2110         */
2111        void Archive::removeMember(Object& parent, const Member& member) {
2112            parent.remove(member);
2113            m_isModified = true;
2114        }
2115    
2116        /** @brief Remove an object from this archive.
2117         *
2118         * Removes the object @obj from this archive and sets the modified state of
2119         * this archive to @c true. If the passed object is either invalid, or does
2120         * not exist in this archive, then this method does nothing.
2121         *
2122         * This method provides a means of "partial" deserialization. By removing
2123         * either objects or members from this archive before calling deserialize(),
2124         * only the remaining objects and remaining members will be restored by this
2125         * framework, all other data of your C++ classes remain untouched.
2126         *
2127         * @param obj - the object to be removed from this archive
2128         * @see isModified() for details about the modified state.
2129         * @see Object for more details about the overall object reflection concept.
2130         */
2131      void Archive::remove(const Object& obj) {      void Archive::remove(const Object& obj) {
2132            //FIXME: Should traverse from root object and remove all members associated with this object
2133          if (!obj.uid()) return;          if (!obj.uid()) return;
2134          m_allObjects.erase(obj.uid());          m_allObjects.erase(obj.uid());
2135            m_isModified = true;
2136      }      }
2137    
2138        /** @brief Access object by its unique identifier.
2139         *
2140         * Returns the object of this archive with the given unique identifier
2141         * @a uid. If the given @a uid is invalid, or if this archive does not
2142         * contain an object with the given unique identifier, then this method
2143         * returns an invalid object instead.
2144         *
2145         * @param uid - unique identifier of sought object
2146         * @see Object for more details about the overall object reflection concept.
2147         * @see Object::isValid() for valid/invalid objects
2148         */
2149      Object& Archive::objectByUID(const UID& uid) {      Object& Archive::objectByUID(const UID& uid) {
2150          return m_allObjects[uid];          return m_allObjects[uid];
2151      }      }
2152    
2153        /** @brief Set the current version for the given object.
2154         *
2155         * Essentially behaves like above's setVersion() method, it just uses the
2156         * abstract reflection data type instead for the respective @a object being
2157         * passed to this method. Refer to above's setVersion() documentation about
2158         * the precise behavior details of setVersion().
2159         *
2160         * @param object - object to set the current version for
2161         * @param v - new current version to set for @a object
2162         */
2163        void Archive::setVersion(Object& object, Version v) {
2164            if (!object) return;
2165            object.setVersion(v);
2166            m_isModified = true;
2167        }
2168    
2169        /** @brief Set the minimum version for the given object.
2170         *
2171         * Essentially behaves like above's setMinVersion() method, it just uses the
2172         * abstract reflection data type instead for the respective @a object being
2173         * passed to this method. Refer to above's setMinVersion() documentation
2174         * about the precise behavior details of setMinVersion().
2175         *
2176         * @param object - object to set the minimum version for
2177         * @param v - new minimum version to set for @a object
2178         */
2179        void Archive::setMinVersion(Object& object, Version v) {
2180            if (!object) return;
2181            object.setMinVersion(v);
2182            m_isModified = true;
2183        }
2184    
2185        /** @brief Set new value for given @c enum object.
2186         *
2187         * Sets the new @a value to the given @c enum @a object.
2188         *
2189         * @param object - the @c enum object to be changed
2190         * @param value - the new value to be assigned to the @a object
2191         * @throws Exception if @a object is not an @c enum type.
2192         */
2193        void Archive::setEnumValue(Object& object, uint64_t value) {
2194            if (!object) return;
2195            if (!object.type().isEnum())
2196                throw Exception("Not an enum data type");
2197            Object* pObject = &object;
2198            if (object.type().isPointer()) {
2199                Object& obj = objectByUID(object.uid(1));
2200                if (!obj) return;
2201                pObject = &obj;
2202            }
2203            const int nativeEnumSize = sizeof(enum operation_t);
2204            DataType& type = const_cast<DataType&>( pObject->type() );
2205            // original serializer ("sender") might have had a different word size
2206            // than this machine, adjust type object in this case
2207            if (type.size() != nativeEnumSize) {
2208                type.m_size = nativeEnumSize;
2209            }
2210            pObject->m_data.resize(type.size());
2211            void* ptr = &pObject->m_data[0];
2212            if (type.size() == 1)
2213                *(uint8_t*)ptr = (uint8_t)value;
2214            else if (type.size() == 2)
2215                *(uint16_t*)ptr = (uint16_t)value;
2216            else if (type.size() == 4)
2217                *(uint32_t*)ptr = (uint32_t)value;
2218            else if (type.size() == 8)
2219                *(uint64_t*)ptr = (uint64_t)value;
2220            else
2221                assert(false /* unknown enum type size */);
2222            m_isModified = true;
2223        }
2224    
2225        /** @brief Set new integer value for given integer object.
2226         *
2227         * Sets the new integer @a value to the given integer @a object. Currently
2228         * this framework handles any integer data type up to 64 bit. For larger
2229         * integer types an assertion failure will be raised.
2230         *
2231         * @param object - the integer object to be changed
2232         * @param value - the new value to be assigned to the @a object
2233         * @throws Exception if @a object is not an integer type.
2234         */
2235        void Archive::setIntValue(Object& object, int64_t value) {
2236            if (!object) return;
2237            if (!object.type().isInteger())
2238                throw Exception("Not an integer data type");
2239            Object* pObject = &object;
2240            if (object.type().isPointer()) {
2241                Object& obj = objectByUID(object.uid(1));
2242                if (!obj) return;
2243                pObject = &obj;
2244            }
2245            const DataType& type = pObject->type();
2246            pObject->m_data.resize(type.size());
2247            void* ptr = &pObject->m_data[0];
2248            if (type.isSigned()) {
2249                if (type.size() == 1)
2250                    *(int8_t*)ptr = (int8_t)value;
2251                else if (type.size() == 2)
2252                    *(int16_t*)ptr = (int16_t)value;
2253                else if (type.size() == 4)
2254                    *(int32_t*)ptr = (int32_t)value;
2255                else if (type.size() == 8)
2256                    *(int64_t*)ptr = (int64_t)value;
2257                else
2258                    assert(false /* unknown signed int type size */);
2259            } else {
2260                if (type.size() == 1)
2261                    *(uint8_t*)ptr = (uint8_t)value;
2262                else if (type.size() == 2)
2263                    *(uint16_t*)ptr = (uint16_t)value;
2264                else if (type.size() == 4)
2265                    *(uint32_t*)ptr = (uint32_t)value;
2266                else if (type.size() == 8)
2267                    *(uint64_t*)ptr = (uint64_t)value;
2268                else
2269                    assert(false /* unknown unsigned int type size */);
2270            }
2271            m_isModified = true;
2272        }
2273    
2274        /** @brief Set new floating point value for given floating point object.
2275         *
2276         * Sets the new floating point @a value to the given floating point
2277         * @a object. Currently this framework supports single precision @c float
2278         * and double precision @c double floating point data types. For all other
2279         * floating point types this method will raise an assertion failure.
2280         *
2281         * @param object - the floating point object to be changed
2282         * @param value - the new value to be assigned to the @a object
2283         * @throws Exception if @a object is not a floating point based type.
2284         */
2285        void Archive::setRealValue(Object& object, double value) {
2286            if (!object) return;
2287            if (!object.type().isReal())
2288                throw Exception("Not a real data type");
2289            Object* pObject = &object;
2290            if (object.type().isPointer()) {
2291                Object& obj = objectByUID(object.uid(1));
2292                if (!obj) return;
2293                pObject = &obj;
2294            }
2295            const DataType& type = pObject->type();
2296            pObject->m_data.resize(type.size());
2297            void* ptr = &pObject->m_data[0];
2298            if (type.size() == sizeof(float))
2299                *(float*)ptr = (float)value;
2300            else if (type.size() == sizeof(double))
2301                *(double*)ptr = (double)value;
2302            else
2303                assert(false /* unknown real type size */);
2304            m_isModified = true;
2305        }
2306    
2307        /** @brief Set new boolean value for given boolean object.
2308         *
2309         * Sets the new boolean @a value to the given boolean @a object.
2310         *
2311         * @param object - the boolean object to be changed
2312         * @param value - the new value to be assigned to the @a object
2313         * @throws Exception if @a object is not a boolean type.
2314         */
2315        void Archive::setBoolValue(Object& object, bool value) {
2316            if (!object) return;
2317            if (!object.type().isBool())
2318                throw Exception("Not a bool data type");
2319            Object* pObject = &object;
2320            if (object.type().isPointer()) {
2321                Object& obj = objectByUID(object.uid(1));
2322                if (!obj) return;
2323                pObject = &obj;
2324            }
2325            const DataType& type = pObject->type();
2326            pObject->m_data.resize(type.size());
2327            bool* ptr = (bool*)&pObject->m_data[0];
2328            *ptr = value;
2329            m_isModified = true;
2330        }
2331    
2332        /** @brief Set new textual string for given String object.
2333         *
2334         * Sets the new textual string @a value to the given String @a object.
2335         *
2336         * @param object - the String object to be changed
2337         * @param value - the new textual string to be assigned to the @a object
2338         * @throws Exception if @a object is not a String type.
2339         */
2340        void Archive::setStringValue(Object& object, String value) {
2341            if (!object) return;
2342            if (!object.type().isString())
2343                throw Exception("Not a String data type");
2344            Object* pObject = &object;
2345            if (object.type().isPointer()) {
2346                Object& obj = objectByUID(object.uid(1));
2347                if (!obj) return;
2348                pObject = &obj;
2349            }
2350            pObject->m_data.resize(value.length() + 1);
2351            char* ptr = (char*) &pObject->m_data[0];
2352            strcpy(ptr, &value[0]);
2353            m_isModified = true;
2354        }
2355    
2356        /** @brief Automatically cast and assign appropriate value to object.
2357         *
2358         * This method automatically converts the given @a value from textual string
2359         * representation into the appropriate data format of the requested
2360         * @a object. So this method is a convenient way to change values of objects
2361         * in this archive with your applications in automated way, i.e. for
2362         * implementing an editor where the user is able to edit values of objects
2363         * in this archive by entering the values as text with a keyboard.
2364         *
2365         * @throws Exception if the passed @a object is not a fundamental, primitive
2366         *         data type or if the provided textual value cannot be converted
2367         *         into an appropriate value for the requested object.
2368         */
2369        void Archive::setAutoValue(Object& object, String value) {
2370            if (!object) return;
2371            const DataType& type = object.type();
2372            if (type.isInteger())
2373                setIntValue(object, atoll(value.c_str()));
2374            else if (type.isReal())
2375                setRealValue(object, atof(value.c_str()));
2376            else if (type.isBool()) {
2377                String val = toLowerCase(value);
2378                if (val == "true" || val == "yes" || val == "1")
2379                    setBoolValue(object, true);
2380                else if (val == "false" || val == "no" || val == "0")
2381                    setBoolValue(object, false);
2382                else
2383                    setBoolValue(object, atof(value.c_str()));
2384            } else if (type.isString())
2385                setStringValue(object, value);
2386            else if (type.isEnum())
2387                setEnumValue(object, atoll(value.c_str()));
2388            else
2389                throw Exception("Not a primitive data type");
2390        }
2391    
2392        /** @brief Get value of object as string.
2393         *
2394         * Converts the current value of the given @a object into a textual string
2395         * and returns that string.
2396         *
2397         * @param object - object whose value shall be retrieved
2398         * @throws Exception if the given object is either invalid, or if the object
2399         *         is not a fundamental, primitive data type.
2400         */
2401        String Archive::valueAsString(const Object& object) {
2402            if (!object)
2403                throw Exception("Invalid object");
2404            if (object.type().isClass())
2405                throw Exception("Object is class type");
2406            const Object* pObject = &object;
2407            if (object.type().isPointer()) {
2408                const Object& obj = objectByUID(object.uid(1));
2409                if (!obj) return "";
2410                pObject = &obj;
2411            }
2412            return _primitiveObjectValueToString(*pObject);
2413        }
2414    
2415        /** @brief Get integer value of object.
2416         *
2417         * Returns the current integer value of the requested integer @a object or
2418         * @c enum object.
2419         *
2420         * @param object - object whose value shall be retrieved
2421         * @throws Exception if the given object is either invalid, or if the object
2422         *         is neither an integer nor @c enum data type.
2423         */
2424        int64_t Archive::valueAsInt(const Object& object) {
2425            if (!object)
2426                throw Exception("Invalid object");
2427            if (!object.type().isInteger() && !object.type().isEnum())
2428                throw Exception("Object is neither an integer nor an enum");
2429            const Object* pObject = &object;
2430            if (object.type().isPointer()) {
2431                const Object& obj = objectByUID(object.uid(1));
2432                if (!obj) return 0;
2433                pObject = &obj;
2434            }
2435            return _primitiveObjectValueToNumber<int64_t>(*pObject);
2436        }
2437    
2438        /** @brief Get floating point value of object.
2439         *
2440         * Returns the current floating point value of the requested floating point
2441         * @a object.
2442         *
2443         * @param object - object whose value shall be retrieved
2444         * @throws Exception if the given object is either invalid, or if the object
2445         *         is not a floating point based type.
2446         */
2447        double Archive::valueAsReal(const Object& object) {
2448            if (!object)
2449                throw Exception("Invalid object");
2450            if (!object.type().isReal())
2451                throw Exception("Object is not an real type");
2452            const Object* pObject = &object;
2453            if (object.type().isPointer()) {
2454                const Object& obj = objectByUID(object.uid(1));
2455                if (!obj) return 0;
2456                pObject = &obj;
2457            }
2458            return _primitiveObjectValueToNumber<double>(*pObject);
2459        }
2460    
2461        /** @brief Get boolean value of object.
2462         *
2463         * Returns the current boolean value of the requested boolean @a object.
2464         *
2465         * @param object - object whose value shall be retrieved
2466         * @throws Exception if the given object is either invalid, or if the object
2467         *         is not a boolean data type.
2468         */
2469        bool Archive::valueAsBool(const Object& object) {
2470            if (!object)
2471                throw Exception("Invalid object");
2472            if (!object.type().isBool())
2473                throw Exception("Object is not a bool");
2474            const Object* pObject = &object;
2475            if (object.type().isPointer()) {
2476                const Object& obj = objectByUID(object.uid(1));
2477                if (!obj) return 0;
2478                pObject = &obj;
2479            }
2480            return _primitiveObjectValueToNumber<bool>(*pObject);
2481        }
2482    
2483        Archive::operation_t Archive::operation() const {
2484            return m_operation;
2485        }
2486    
2487      // *************** Archive::Syncer ***************      // *************** Archive::Syncer ***************
2488      // *      // *
2489    
# Line 756  namespace Serialization { Line 2505  namespace Serialization {
2505          memcpy(pDst, &srcObj.rawData()[0], dstObj.type().size());          memcpy(pDst, &srcObj.rawData()[0], dstObj.type().size());
2506      }      }
2507    
2508        void Archive::Syncer::syncString(const Object& dstObj, const Object& srcObj) {
2509            assert(dstObj.type().isString());
2510            assert(dstObj.type() == srcObj.type());
2511            String* pDst = (String*)(void*)dstObj.uid().id;
2512            *pDst = (String) (const char*) &srcObj.rawData()[0];
2513        }
2514    
2515        void Archive::Syncer::syncArray(const Object& dstObj, const Object& srcObj) {
2516            assert(dstObj.type().isArray());
2517            assert(dstObj.type() == srcObj.type());
2518            dstObj.m_sync(const_cast<Object&>(dstObj), srcObj, this);
2519        }
2520    
2521        void Archive::Syncer::syncSet(const Object& dstObj, const Object& srcObj) {
2522            assert(dstObj.type().isSet());
2523            assert(dstObj.type() == srcObj.type());
2524            dstObj.m_sync(const_cast<Object&>(dstObj), srcObj, this);
2525        }
2526    
2527        void Archive::Syncer::syncMap(const Object& dstObj, const Object& srcObj) {
2528            assert(dstObj.type().isMap());
2529            assert(dstObj.type() == srcObj.type());
2530            dstObj.m_sync(const_cast<Object&>(dstObj), srcObj, this);
2531        }
2532    
2533      void Archive::Syncer::syncPointer(const Object& dstObj, const Object& srcObj) {      void Archive::Syncer::syncPointer(const Object& dstObj, const Object& srcObj) {
2534          assert(dstObj.type().isPointer());          assert(dstObj.type().isPointer());
2535          assert(dstObj.type() == srcObj.type());          assert(dstObj.type() == srcObj.type());
# Line 782  namespace Serialization { Line 2556  namespace Serialization {
2556          m_dst.m_allObjects.erase(dstObj.uid());          m_dst.m_allObjects.erase(dstObj.uid());
2557    
2558          if (dstObj.type().isPrimitive() && !dstObj.type().isPointer()) {          if (dstObj.type().isPrimitive() && !dstObj.type().isPointer()) {
2559              syncPrimitive(dstObj, srcObj);              if (dstObj.type().isString())
2560                    syncString(dstObj, srcObj);
2561                else
2562                    syncPrimitive(dstObj, srcObj);
2563              return; // end of recursion              return; // end of recursion
2564          }          }
2565    
2566            if (dstObj.type().isArray()) {
2567                syncArray(dstObj, srcObj);
2568                return;
2569            }
2570    
2571            if (dstObj.type().isSet()) {
2572                syncSet(dstObj, srcObj);
2573                return;
2574            }
2575    
2576            if (dstObj.type().isMap()) {
2577                syncMap(dstObj, srcObj);
2578                return;
2579            }
2580    
2581          if (dstObj.type().isPointer()) {          if (dstObj.type().isPointer()) {
2582              syncPointer(dstObj, srcObj);              syncPointer(dstObj, srcObj);
2583              return;              return;
# Line 834  namespace Serialization { Line 2626  namespace Serialization {
2626      // *************** Exception ***************      // *************** Exception ***************
2627      // *      // *
2628    
2629        Exception::Exception() {
2630        }
2631    
2632        Exception::Exception(String format, ...) {
2633            va_list arg;
2634            va_start(arg, format);
2635            Message = assemble(format, arg);
2636            va_end(arg);
2637        }
2638    
2639        Exception::Exception(String format, va_list arg) {
2640            Message = assemble(format, arg);
2641        }
2642    
2643        /** @brief Print exception message to stdout.
2644         *
2645         * Prints the message of this Exception to the currently defined standard
2646         * output (that is to the terminal console for example).
2647         */
2648      void Exception::PrintMessage() {      void Exception::PrintMessage() {
2649          std::cout << "Serialization::Exception: " << Message << std::endl;          std::cout << "Serialization::Exception: " << Message << std::endl;
2650      }      }
2651    
2652        String Exception::assemble(String format, va_list arg) {
2653            char* buf = NULL;
2654            vasprintf(&buf, format.c_str(), arg);
2655            String s = buf;
2656            free(buf);
2657            return s;
2658        }
2659    
2660  } // namespace Serialization  } // namespace Serialization

Legend:
Removed from v.3138  
changed lines
  Added in v.3777

  ViewVC Help
Powered by ViewVC