From: hama Date: Fri, 27 Mar 2015 23:54:39 +0000 (+0100) Subject: implementation of ReSerializable X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=b1ba5376bdd531769edbcb078855c4df18d2a94b;p=crepublib implementation of ReSerializable --- diff --git a/base/ReByteBuffer.cpp b/base/ReByteBuffer.cpp index 045d37e..0e68028 100644 --- a/base/ReByteBuffer.cpp +++ b/base/ReByteBuffer.cpp @@ -216,8 +216,9 @@ ReByteBuffer& ReByteBuffer::appendFix(const char* data, size_t length, * @param maxLength the maximal count of appended chars * @return *this (chaining) */ -ReByteBuffer& ReByteBuffer::appendDump(const char* data, size_t length, int maxLength){ - if (length == size_t(-1)){ +ReByteBuffer& ReByteBuffer::appendDump(const char* data, size_t length, + int maxLength) { + if (length == size_t(-1)) { length = strlen(data); } if ((int) length > maxLength) @@ -225,14 +226,14 @@ ReByteBuffer& ReByteBuffer::appendDump(const char* data, size_t length, int maxL // test if text or binary: bool isBinary = false; unsigned char cc; - for (size_t ii = 0; ii < length; ii++){ - if ( (cc = (unsigned char) data[ii]) > 126 - || (cc < ' ' && cc != '\n' && cc != '\r' && cc != '\t')){ + for (size_t ii = 0; ii < length; ii++) { + if ((cc = (unsigned char) data[ii]) > 126 + || (cc < ' ' && cc != '\n' && cc != '\r' && cc != '\t')) { isBinary = true; break; } } - if (! isBinary) + if (!isBinary) append(data, length); else appendHexDump(data, min(length, maxLength / 5)).reduceLength(); @@ -688,7 +689,7 @@ int ReByteBuffer::indexOf(const Byte* toFind, size_t toFindLength, int start, * and the length is at least minLength bytes */ bool ReByteBuffer::isPrefixOf(const char* source, size_t length, - bool ignoreCase, int minLength){ + bool ignoreCase, int minLength) { if (length == (size_t) -1) length = strlen(source); bool rc = length >= m_length && m_length <= length; diff --git a/base/ReByteBuffer.hpp b/base/ReByteBuffer.hpp index 8f98f15..0a27f85 100644 --- a/base/ReByteBuffer.hpp +++ b/base/ReByteBuffer.hpp @@ -47,29 +47,29 @@ public: ReByteBuffer& append(const ReByteBuffer& source); ReByteBuffer& append(double, const char* format = "%f"); /** Appends a 8 bit value in an architecture independent way. - * @param value character to append + * @param value integer to append * @return *this (for chaining) */ - inline ReByteBuffer& appendBits8(int value){ + inline ReByteBuffer& appendBits8(int value) { setLength(m_length + 1); m_buffer[m_length - 1] = (char) value; return *this; } /** Appends a 16 bit value in an architecture independent way. - * @param value character to append + * @param value integer to append * @return *this (for chaining) */ - inline ReByteBuffer& appendBits16(int value){ + inline ReByteBuffer& appendBits16(int value) { setLength(m_length + 2); m_buffer[m_length - 2] = char(value >> 8); m_buffer[m_length - 1] = char(value); return *this; } /** Appends a 24 bit value in an architecture independent way. - * @param value character to append + * @param value integer to append * @return *this (for chaining) */ - inline ReByteBuffer& appendBits24(int value){ + inline ReByteBuffer& appendBits24(int value) { setLength(m_length + 3); m_buffer[m_length - 3] = char(value >> 16); m_buffer[m_length - 2] = char(value >> 8); @@ -77,10 +77,10 @@ public: return *this; } /** Appends a 24 bit value in an architecture independent way. - * @param value character to append + * @param value integer to append * @return *this (for chaining) */ - inline ReByteBuffer& appendBits32(int value){ + inline ReByteBuffer& appendBits32(int value) { setLength(m_length + 4); m_buffer[m_length - 4] = char(value >> 24); m_buffer[m_length - 3] = char(value >> 16); @@ -88,11 +88,11 @@ public: m_buffer[m_length - 1] = char(value); return *this; } - /** Appends a 24 bit value in an architecture independent way. - * @param value character to append + /** Appends a 64 bit value in an architecture independent way. + * @param value integer to append * @return *this (for chaining) */ - inline ReByteBuffer& appendBits64(int64_t value){ + inline ReByteBuffer& appendBits64(int64_t value) { setLength(m_length + 8); m_buffer[m_length - 8] = char(value >> 56); m_buffer[m_length - 7] = char(value >> 48); @@ -113,52 +113,6 @@ public: m_buffer[m_length - 1] = aChar; return *this; } - /** Appends 2 characters. - * @param char1 first character to append - * @param char2 2nd character to append - * @return *this (for chaining) - */ - inline ReByteBuffer& appendChar2(char char1, char char2) { - setLength(m_length + 2); - m_buffer[m_length - 2] = char1; - m_buffer[m_length - 1] = char2; - return *this; - } - /** Appends 4 characters. - * @param char1 first character to append - * @param char2 2nd character to append - * @param char3 3rd character to append - * @param char4 4th character to append - * @return *this (for chaining) - */ - inline ReByteBuffer& appendChar(char char1, char char2, char char3, char char4) { - setLength(m_length + 4); - m_buffer[m_length - 4] = char1; - m_buffer[m_length - 3] = char2; - m_buffer[m_length - 2] = char3; - m_buffer[m_length - 1] = char4; - return *this; - } - /** Appends 4 characters. - * @param char1 first character to append - * @param char2 2nd character to append - * @param char3 3rd character to append - * @param char4 4th character to append - * @return *this (for chaining) - */ - inline ReByteBuffer& appendChar(char char1, char char2, char char3, - char char4, char char5, char char6, char char7, char char8) { - setLength(m_length + 8); - m_buffer[m_length - 8] = char1; - m_buffer[m_length - 7] = char2; - m_buffer[m_length - 6] = char3; - m_buffer[m_length - 5] = char4; - m_buffer[m_length - 4] = char5; - m_buffer[m_length - 3] = char6; - m_buffer[m_length - 2] = char7; - m_buffer[m_length - 1] = char8; - return *this; - } /** Appends a character at least one time. * @param aChar character to append * @param count number of times to append @@ -174,7 +128,8 @@ public: } ReByteBuffer& appendFix(const char* data, size_t length, int maxLength, int minLength = 0, const char* separator = "*", char padding = ' '); - ReByteBuffer& appendDump(const char* data, size_t length = -1, int maxLength = 80); + ReByteBuffer& appendDump(const char* data, size_t length = -1, + int maxLength = 80); ReByteBuffer& appendHexDump(const char* data, size_t length = -1, int offset = 0, int bytePerLine = 16, const char* offsetFormat = "%04x: ", bool withAscii = true, int groupWidth = 1, int gapBehind = @@ -281,7 +236,8 @@ public: bool insert(size_t ix, const Byte* source, size_t length) { return splice(ix, 0, source, length); } - bool isPrefixOf(const char* source, size_t length = -1, bool ignoreCase = false, int minLength = 0); + bool isPrefixOf(const char* source, size_t length = -1, bool ignoreCase = + false, int minLength = 0); /** Returns the last character. * @return '\0': empty buffer
* otherwise: the last character diff --git a/base/ReException.cpp b/base/ReException.cpp index cfe1f93..26ecb76 100644 --- a/base/ReException.cpp +++ b/base/ReException.cpp @@ -40,8 +40,10 @@ ReException::ReException(const char* message, const char* file, int lineNo) : /** @brief Destructor. */ ReException::~ReException() { - if (m_message != NULL) + if (m_message != NULL) { free((void*) m_message); + m_message = NULL; + } } ReException::ReException(const ReException& source) : diff --git a/base/ReI18N.cpp b/base/ReI18N.cpp index 8361d46..861c21e 100644 --- a/base/ReI18N.cpp +++ b/base/ReI18N.cpp @@ -184,4 +184,13 @@ ReVarArgs& ReI18N::trF(const char* key, ReVarArgs& args) { args.reset(translate(key)); return args; } - +/** + * Translate a message. + * + * @param key the message to translate + * @return the translated message + */ +const char* i18nTranslate(const char* key) { + const char* rc = i18n(key); + return rc; +} diff --git a/base/ReI18N.hpp b/base/ReI18N.hpp index 9003ed0..85dcbed 100644 --- a/base/ReI18N.hpp +++ b/base/ReI18N.hpp @@ -40,6 +40,7 @@ protected: static int m_count; static ReVarArgs m_varArgs; }; +extern const char* i18nTranslate(const char* key); inline const char* i18n(const char* key) { return ReI18N::tr(key); } diff --git a/base/ReMutex.cpp b/base/ReMutex.cpp index e6427b2..8f8730c 100644 --- a/base/ReMutex.cpp +++ b/base/ReMutex.cpp @@ -53,7 +53,7 @@ bool ReMutex::timedLock(int sec) { #elif defined __WIN32__ int maxCount = sec * 50; int count = 0; - while(TryEnterCriticalSection(&m_mutex) != 0 && ++count < maxCount){ + while(TryEnterCriticalSection(&m_mutex) != 0 && ++count < maxCount) { Sleep(20); } rc = count < maxCount; diff --git a/base/ReSerializable.cpp b/base/ReSerializable.cpp index d08c6d8..763eb4b 100644 --- a/base/ReSerializable.cpp +++ b/base/ReSerializable.cpp @@ -12,11 +12,27 @@ /** * Constructor. * - * @param length the + * @param currentLength the current length of the serialized bytes + * @param expectedlength the length needed for the current object */ -ReSerializationLengthException::ReSerializationLengthException(int currentLength, - int expectedlength) : - ReSerializationException(), - m_currentLength(currentLength), - m_expectedLength(expectedlength){ +ReSerializationLengthException::ReSerializationLengthException( + int currentLength, int expectedlength) : + ReSerializationException( + ReByteBuffer(i18n("pack error: length to small: ")).appendInt( + currentLength).appendChar('/').appendInt(expectedlength).str()), + m_currentLength(currentLength), + m_expectedLength(expectedlength) { +} +/** + * Constructor. + * + * @param serialId a class specific identifier, inclusive a serialization version + */ +ReSerializable::ReSerializable(int serialId) : + m_serialId(serialId) { +} +/** + * Destructor. + */ +ReSerializable::~ReSerializable() { } diff --git a/base/ReSerializable.hpp b/base/ReSerializable.hpp index dc013bd..6afb0f5 100644 --- a/base/ReSerializable.hpp +++ b/base/ReSerializable.hpp @@ -10,15 +10,23 @@ #ifndef BASE_RESERIALIZABLE_HPP_ #define BASE_RESERIALIZABLE_HPP_ +extern const char* i18nTranslate(const char* key); /** * Base class for all serialization/deserialization errors. */ class ReSerializationException: public ReException { +public: + /** Constructor. + * @param message description of the error + */ + ReSerializationException(const char* message) : + ReException(message) { + } }; /** * The length is not enough for the deserialization. */ -class ReSerializationLengthException : public ReSerializationException { +class ReSerializationLengthException: public ReSerializationException { public: ReSerializationLengthException(int currentLength, int expectedLength); public: @@ -28,7 +36,14 @@ public: /** * Unexpected data found while unpacking. */ -class ReSerializeFormatException : public ReSerializationException { +class ReSerializeFormatException: public ReSerializationException { +public: + /** Constructor. + * @param message description of the error + */ + ReSerializeFormatException(const char* message) : + ReSerializationException(message) { + } }; /** * Abstract base class for serializing. @@ -38,8 +53,8 @@ class ReSerializeFormatException : public ReSerializationException { */ class ReSerializable { public: - virtual ~ReSerializable() { - } + ReSerializable(int serialId); + virtual ~ReSerializable(); public: /** Appends a boolean value. * @param buffer IN/OUT: the buffer with the serialized values @@ -52,21 +67,21 @@ public: * @param buffer IN/OUT: the buffer with the serialized values * @param value the value to serialize */ - inline void packString8(ReByteBuffer& buffer, ReByteBuffer& value) { + inline void packString255(ReByteBuffer& buffer, ReByteBuffer& value) { buffer.appendBits8(value.length()).append(value); } /** Appends a string with a maximal length of 64KiByte to the buffer. * @param buffer IN/OUT: the buffer with the serialized values * @param value the value to serialize */ - inline void appendString16(ReByteBuffer& buffer, ReByteBuffer& value) { + inline void packString64k(ReByteBuffer& buffer, ReByteBuffer& value) { buffer.appendBits16(value.length()).append(value); } /** Appends a string with a maximal length of 4GiByte to the buffer. * @param buffer IN/OUT: the buffer with the serialized values * @param value the value to serialize */ - inline void appendString32(ReByteBuffer& buffer, ReByteBuffer& value) { + inline void packString4t(ReByteBuffer& buffer, ReByteBuffer& value) { buffer.appendBits32(value.length()).append(value); } /** Sets the members of the instance from the byte sequence. @@ -79,6 +94,17 @@ public: * serialized members */ virtual void deserialize(uint8_t*& sequence, size_t& length) = 0; + /** Sets the members of the instance from the byte sequence in a buffer. + * @param sequence the serialized sequence + * @throw ReSerializationLengthException + */ + inline void deserializeBuffer(const ReByteBuffer& sequence) { + size_t length = sequence.length(); + uint8_t* seq = reinterpret_cast(sequence.buffer()); + deserialize(seq, length); + if (length != 0) + throw ReSerializationLengthException(length, 0); + } /** Appends the class members to the end of the buffer. * * @param buffer IN/OUT: the buffer containing the serialized bytes @@ -95,7 +121,8 @@ public: throw ReSerializationLengthException(length, 1); char cc = *sequence++; if (cc != 't' && cc != 'f') - throw ReSerializeFormatException(i18n("not a boolean value")); + throw ReSerializeFormatException( + i18nTranslate("not a boolean value")); value = cc == 't'; length--; } @@ -149,7 +176,8 @@ public: * @param length IN/OUT: the length of sequence * @param value OUT: the value read from the sequence */ - inline void unpackInt64(uint8_t*& sequence, size_t& length, int64_t& value) { + inline void unpackInt64(uint8_t*& sequence, size_t& length, + int64_t& value) { if (length < 8) throw ReSerializationLengthException(length, 8); value = (int64_t(*sequence++) << 56) + (int64_t(*sequence++) << 48) @@ -163,7 +191,8 @@ public: * @param length IN/OUT: the length of sequence * @param value OUT: the value read from the sequence */ - inline void unpackString255(uint8_t*& sequence, size_t& length, ReByteBuffer& value) { + inline void unpackString255(uint8_t*& sequence, size_t& length, + ReByteBuffer& value) { size_t strLen = 0; if (length == 0 || (strLen = *sequence) > length) throw ReSerializationLengthException(length, 1 + strLen); @@ -175,26 +204,31 @@ public: * @param length IN/OUT: the length of sequence * @param value OUT: the value read from the sequence */ - inline void unpackString64k(uint8_t*& sequence, size_t& length, ReByteBuffer& value) { + inline void unpackString64k(uint8_t*& sequence, size_t& length, + ReByteBuffer& value) { size_t strLen = 0; if (length == 0 || (strLen = (sequence[0] << 8) + sequence[1]) > length) throw ReSerializationLengthException(length, 1 + strLen); value.set(reinterpret_cast(sequence), strLen); - length -= strLen - 1; + length -= strLen - 2; } /** Reads a string with a max. length of 64KiByte from the serialized byte sequence. * @param sequence IN/OUT: the byte sequence with the serialized data * @param length IN/OUT: the length of sequence * @param value OUT: the value read from the sequence */ - inline void unpackString4t(uint8_t*& sequence, size_t& length, ReByteBuffer& value) { + inline void unpackString4t(uint8_t*& sequence, size_t& length, + ReByteBuffer& value) { size_t strLen = 0; - if (length == 0 || (strLen = (sequence[0] << 24) + (sequence[1] << 16) - + (sequence[2] << 8) + sequence[3]) > length) + if (length == 0 + || (strLen = (sequence[0] << 24) + (sequence[1] << 16) + + (sequence[2] << 8) + sequence[3]) > length) throw ReSerializationLengthException(length, 1 + strLen); value.set(reinterpret_cast(sequence), strLen); - length -= strLen - 1; + length -= strLen - 4; } +private: + int m_serialId; }; #endif /* BASE_RESERIALIZABLE_HPP_ */ diff --git a/base/rebase.hpp b/base/rebase.hpp index f035848..67d4e9e 100644 --- a/base/rebase.hpp +++ b/base/rebase.hpp @@ -107,7 +107,7 @@ public: * @param b 2nd value * @return minimum of a and b */ -inline int min(int a, int b){ +inline int min(int a, int b) { return a < b ? a : b; } /** Returns the maximum of 2 values. @@ -115,9 +115,10 @@ inline int min(int a, int b){ * @param b 2nd value * @return maximum of a and b */ -inline int max(int a, int b){ +inline int max(int a, int b) { return a < b ? a : b; } +#include "base/ReException.hpp" #include "base/ReMutex.hpp" #include "base/ReByteBuffer.hpp" #include "base/ReSerializable.hpp" @@ -126,7 +127,6 @@ inline int max(int a, int b){ #include "base/ReThread.hpp" #include "base/ReTestUnit.hpp" #include "base/ReCString.hpp" -#include "base/ReException.hpp" #include "base/ReStringUtils.hpp" #include "base/ReDirectory.hpp" #include "base/ReSeqArray.hpp" diff --git a/cunit/cuReByteBuffer.cpp b/cunit/cuReByteBuffer.cpp index cbb0697..4a68ea1 100644 --- a/cunit/cuReByteBuffer.cpp +++ b/cunit/cuReByteBuffer.cpp @@ -16,6 +16,7 @@ public: } private: void run() { + testAppendBits(); testAppendDump(); testAppendFix(); testEnsureLastChar(); @@ -48,7 +49,15 @@ private: testSplice(); testReplace(); } - void testIsPrefixOf(){ + void testAppendBits() { + ReByteBuffer buffer; + buffer.appendBits8(0x31).appendBits16(0x3233).appendBits24(0x343536); + buffer.appendBits32(0x37383941); + checkEqu("123456789A", buffer); + buffer.set("x", 1).appendBits64(0x4243444546474849LL); + checkEqu("xBCDEFGHI", buffer); + } + void testIsPrefixOf() { ReByteBuffer buffer; // predefined length (of source): // case sensitive, same size @@ -68,7 +77,6 @@ private: // case sensitive, longer checkF(buffer.set("aBcd").isPrefixOf("aBcd", 3)); - // case insensitive, same size checkT(buffer.set("aBc").isPrefixOf("abc", -1, true)); // case sensitive, shorter @@ -80,7 +88,7 @@ private: checkT(buffer.set("aBc").isPrefixOf("abcd", -1, true, 2)); checkF(buffer.set("aB").isPrefixOf("abc", -1, true, 3)); } - void testAppendDump(){ + void testAppendDump() { ReByteBuffer buffer; // true ASCII: buffer.appendDump("abc"); @@ -91,9 +99,11 @@ private: checkEqu("a\tb\nc\rd", buffer.str()); // binary: int64_t ii = 0x12345678abcdefll; - buffer.setLength(0).appendDump(reinterpret_cast(&ii), sizeof ii, 40); - checkEqu("0000: ef cd ab 78 56 34 12 00 | ...xV4.. ", - buffer.str()); + buffer.setLength(0).appendDump(reinterpret_cast(&ii), + sizeof ii, 40); + checkEqu( + "0000: ef cd ab 78 56 34 12 00 | ...xV4.. ", + buffer.str()); } void testAppendFix() { ReByteBuffer buffer; @@ -549,14 +559,14 @@ private: checkEqu(0, buffer.atoi(5, 6)); buffer.set("0x98765432", -1); - checkEqu(0x98765432, (unsigned ) buffer.atoi()); + checkEqu(0x98765432U, (unsigned ) buffer.atoi()); buffer.set("0xabcdef01", -1); - checkEqu(0xabcdef01, (unsigned ) buffer.atoi()); + checkEqu(0xabcdef01U, (unsigned ) buffer.atoi()); buffer.set("0xABCDEF01", -1); - checkEqu(0xabcdef01, (unsigned ) buffer.atoi()); + checkEqu(0xabcdef01U, (unsigned ) buffer.atoi()); buffer.set("0xaFFe01", -1); - checkEqu(0xaFFe01u, (unsigned ) buffer.atoi()); + checkEqu(0xaFFe01U, (unsigned ) buffer.atoi()); } void testEnsureSizeGetLength() { ReByteBuffer buf1; diff --git a/cunit/cuReSerializable.cpp b/cunit/cuReSerializable.cpp new file mode 100644 index 0000000..dbb53b1 --- /dev/null +++ b/cunit/cuReSerializable.cpp @@ -0,0 +1,113 @@ +/* + * cuReSerializable.cpp + * + * License: Public domain + * Do what you want. + * No warranties and disclaimer of any damages. + * The latest sources: https://github.com/republib + */ +#include "base/rebase.hpp" + +class ExampleClass: public ReSerializable { +public: + ExampleClass() : + ReSerializable(m_serialId), + m_int8(0), + m_int16(0), + m_int32(0), + m_bool(false), + m_int64(0), + m_string255(), + m_string64k(), + m_string4t() { + } + ExampleClass(int int8, int int16, int int32, bool boolValue, int64_t int64, + const char* string1, const char* string2, const char* string3) : + ReSerializable(m_serialId), + m_int8(int8), + m_int16(int16), + m_int32(int32), + m_bool(boolValue), + m_int64(int64), + m_string255(string1), + m_string64k(string2), + m_string4t(string3) { + } + virtual void deserialize(uint8_t*& sequence, size_t& length) { + int id; + unpackInt24(sequence, length, id); + unpackInt8(sequence, length, m_int8); + unpackInt16(sequence, length, m_int16); + unpackInt32(sequence, length, m_int32); + unpackBool(sequence, length, m_bool); + unpackInt64(sequence, length, m_int64); + unpackString255(sequence, length, m_string255); + unpackString64k(sequence, length, m_string64k); + unpackString4t(sequence, length, m_string4t); + } + virtual ReByteBuffer& serialize(ReByteBuffer& buffer) { + buffer.appendBits24(m_serialId).appendBits8(m_int8); + buffer.appendBits16(m_int16).appendBits32(m_int32); + packBool(buffer, m_bool); + buffer.appendBits64(m_int64); + packString255(buffer, m_string255); + packString64k(buffer, m_string64k); + packString4t(buffer, m_string4t); + } + const char* toString(ReByteBuffer& buffer) { + buffer.setLength(0).append("id: ").appendInt(m_serialId); + buffer.append(" i8:").appendInt(m_int8); + buffer.append(" i16:").appendInt(m_int16); + buffer.append(" i32:").appendInt(m_int32); + buffer.append(" b:").appendChar(m_bool ? 't' : 'f'); + buffer.append(" i64:").appendInt(m_int64); + buffer.append(" s255:").append(m_string255); + buffer.append(" s64k:").append(m_string64k); + buffer.append(" s4t:").append(m_string4t); + return buffer.str(); + } +private: + int m_int8; + int m_int16; + int m_int32; + bool m_bool; + int64_t m_int64; + ReByteBuffer m_string255; + ReByteBuffer m_string64k; + ReByteBuffer m_string4t; +private: + static int m_serialId; +}; +int ExampleClass::m_serialId = 0x123401; + +class TestReSerializable: public ReTestUnit { + typedef ReByteBuffer::Byte Byte; +public: + TestReSerializable() : + ReTestUnit("ReSerializable", __FILE__) { + run(); + } +private: + void run() { + testBasic(); + } + void testBasic() { + ReByteBuffer buffer; + ExampleClass example(250, 64000, 12345678, true, 0x12345678abcdll, + "king", "lives", "!"); + checkEqu( + "id: 1192961 i8:250 i16:64000 i32:12345678 b:t i64:20015998348237 s255:king s64k:lives s4t:!", + example.toString(buffer)); + ReByteBuffer serialBuffer; + example.serialize(serialBuffer); + ExampleClass example2; + example.deserializeBuffer(serialBuffer); + checkEqu("", example2.toString(buffer)); + } +}; +extern void testReSerializable(void); + +void testReSerializable(void) { + TestReSerializable unit; +} + diff --git a/cunit/testall.cpp b/cunit/testall.cpp index d4c8cd1..4e566a2 100644 --- a/cunit/testall.cpp +++ b/cunit/testall.cpp @@ -15,8 +15,8 @@ static bool s_testAll = true; void testBase() { - extern void testReSeqArray(void); - testReSeqArray(); + extern void testReSerializable(void); + testReSerializable(); extern void testReTestUnit(); //testReTestUnit(); @@ -40,6 +40,8 @@ void testBase() { testReProgramArgs(); extern void testReLogger(void); testReLogger(); + extern void testReSerializable(void); + testReSerializable(); } void testString() {