µSer
Examples

Table of Contents

µSer ships with a set of examples in the "examples" subdirectory. Run "make" in that directory to compile them all. The "examples/C" directory contains an example for using µSer with a C project consisting of multiple files.

Serializing an integer into an array

#include <cstdint>
#include <iostream>
#include <uSer.hh>
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [4];
// Define the data to be serialized.
const std::uint32_t x = 0x54534554;
// Perform the actual serialization.
uSer::serialize (raw, x);
// Write the binary data to standard output.
std::cout.write (reinterpret_cast<char*> (raw), 4) << std::endl;
}

Deserializing an integer from an array

#include <cstdint>
#include <iostream>
#include <ios>
#include <uSer.hh>
int main () {
// Define an array with binary data to be deserialized.
const std::uint8_t raw [4] = { 0x54, 0x45, 0x53, 0x54 };
// Define a variable that receives the deserialized data.
std::uint32_t x;
// Perform the actual deserialization.
// Write the result to standard output.
std::cout << "0x" << std::hex << x << std::endl;
}

Serializing an array of integers into an array

#include <cstdint>
#include <iostream>
#include <array>
#include <uSer.hh>
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [4];
// Define the data to be serialized.
const std::array<std::uint16_t, 2> x {{ 0x4554, 0x5453 }};
// Perform the actual serialization.
uSer::serialize (raw, x);
// Write the binary data to standard output.
std::cout.write (reinterpret_cast<char*> (raw), 4) << std::endl;
}

Serializing a tuple of integers into an array

#include <cstdint>
#include <iostream>
#include <tuple>
#include <uSer.hh>
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [4];
// Define the data to be serialized.
const std::tuple<std::uint16_t, std::uint8_t, std::uint8_t> x { 0x4554, 0x53, 0x54 };
// Perform the actual serialization.
uSer::serialize (raw, x);
// Write the binary data to standard output.
std::cout.write (reinterpret_cast<char*> (raw), 4) << std::endl;
}

Serializing a vector of integers into an array

#include <cstdint>
#include <iostream>
#include <vector>
#define USER_EXCEPTIONS
#include <uSer.hh>
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [4];
// Define the data to be serialized.
const std::vector<std::uint16_t> x { 0x4554, 0x5453 };
try {
// Perform the actual serialization.
uSer::serialize (raw, x);
// Write the binary data to standard output.
std::cout.write (reinterpret_cast<char*> (raw), 4) << std::endl;
} catch (const uSer::Exception& ex) {
// In case of error, print it
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}

Pre-Processing raw data with swapped bytes

#include <cstdint>
#include <iostream>
#include <tuple>
#include <uSer.hh>
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [4];
// Define the data to be serialized.
std::tuple<std::uint16_t, std::uint8_t, std::uint8_t> x { 0x4554, 0x53, 0x54 };
// Perform the actual serialization.
uSer::serialize (raw, x);
// Simulate a communication channel which combines the 8-Bit-Integers into 16-Bit-Integers
// in a big endian way:
std::uint16_t raw2 [2];
for (std::size_t i = 0; i < sizeof(raw)/2 /* uint8_t always has size 1 */; ++i) {
raw2[i] = static_cast<std::uint16_t> ((uint16_t { raw[i*2] } << 8) | raw [i*2+1]);
}
// Turn the swapped 16-Bit-Words back into a sequence of words where the least significant bit comes first
std::uint16_t raw3 [2];
uSer::deserialize<uSer::ByteOrder::BE> (raw2, raw3);
// Actually deserialize the data
uSer::deserialize (raw3, x);
// Write the data to standard output. We need to convert the uint8_t elements into integers,
// because else they will be interpreted as individual characters.
std::cout << std::hex << "0x" << std::get<0> (x) << ", " << "0x" << int { std::get<1> (x) } << ", " << "0x" << int { std::get<2> (x) } << std::endl;
}

Specifying attributes via function arguments

#include <cstdint>
#include <iostream>
#include <uSer.hh>
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [4];
// Define the data to be serialized.
const std::uint_least32_t x = 0x54534554;
// Perform the actual serialization in Big Endian order.
uSer::serialize<uSer::ByteOrder::BE, uSer::Width<32>> (raw, x);
// Write the binary data to standard output.
std::cout.write (reinterpret_cast<char*> (raw), 4) << std::endl;
}

