How to do it...

The goal of serialization is to encode any data in a way that can be properly decoded on another system or in another application. The typical obstacles for developers are as follows:

A generic approach to this problem is letting a class define the functions to convert its content into a serialized form and restore an instance of a class from the serialized form.

In our application, we will overload operator<< of the output stream and operator>> of the input stream to serialize and deserialize data respectively:

  1. In your ~/test working directory, create a subdirectory called stream.
  2. Use your favorite text editor to create a stream.cpp file in the stream subdirectory.
  3. Start with the definition of the data structures that you want to serialize:
#include <iostream>
#include <sstream>
#include <list>

struct Point {
int x, y;
};

struct Paths {
Point source;
std::list<Point> destinations;
};
  1. Next, we overload the << and >> operators that are responsible for writing and reading the Point objects into and from a stream respectively. For the Point data type enter the following:
std::ostream& operator<<(std::ostream& o, const Point& p) {
o << p.x << " " << p.y << " ";
return o;
}

std::istream& operator>>(std::istream& is, Point& p) {
is >> p.x;
is >> p.y;
return is;
}
  1. They are followed by the << and >> overloaded operators for the Paths objects:
std::ostream& operator<<(std::ostream& o, const Paths& paths) {
o << paths.source << paths.destinations.size() << " ";
for (const auto& x : paths.destinations) {
o << x;
}
return o;
}

std::istream& operator>>(std::istream& is, Paths& paths) {
size_t size;
is >> paths.source;
is >> size;
for (;size;size--) {
Point tmp;
is >> tmp;
paths.destinations.push_back(tmp);
}
return is;
}
  1. Now, let's wrap everything up in the main function:
int main(int argc, char** argv) {
Paths paths = {{0, 0}, {{1, 1}, {0, 1}, {1, 0}}};

std::stringstream in;
in << paths;
std::string serialized = in.str();
std::cout << "Serialized paths into the string: ["
<< serialized << "]" << std::endl;

std::stringstream out(serialized);
Paths paths2;
out >> paths2;
std::cout << "Original: " << paths.destinations.size()
<< " destinations" << std::endl;
std::cout << "Restored: " << paths2.destinations.size()
<< " destinations" << std::endl;

return 0;
}
  1. Finally, create a CMakeLists.txt file containing the build rules for our program:
cmake_minimum_required(VERSION 3.5.1)
project(stream)
add_executable(stream stream.cpp)

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

SET(CMAKE_CXX_FLAGS "--std=c++11")

set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabi-g++)

You can now build and run the application.