µ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>
int main () {
std::uint8_t raw [4];
const std::uint32_t x = 0x54534554;
std::cout.write (reinterpret_cast<char*> (raw), 4) << std::endl;
}
Deserializing an integer from an array
#include <cstdint>
#include <iostream>
#include <ios>
int main () {
const std::uint8_t raw [4] = { 0x54, 0x45, 0x53, 0x54 };
std::uint32_t x;
std::cout << "0x" << std::hex << x << std::endl;
}
Serializing an array of integers into an array
#include <cstdint>
#include <iostream>
#include <array>
int main () {
std::uint8_t raw [4];
const std::array<std::uint16_t, 2> x {{ 0x4554, 0x5453 }};
std::cout.write (reinterpret_cast<char*> (raw), 4) << std::endl;
}
Serializing a tuple of integers into an array
#include <cstdint>
#include <iostream>
#include <tuple>
int main () {
std::uint8_t raw [4];
const std::tuple<std::uint16_t, std::uint8_t, std::uint8_t> x { 0x4554, 0x53, 0x54 };
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
int main () {
std::uint8_t raw [4];
const std::vector<std::uint16_t> x { 0x4554, 0x5453 };
try {
std::cout.write (reinterpret_cast<char*> (raw), 4) << std::endl;
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}
Pre-Processing raw data with swapped bytes
#include <cstdint>
#include <iostream>
#include <tuple>
int main () {
std::uint8_t raw [4];
std::tuple<std::uint16_t, std::uint8_t, std::uint8_t> x { 0x4554, 0x53, 0x54 };
std::uint16_t raw2 [2];
for (std::size_t i = 0; i < sizeof(raw)/2 ; ++i) {
raw2[i] = static_cast<std::uint16_t> ((uint16_t { raw[i*2] } << 8) | raw [i*2+1]);
}
std::uint16_t raw3 [2];
uSer::deserialize<uSer::ByteOrder::BE> (raw2, raw3);
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>
int main () {
std::uint8_t raw [4];
const std::uint_least32_t x = 0x54534554;
uSer::serialize<uSer::ByteOrder::BE, uSer::Width<32>> (raw, x);
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
int main () {
std::vector<std::uint8_t> raw (4);
const std::uint32_t x = 0x54534554;
try {
std::cout.write (reinterpret_cast<char*> (raw.data ()), 4) << std::endl;
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}
Serializing an integer into a std::vector with FixedSize
#include <cstdint>
#include <iostream>
#include <vector>
int main () {
std::vector<std::uint8_t> raw (4);
const std::uint32_t x = 0x54534554;
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
int main () {
const std::vector<std::uint8_t> raw { 0xBE, 0xBA, 0xFE, 0xC0, 0xEF, 0xBE, 0xAD, 0xDE };
try {
std::uint32_t x;
uSer::deserialize<uSer::RawInfo<uint16_t, 8>> (raw, x, raw.size () / 2);
std::cout << std::hex << x << std::endl;
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>
int main () {
std::vector<std::uint8_t> raw;
const std::uint32_t x = 0x54534554;
uSer::serialize<uSer::RawInfo<std::uint8_t>> (std::back_inserter (raw), x, uSer::infSize);
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>
int main () {
std::uint8_t raw [4];
const std::vector<std::uint16_t> x { 0x4554, 0x5453 };
std::cout.write (reinterpret_cast<char*> (raw), 4) << std::endl;
} else {
}
}
Defining a simple struct for serialization
#include <cstdint>
#include <iostream>
struct A {
std::uint16_t a, b;
std::uint8_t c;
std::uint32_t d;
};
int main () {
std::uint8_t raw [9];
A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
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>
struct A {
std::uint16_t a, b;
std::uint8_t c;
std::uint32_t d;
};
int main () {
std::uint8_t raw [9];
A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
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>
struct A {
std::uint16_t a, b;
std::uint8_t c;
std::uint32_t d;
};
int main () {
std::uint8_t raw [9];
A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
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>
struct A {
std::uint16_t a, b;
std::uint8_t c;
};
int main () {
std::uint8_t raw [9];
A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
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>
struct A {
(std::uint16_t,a,
uSer::AttrNone),
(std::uint16_t,b),
(std::uint8_t,c),
(std::uint32_t,d,
uSer::ByteOrder::BE)
)
};
int main () {
std::uint8_t raw [9];
A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
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>
namespace N {
struct A {
std::uint16_t a, b;
std::uint8_t c;
std::uint32_t d;
};
}
int main () {
std::uint8_t raw [9];
N::A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
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>
namespace N {
struct A {
std::uint16_t a, b;
std::uint8_t c;
std::uint32_t d;
};
}
(b),
(c),
int main () {
std::uint8_t raw [9];
N::A x { 0x1234, 0x4321, 0xAF, 0xDEADBEEF };
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>
int main () {
std::uint8_t raw [2];
std::uint16_t x = 0x765;
uSer::serialize<uSer::ByteOrder::LE, uSer::Width<11>> (raw, x);
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
uSer::serialize<uSer::ByteOrder::BE, uSer::Width<11>> (raw, x);
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>
int main () {
std::uint8_t raw [2];
const std::int16_t x = -291;
uSer::serialize<uSer::SignFormat::TwosComplement> (raw, x);
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
uSer::serialize<uSer::SignFormat::OnesComplement> (raw, x);
for (std::uint8_t r : raw)
std::cout << std::hex << std::setw(2) << "0x" << int { r } << ", ";
std::cout << std::endl;
uSer::serialize<uSer::SignFormat::SignedMagnitude> (raw, x);
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>
struct Color {
std::uint8_t r, g, b;
};
int main () {
std::uint16_t raw [1];
Color c1 { 24, 55, 12 };
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>
struct Color {
std::uint8_t r, b;
};
int main () {
std::uint16_t raw [1];
Color c1 { 24, 12 };
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
int main () {
try {
std::ifstream input ("rawdata.be", std::ios::binary);
if (!input)
throw std::runtime_error ("File could not be opened");
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;
uSer::deserialize<uSer::ByteOrder::BE> (std::istream_iterator<std::uint8_t> (input), x, fSize);
std::ofstream output ("rawdata.le", std::ios::binary);
output.exceptions (std::ofstream::failbit);
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) {
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
class InIter {
public:
InIter (std::istream& os) : m_stream (&os) {}
InIter (InIter&& src) = default;
InIter& operator = (InIter&&) = default;
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;
std::uint8_t operator * () {
std::uint8_t x;
*m_stream >> x;
return x;
}
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;
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;
OutIter& operator * () {
return *this;
}
void operator = (std::uint8_t x) {
*m_stream << x;
}
OutIter& operator ++ () {
return *this;
}
private:
std::ostream* m_stream;
};
int main () {
try {
std::ifstream input ("rawdata.be", std::ios::binary);
if (!input)
throw std::runtime_error ("File could not be opened");
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;
uSer::deserialize<uSer::ByteOrder::BE> (InIter { input }, x, fSize);
std::ofstream output ("rawdata.le", std::ios::binary);
output.exceptions (std::ofstream::failbit);
uSer::serialize<uSer::ByteOrder::LE> (OutIter (output), x, uSer::infSize);
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what () << std::endl;
}
}
Deserializing a vector of integers from an array.
#include <cstdint>
#include <iostream>
#include <vector>
#define USER_EXCEPTIONS
int main () {
const std::uint8_t raw [4] { 0x54, 0x45, 0x53, 0x54 };
std::vector<std::uint16_t> x (2);
try {
std::cout << std::hex << "0x" << x [0] << ", 0x" << x [1] << std::endl;
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}
Defining dynamic data structures with Dyn::Size
#include <cstdint>
#include <iostream>
#define USER_EXCEPTIONS
struct A;
std::size_t g_getSize (const struct A&);
struct Functional {
std::size_t operator () (const struct A&);
} functional;
struct A {
std::size_t m_getSize () const {
return N2;
}
std::uint16_t N1;
std::uint8_t array1 [20];
std::uint16_t N2;
std::uint8_t array2 [20];
std::uint16_t N3;
std::uint8_t array3 [20];
std::uint16_t N4;
std::uint8_t array4 [20];
};
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 () {
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 };
A x;
try {
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;
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}
Defining optional data structures with Dyn::Optional
#include <cstdint>
#include <iostream>
#define USER_EXCEPTIONS
struct A;
bool g_getSize (const struct A&);
struct Functional {
bool operator () (const struct A&);
} functional;
struct A {
bool m_getSize () const {
return N2;
}
std::uint8_t N1;
std::uint8_t v1;
std::uint8_t N2;
std::uint8_t v2;
std::uint8_t N3;
std::uint8_t v3;
std::uint8_t N4;
std::uint8_t v4;
};
bool g_getSize (const struct A& a) {
return a.N3 != 0;
}
bool Functional::operator () (const struct A& a) {
return a.N4 != 0;
}
int main () {
const std::uint8_t raw [6] = { 0x01, 0x01,
0x00,
0x01, 0x2A,
0x00 };
A x;
try {
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;
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
struct A;
void serPost (const uint8_t&, const A&);
struct DeSerPre {
void operator () (uint8_t&, A&);
} deSerPre;
struct A {
void serPre (const uint8_t& a) const {
std::cout << "SerPre\n";
if (a != 42)
}
std::cout << "DeSerPost\n";
}
std::uint8_t a, b, c;
};
void serPost (const uint8_t&, const A&) {
std::cout << "SerPost\n";
}
void DeSerPre::operator () (uint8_t&, A&) {
std::cout << "DeSerPre\n";
}
int main () {
std::uint8_t raw [3];
A x { 42, 2, 3 };
try {
std::cerr << "uSer Error: " << ex.what () << std::endl;
}
}
Using µSer in C projects
Common header file for data structures
struct PacketA {
uint32_t a;
uint16_t b;
int8_t c;
};
typedef struct PacketB_ {
uint8_t N;
struct PacketA packets [8];
} PacketB;
Implementation of the C++ serialization functions
Calling serialization functions from C code
#include <stdio.h>
#include <inttypes.h>
#include "packet.h"
int main () {
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;
uint8_t raw [15];
return 1;
} else {
puts ("Serialization result:");
for (size_t i = 0; i < sizeof(raw); ++i) {
printf ("%02x, ", raw [i]);
}
puts ("");
}
ec = deserializePacketB (raw, &pk, sizeof (raw));
return 1;
} else {
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);
}
}
}