Serializing an integer into a std::vector

#include <cstdint>
#include <iostream>
#include <vector>
#define USER_EXCEPTIONS
#include <uSer.hh>
int main () {
// Define a vector to receive the raw binary data.
std::vector<std::uint8_t> raw (4);
// Define the data to be serialized.
const std::uint32_t x = 0x54534554;
try {
// Perform the actual serialization. Implicitly uses raw.size() to obtain the raw buffer size.
uSer::serialize (raw, x);
// Write the binary data to standard output.
std::cout.write (reinterpret_cast<char*> (raw.data ()), 4) << std::endl;
} catch (const uSer::Exception& ex) {
// In case of error, print it
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}

Serializing an integer into a std::vector with FixedSize

#include <cstdint>
#include <iostream>
#include <vector>
#include <uSer.hh>
int main () {
// Define a vector to receive the raw binary data.
std::vector<std::uint8_t> raw (4);
// Define the data to be serialized.
const std::uint32_t x = 0x54534554;
// Perform the actual serialization.
uSer::serialize (raw, x, uSer::fixedSize<4>);
// Write the binary data to standard output.
std::cout.write (reinterpret_cast<char*> (raw.data ()), 4) << std::endl;
}

Deserializing an integer from a dynamically-sized std::vector with DynSize

#include <cstdint>
#include <iostream>
#include <vector>
#define USER_EXCEPTIONS
#include <uSer.hh>
int main () {
// Define a vector to with the raw binary data.
const std::vector<std::uint8_t> raw { 0xBE, 0xBA, 0xFE, 0xC0, 0xEF, 0xBE, 0xAD, 0xDE };
try {
// Define a variable to receive the deserialized data
std::uint32_t x;
// Perform the actual deserialization while using only the first half of the buffer.
uSer::deserialize<uSer::RawInfo<uint16_t, 8>> (raw, x, raw.size () / 2);
// uSer::deserialize (raw, x, uSer::DynSize { raw.size () / 2 }); // Same functionality made explicit
// Write the integer to standard output.
std::cout << std::hex << x << std::endl;
} catch (const uSer::Exception& ex) {
// In case of error, print it
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}

Serializing an integer into a std::vector with InfSize

#include <cstdint>
#include <iostream>
#include <vector>
#include <iterator>
#include <uSer.hh>
int main () {
// Define a vector to receive the raw binary data.
std::vector<std::uint8_t> raw;
// Define the data to be serialized.
const std::uint32_t x = 0x54534554;
// Perform the actual serialization; automatically append elements
// to the raw vector without an explicit size limit.
uSer::serialize<uSer::RawInfo<std::uint8_t>> (std::back_inserter (raw), x, uSer::infSize);
// Write the binary data to standard output.
std::cout.write (reinterpret_cast<const char*> (raw.data ()), static_cast<std::streamsize> (raw.size ())) << std::endl;
}

Using return codes to process errors

#include <cstdint>
#include <iostream>
#include <vector>
#include <uSer.hh>
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [4];
// Define the data to be serialized.
const std::vector<std::uint16_t> x { 0x4554, 0x5453 };
// Perform the actual serialization.
// Check for success
if (ec == uSer_EOK) {
// Write the binary data to standard output.
std::cout.write (reinterpret_cast<char*> (raw), 4) << std::endl;
} else {
// Print error message
std::cerr << "uSer Error: " << uSer_getErrorMessage(ec) << std::endl;
}
}

Defining a simple struct for serialization

#include <cstdint>
#include <iostream>
#include <uSer.hh>
// Define a serializable struct
struct A {
// USER_STRUCT (A, uSer::AttrNone)
// Begin declaration
// Define arbitrary members
std::uint16_t a, b;
std::uint8_t c;
std::uint32_t d;
// Make members known to µSer
USER_ENUM_MEM (a, b, c, d)
};
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [9];
// Define the data to be serialized.
A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
// Perform the actual serialization.
uSer::serialize (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
}

Annotating a struct with attributes

#include <cstdint>
#include <iostream>
#include <uSer.hh>
// Define a serializable struct
struct A {
// Begin declaration
// Define arbitrary members
std::uint16_t a, b;
std::uint8_t c;
std::uint32_t d;
// Make members known to µSer
USER_ENUM_MEM (a, b, c, d)
};
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [9];
// Define the data to be serialized.
A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
// Perform the actual serialization.
uSer::serialize (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
}

Annotating a struct member with attributes

#include <cstdint>
#include <iostream>
#include <uSer.hh>
// Define a serializable struct
struct A {
// Begin declaration
// Define arbitrary members
std::uint16_t a, b;
std::uint8_t c;
std::uint32_t d;
// Annotate a member
USER_MEM_ANNOT(d, uSer::ByteOrder::BE)
// Make members known to µSer
USER_ENUM_MEM (a, b, c, d)
};
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [9];
// Define the data to be serialized.
A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
// Perform the actual serialization.
uSer::serialize (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
}

Defining and annotating a struct member in one step

#include <cstdint>
#include <iostream>
#include <uSer.hh>
// Define a serializable struct
struct A {
// Begin declaration
// Define arbitrary members
std::uint16_t a, b;
std::uint8_t c;
USER_MEM(std::uint32_t, d, uSer::ByteOrder::BE)
// Annotate a member
// Make members known to µSer
USER_ENUM_MEM (a, b, c, d)
};
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [9];
// Define the data to be serialized.
A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
// Perform the actual serialization.
uSer::serialize (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
}

Defining and annotating all struct members in one step

#include <cstdint>
#include <iostream>
#include <uSer.hh>
// Define a serializable struct
struct A {
// Begin declaration
// Define and annotate multiple members
(std::uint16_t,a,uSer::AttrNone),
(std::uint16_t,b),
(std::uint8_t,c),
(std::uint32_t,d,uSer::ByteOrder::BE)
)
};
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [9];
// Define the data to be serialized.
A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
// Perform the actual serialization.
uSer::serialize (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
}

