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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3775 - (hide annotations) (download)
Tue May 19 15:23:11 2020 UTC (3 years, 11 months ago) by schoenebeck
File size: 98871 byte(s)
Serialization.cpp/.h: Added built-in support for C++ Array<> objects

* Introduced out of the box support for serialising / deserialising
  variables of C++ Array<> data types (a.k.a. std::vector from the STL).

* Member offsets are now signed and for (newly added support of) member
  variables on the heap -1 is always used as offset instead.

* Bumped version (4.2.0.svn13).

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

  ViewVC Help
Powered by ViewVC