Annotating a struct without modifying its definition

#include <cstdint>
#include <iostream>
#include <uSer.hh>
namespace N {
// Normal definition of a struct
struct A {
// Define arbitrary members
std::uint16_t a, b;
std::uint8_t c;
std::uint32_t d;
};
}
// These macro calls need to be in the global scope.
// Optional: Define attributes for the whole struct.
// Optional: Annotate a struct member
USER_EXT_MEM_ANNOT(N::A, d, uSer::ByteOrder::BE)
// List all struct members
USER_EXT_ENUM_MEM(N::A, a, b, c, d)
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [9];
// Define the data to be serialized.
N::A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
// Perform the actual serialization.
uSer::serialize (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
}

Annotating a struct without modifying its definition in a compact fashion

#include <cstdint>
#include <iostream>
#include <uSer.hh>
namespace N {
// Normal definition of a struct
struct A {
// Define arbitrary members
std::uint16_t a, b;
std::uint8_t c;
std::uint32_t d;
};
}
// These macro calls need to be in the global scope.
// Optional: Define attributes for the whole struct.
// List all struct members
(a,uSer::AttrNone),
(b),
(c),
(d,uSer::ByteOrder::BE))
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [9];
// Define the data to be serialized.
N::A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
// Perform the actual serialization.
uSer::serialize (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
}

Serializing an integer that is not a multiple of a byte in different byte orders

#include <cstdint>
#include <iostream>
#include <uSer.hh>
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [2];
// Define the data to be serialized.
std::uint16_t x = 0x765;
// Serialize as little endian
uSer::serialize<uSer::ByteOrder::LE, uSer::Width<11>> (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
// Serialize as big endian
uSer::serialize<uSer::ByteOrder::BE, uSer::Width<11>> (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
}

Serializing a signed integer with different sign formats

#include <cstdint>
#include <iostream>
#include <uSer.hh>
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [2];
// Define the data to be serialized.
const std::int16_t x = -291;
// Serialize as 2's complement
uSer::serialize<uSer::SignFormat::TwosComplement> (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
// Serialize as 1's complement
uSer::serialize<uSer::SignFormat::OnesComplement> (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
// Serialize as signed-magnitude
uSer::serialize<uSer::SignFormat::SignedMagnitude> (raw, x);
// Write the binary data to standard output.
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
}

Setting integer width to convert RGB565 color values

#include <cstdint>
#include <iostream>
#include <uSer.hh>
// Define a serializable struct for storing colors in RGB565 format.
struct Color {
// Define color components.
std::uint8_t r, g, b;
// Explicitly set the sizes of the integers
USER_MEM_ANNOT(r, uSer::Width<5>)
USER_MEM_ANNOT(g, uSer::Width<6>)
USER_MEM_ANNOT(b, uSer::Width<5>)
// Make members known to µSer
USER_ENUM_MEM (r, g, b)
};
int main () {
// Store the binary data in a 16bit-Integer
std::uint16_t raw [1];
// Define a color to be serialized.
Color c1 { 24, 55, 12 };
// Convert RGB565-Color into 16bit-Value
uSer::serialize (raw, c1);
// Output the raw value
std::cout << std::hex << std::setw(4) << "0x" << raw[0] << std::endl;
}

Using padding bits to skip dummy data in the raw stream

#include <cstdint>
#include <iostream>
#include <uSer.hh>
// Define a serializable struct for storing colors in RGB565 format.
struct Color {
// Define color components.
std::uint8_t r, b;
// Explicitly set the sizes of the integers
USER_MEM_ANNOT(r, uSer::Width<5>, uSer::Padding::Fixed<6>)
USER_MEM_ANNOT(b, uSer::Width<5>)
// Make members known to µSer
};
int main () {
// Store the binary data in a 16bit-Integer
std::uint16_t raw [1];
// Define a color to be serialized.
Color c1 { 24, 12 };
// Convert RGB565-Color into 16bit-Value
uSer::serialize (raw, c1);
// Output the raw value
std::cout << std::hex << std::setw(4) << "0x" << raw[0] << std::endl;
}

Convert a file's byte order by using input/output iterators

#include <cstdint>
#include <iostream>
#include <ios>
#include <fstream>
#include <iterator>
#include <sstream>
#define USER_EXCEPTIONS
#include <uSer.hh>
int main () {
try {
// Open input file
std::ifstream input ("rawdata.be", std::ios::binary);
if (!input)
throw std::runtime_error ("File could not be opened");
// Determine file size
input.seekg (0, std::ios::end);
std::size_t fSize = static_cast<std::size_t> (input.tellg ());
input.seekg (0, std::ios::beg);
std::uint32_t x;
// Read a 32-Bit-Integer as big endian
uSer::deserialize<uSer::ByteOrder::BE> (std::istream_iterator<std::uint8_t> (input), x, fSize);
// Open output file
std::ofstream output ("rawdata.le", std::ios::binary);
output.exceptions (std::ofstream::failbit);
// Write the integer as little endian
uSer::serialize<uSer::RawInfo<std::uint8_t>, uSer::ByteOrder::LE> (std::ostream_iterator<std::uint8_t> (output), x, uSer::infSize);
} catch (const std::exception& ex) {
// Handle errors
std::cerr << "Error: " << ex.what () << std::endl;
}
}

Convert a file's byte order by using custom input/output iterators

#include <cstdint>
#include <iostream>
#include <ios>
#include <fstream>
#include <iterator>
#include <sstream>
#define USER_EXCEPTIONS
#include <uSer.hh>
class InIter {
public:
InIter (std::istream& os) : m_stream (&os) {}
InIter (InIter&& src) = default;
InIter& operator = (InIter&&) = default;
// Type Definitions for std::iterator_traits
using value_type = std::uint8_t;
using iterator_category = std::input_iterator_tag;
using pointer = std::uint8_t*;
using reference = std::uint8_t&;
using difference_type = std::ptrdiff_t;
// Operator * can be used to perform the actual reading
std::uint8_t operator * () {
std::uint8_t x;
*m_stream >> x;
return x;
}
// The increment operator can be a NOP
InIter& operator ++ () { return *this; }
private:
std::istream* m_stream;
};
class OutIter {
public:
OutIter (std::ostream& os) : m_stream (&os) {}
OutIter (OutIter&& src) = default;
OutIter& operator = (OutIter&&) = default;
// Type Definitions for std::iterator_traits
using value_type = std::uint8_t;
using iterator_category = std::output_iterator_tag;
using pointer = std::uint8_t*;
using reference = std::uint8_t&;
using difference_type = std::ptrdiff_t;
// To allow "(*iter)=x", simply return *this.
OutIter& operator * () {
return *this;
}
// Do the actual writing
void operator = (std::uint8_t x) {
*m_stream << x;
}
// The increment operator can be a NOP
OutIter& operator ++ () {
return *this;
}
private:
std::ostream* m_stream;
};
int main () {
try {
// Open input file
std::ifstream input ("rawdata.be", std::ios::binary);
if (!input)
throw std::runtime_error ("File could not be opened");
// Determine file size
input.seekg (0, std::ios::end);
std::size_t fSize = static_cast<std::size_t> (input.tellg ());
input.seekg (0, std::ios::beg);
std::uint32_t x;
// Read a 32-Bit-Integer as big endian
uSer::deserialize<uSer::ByteOrder::BE> (InIter { input }, x, fSize);
// Open output file
std::ofstream output ("rawdata.le", std::ios::binary);
output.exceptions (std::ofstream::failbit);
// Write the integer as little endian
uSer::serialize<uSer::ByteOrder::LE> (OutIter (output), x, uSer::infSize);
} catch (const std::exception& ex) {
// Handle errors
std::cerr << "Error: " << ex.what () << std::endl;
}
}

Deserializing a vector of integers from an array.

#include <cstdint>
#include <iostream>
#include <vector>
#define USER_EXCEPTIONS
#include <uSer.hh>
int main () {
// Define an array with the raw binary data.
const std::uint8_t raw [4] { 0x54, 0x45, 0x53, 0x54 };
// Define a vector to receive the deserialized data and allocate it to the desired size.
std::vector<std::uint16_t> x (2);
try {
// Perform the actual deserialization.
// Write the data to standard output.
std::cout << std::hex << "0x" << x [0] << ", 0x" << x [1] << std::endl;
} catch (const uSer::Exception& ex) {
// In case of error, print it
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}

Defining dynamic data structures with Dyn::Size

#include <cstdint>
#include <iostream>
#define USER_EXCEPTIONS
#include <uSer.hh>
struct A;
std::size_t g_getSize (const struct A&);
// Functional for determining the dynamic size (class with operator () overloaded)
struct Functional {
std::size_t operator () (const struct A&);
} functional;
// Define a serializable struct
struct A {
// Member function for determining the dynamic size
std::size_t m_getSize () const {
return N2;
}
// Begin declaration
// Size of array1
std::uint16_t N1;
// Define an array
std::uint8_t array1 [20];
// Define the array size dynamically using a member variable
USER_MEM_ANNOT (array1, uSer::Dyn::Size<&A::N1>)
std::uint16_t N2;
// Define an array
std::uint8_t array2 [20];
// Define the array size dynamically using a member function
USER_MEM_ANNOT (array2, uSer::Dyn::Size<&A::m_getSize>)
std::uint16_t N3;
// Define an array
std::uint8_t array3 [20];
// Define the array size dynamically using a free function
USER_MEM_ANNOT (array3, uSer::Dyn::Size<&g_getSize>)
std::uint16_t N4;
// Define an array
std::uint8_t array4 [20];
// Define the array size dynamically using a functional (class with operator () overloaded)
USER_MEM_ANNOT (array4, uSer::Dyn::Size<&functional>)
// Make members known to µSer
USER_ENUM_MEM (N1, array1, N2, array2, N3, array3, N4, array4)
};
// Free function for determining the dynamic size
std::size_t g_getSize (const struct A& a) {
return a.N3;
}
std::size_t Functional::operator () (const struct A& a) {
return a.N4;
}
int main () {
// Define an array with binary data
const std::uint8_t raw [26] = { 0x03, 0x00, 0x01, 0x02, 0x03,
0x04, 0x00, 0x01, 0x02, 0x03, 0x04,
0x05, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
// Define a struct to receive the deserialized data
A x;
try {
// Perform the actual deserialization.
// Write the read data to standard output.
std::copy (x.array1, x.array1 + x.N1, std::ostream_iterator<int> (std::cout, ", ")); std::cout << std::endl;
std::copy (x.array2, x.array2 + x.N2, std::ostream_iterator<int> (std::cout, ", ")); std::cout << std::endl;
std::copy (x.array3, x.array3 + x.N3, std::ostream_iterator<int> (std::cout, ", ")); std::cout << std::endl;
std::copy (x.array4, x.array4 + x.N4, std::ostream_iterator<int> (std::cout, ", ")); std::cout << std::endl;
} catch (const uSer::Exception& ex) {
// In case of error, print it
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}

Defining optional data structures with Dyn::Optional

#include <cstdint>
#include <iostream>
#define USER_EXCEPTIONS
#include <uSer.hh>
struct A;
bool g_getSize (const struct A&);
// Functional for determining the existence (class with operator () overloaded)
struct Functional {
bool operator () (const struct A&);
} functional;
// Define a serializable struct
struct A {
// Member function for determining whether an object is present
bool m_getSize () const {
return N2;
}
// Begin declaration
// 1 if v1 exists, 0 else
std::uint8_t N1;
// Define an optional object
std::uint8_t v1;
// Define the presence of an object to be optional using a member variable
USER_MEM_ANNOT (v1, uSer::Dyn::Optional<&A::N1>)
// 1 if v2 exists, 0 else
std::uint8_t N2;
// Define an optional object
std::uint8_t v2;
// Define the presence of an object to be optional using a member function
USER_MEM_ANNOT (v2, uSer::Dyn::Optional<&A::m_getSize>)
// 1 if v3 exists, 0 else
std::uint8_t N3;
// Define an optional object
std::uint8_t v3;
// Define the presence of an object to be optional using a free function
USER_MEM_ANNOT (v3, uSer::Dyn::Optional<&g_getSize>)
// 1 if v4 exists, 0 else
std::uint8_t N4;
// Define an optional object
std::uint8_t v4;
// Define the presence of an object to be optional using a functional (class with operator () overloaded)
USER_MEM_ANNOT (v4, uSer::Dyn::Optional<&functional>)
// Make members known to µSer
USER_ENUM_MEM (N1, v1, N2, v2, N3, v3, N4, v4)
};
// Free function for determining the presence
bool g_getSize (const struct A& a) {
return a.N3 != 0;
}
bool Functional::operator () (const struct A& a) {
return a.N4 != 0;
}
int main () {
// Define an array with binary data
const std::uint8_t raw [6] = { 0x01, 0x01,
0x00,
0x01, 0x2A,
0x00 };
// Define a struct to receive the deserialized data
A x;
try {
// Perform the actual deserialization.
// Write the read data to standard output.
if (x.N1) std::cout << "v1: " << std::hex << int (x.v1) << std::endl;
if (x.N2) std::cout << "v2: " << std::hex << int (x.v2) << std::endl;
if (x.N3) std::cout << "v3: " << std::hex << int (x.v3) << std::endl;
if (x.N4) std::cout << "v4: " << std::hex << int (x.v4) << std::endl;
} catch (const uSer::Exception& ex) {
// In case of error, print it
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}

Defining hook funktions to be called before/after (de)serialization

#include <cstdint>
#include <iostream>
#define USER_EXCEPTIONS
#include <uSer.hh>
struct A;
void serPost (const uint8_t&, const A&);
// Functional to be called before deserialization
struct DeSerPre {
void operator () (uint8_t&, A&);
} deSerPre;
// Define a serializable struct
struct A {
// Member function to be called before serialization
void serPre (const uint8_t& a) const {
std::cout << "SerPre\n";
// Simulate an error condition
if (a != 42)
}
// Member function to be called after deserialization
uSer_ErrorCode deSerPost () {
std::cout << "DeSerPost\n";
// Simulate an error condition
return a == 42 ? uSer_EOK : uSer_EHOOK;
}
// Begin declaration. Define hook to be called after deserialization of the whole struct.
// Define some members
std::uint8_t a, b, c;
// Define hooks to be called before/after (de)serialization of the individual members
USER_MEM_ANNOT(a, uSer::Hook::SerPre<&A::serPre>)
USER_MEM_ANNOT(b, uSer::Hook::SerPost<&serPost>)
USER_MEM_ANNOT(c, uSer::Hook::DeSerPre<&deSerPre>)
// Make members known to µSer
USER_ENUM_MEM (a, b, c)
};
// Hook function to be called after serialization
void serPost (const uint8_t&, const A&) {
std::cout << "SerPost\n";
}
void DeSerPre::operator () (uint8_t&, A&) {
std::cout << "DeSerPre\n";
}
int main () {
// Define an array to receive the raw binary data.
std::uint8_t raw [3];
// Define the data to be serialized.
A x { 42, 2, 3 };
try {
// Serialize & deserialize
uSer::serialize (raw, x);
} catch (const uSer::Exception& ex) {
// In case of error, print it
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}

Using µSer in C projects

Common header file for data structures

#include <uSer.hh>
// Define data structures compatible with both C and C++.
// Define a simple struct containing integers
struct PacketA {
// Begin struct definition
uint32_t a;
// Encode a in Big-Endian in the raw data
USER_MEM_ANNOT(a, uSer::ByteOrder::BE)
uint16_t b;
int8_t c;
// Encode c in signed-magnitude format in the raw data
USER_MEM_ANNOT(c, uSer::SignFormat::SignedMagnitude)
// List members
USER_ENUM_MEM (a, b, c)
};
// Define a struct containing a dynamic data structure. Use the "typedef struct"-Trick to make usage more convenient.
typedef struct PacketB_ {
// Begin struct definition
uint8_t N;
struct PacketA packets [8];
// Let N denote the size of the arra packets.
USER_MEM_ANNOT (packets, uSer::Dyn::Size<&PacketB_::N>)
USER_ENUM_MEM (N, packets)
} PacketB;
// Declare functions for (de)serialization of the data structure. These will be implemented as C++.
USER_EXTERN_C uSer_ErrorCode serializePacketB (uint8_t* raw, const PacketB* pk, size_t bufferSize);
USER_EXTERN_C uSer_ErrorCode deserializePacketB (const uint8_t* raw, PacketB* pk, size_t bufferSize);

Implementation of the C++ serialization functions

#include "packet.h"
USER_EXTERN_C uSer_ErrorCode serializePacketB (uint8_t* raw, const PacketB* pk, size_t bufferSize) {
// Just call the µSer serialization function
return uSer::serialize (raw, *pk, bufferSize);
}
USER_EXTERN_C uSer_ErrorCode deserializePacketB (const uint8_t* raw, PacketB* pk, size_t bufferSize) {
// Just call the µSer deserialization function
return uSer::deserialize (raw, *pk, bufferSize);
}

Calling serialization functions from C code

#include <stdio.h>
#include <inttypes.h>
#include "packet.h"
int main () {
// Define a packet instance and fill it with some data
PacketB pk;
pk.N = 2;
pk.packets [0].a = 0xDEADBEEF;
pk.packets [0].b = 0xAA55;
pk.packets [0].c = -42;
pk.packets [1].a = 0xC0FEBABE;
pk.packets [1].b = 0x1234;
pk.packets [1].c = 35;
// Define an array to receive the raw data
uint8_t raw [15];
// Serialize the packet into the raw data
uSer_ErrorCode ec = serializePacketB (raw, &pk, sizeof (raw) /* uint8_t always has size 1 */);
// Check for errors
if (ec != uSer_EOK) {
fprintf (stderr, "Serialization error: %s\n", uSer_getErrorMessage (ec));
return 1;
} else {
// Print the raw data
puts ("Serialization result:");
for (size_t i = 0; i < sizeof(raw); ++i) {
printf ("%02x, ", raw [i]);
}
puts ("");
}
// Deserialize the raw data back into the struct
ec = deserializePacketB (raw, &pk, sizeof (raw));
// Check for errors
if (ec != uSer_EOK) {
fprintf (stderr, "Deserialization error: %s\n", uSer_getErrorMessage (ec));
return 1;
} else {
// Print the deserialized data structure
puts ("Deserialization result:");
printf ("pk.N=%" PRIu8 "\n", pk.N);
for (size_t i = 0; i < pk.N; ++i) {
printf ("pk.packets[%zd].a=%" PRIx32 ", .b=%" PRIx16 ", .c=%" PRId8 "\n", i, pk.packets [i].a, pk.packets [i].b, pk.packets [i].c);
}
}
}