Backend Development 41 min read

C++ Serialization: Concepts, Tools, and Best Practices

Serialization in C++ bridges in‑memory objects and persistent or network formats, addressing challenges like byte order, data type compatibility, and complex structures, while the article reviews core principles, binary and text formats, major libraries such as Protobuf, Cereal, Cista++, ThorsSerializer, and offers guidance on selecting and using them effectively.

Deepin Linux
Deepin Linux
Deepin Linux
C++ Serialization: Concepts, Tools, and Best Practices

1. Introduction to Serialization

In C++ development, data moves fluidly in memory, but storing or transmitting that data requires serialization to convert objects into a storable or transmittable byte sequence.

1.1 What is Serialization?

Serialization transforms an object's state into a byte stream for storage or network transfer; deserialization restores the object from that stream.

1.2 Why is Serialization Crucial in C++?

It enables persistence of complex structures (e.g., B‑tree nodes in databases) and efficient data exchange in distributed systems such as multiplayer games or e‑commerce services.

2. Core Working Principles

⑴ Serialization Methods

Text formats: JSON, XML

Binary format: Protobuf

⑵ Binary Serialization

Serialization: converting data structures to a binary string.

Deserialization: converting the binary string back to data structures.

序列化后,数据小,传输速度快
序列化、反序列化速度快

⑶ Demonstration

① Basic type serialization/deserialization

int main()
{
    DataStream ds;
    int n =123;
    double d = 23.2;
    string s = "hellow serialization";
    ds << n <

② Composite type serialization/deserialization

int main()
{
    std::vector
v{3,2,1};
    std::map
m;
    m["name"] = "kitty";
    m["phone"] = "12121";
    m["gender"] = "male";
    DataStream ds;
    ds<
v;
    std::map
m;
    ds>>v>>m;
    for(auto it = v.begin();it != v.end();it++)
    {
        std::cout<<*it<
first<<"="<
second<

③ Custom class serialization/deserialization

class A:public Serialization
{ //自定义类的序列化
public:
    A();
    ~A();
    A(const string & name,int age):m_name(name),m_age(age){}
    void show(){ std::cout<
v;
    std::map
m;
    ds>>v>>m;
    // ... same as above
}

3. C++ Serialization Tools

Choosing a tool depends on efficiency, compatibility, ease of use, and scenario.

3.1 Protobuf (Google’s solution)

Binary, language‑neutral, high‑performance. Example .proto definition and usage:

syntax = "proto3";

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;
  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }
  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }
  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}
#include "addressbook.pb.h"
#include
#include
void Serialize() {
  addressbook::AddressBook address_book;
  auto* person = address_book.add_people();
  person->set_name("Alice");
  person->set_id(1);
  person->set_email("[email protected]");
  auto* phone = person->add_phones();
  phone->set_number("123-456-7890");
  phone->set_type(addressbook::Person::MOBILE);
  std::fstream output("addressbook.pb", std::ios::out | std::ios::trunc | std::ios::binary);
  address_book.SerializeToOstream(&output);
}

void Deserialize() {
  addressbook::AddressBook address_book;
  std::fstream input("addressbook.pb", std::ios::in | std::ios::binary);
  address_book.ParseFromIstream(&input);
  for(int i=0;i

3.2 Cereal (C++11 header‑only)

Supports JSON, XML, binary. Example struct and usage:

#include
#include
#include
#include
#include
#include
struct Student {
    std::string name;
    int age;
    float grade;
    template
void serialize(Archive& ar){ ar(name, age, grade); }
};

void SerializeToJson(){
    Student s{"Bob",20,85.5f};
    std::ostringstream oss;
    { cereal::JSONOutputArchive oa(oss); oa(s); }
    std::cout<<"Serialized to JSON: "<

3.3 Cista++ (C++17, zero‑overhead)

Provides non‑intrusive reflection and direct‑to‑file serialization, ideal for large data sets such as GIS or game assets.

3.4 ThorsSerializer (multi‑format)

Supports binary, JSON, YAML, BSON; useful for network messages and logging.

4. Choosing the Right Tool

Consider efficiency (binary formats like Protobuf are smallest and fastest), compatibility (cross‑language support), ease of use (Cereal’s header‑only API), and scenario (Cista++ for massive data, Cereal for internal C++ pipelines, Protobuf for cross‑service communication).

5. Protobuf C++ Usage Guide

4.1 Installation

Download from GitHub and follow README.

4.2 Defining .proto Files

//AppExam.proto
syntax = "proto3";
package App;

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;
  enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; }
  message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; }
  repeated PhoneNumber phone = 4;
}

message AddressBook { repeated Person person = 1; }

4.3 Generating Code

./protoc -I=../../test/protobuf --cpp_out=../../test/protobuf ../../test/protobuf/AppExam.proto

4.4 API Overview

// name
inline bool has_name() const;
inline void clear_name();
inline const ::std::string& name() const;
inline void set_name(const ::std::string& value);
inline void set_name(const char* value);
inline ::std::string* mutable_name();

// id
inline bool has_id() const;
inline void clear_id();
inline int32_t id() const;
inline void set_id(int32_t value);

// serialization
bool SerializeToString(string* output) const;
bool ParseFromString(const string& data);

4.5 Any Message Type

syntax = "proto3";
package App;
import "google/protobuf/any.proto";
message ErrorStatus { repeated google.protobuf.Any details = 1; }
message NetworkErrorDetails { int32 a = 1; int32 b = 2; }
message LocalErrorDetails { int64 x = 1; string y = 2; }
// pack
App::NetworkErrorDetails details;
details.set_a(1);
details.set_b(2);
App::ErrorStatus status;
status.add_details()->PackFrom(details);
std::string str;
status.SerializeToString(&str);

// unpack
App::ErrorStatus status;
status.ParseFromString(str);
for(const auto& d : status.details()){
    if(d.Is
()){
        App::NetworkErrorDetails nd;
        d.UnpackTo(&nd);
        // use nd
    }
}

6. Pitfalls and Solutions

6.1 Data‑type Mismatch

Ensure .proto field types match C++ variable types (e.g., int32 ↔ int32_t, not int64).

6.2 Version Compatibility

When adding fields, keep existing tags unchanged and mark new fields as optional; regenerate code for all services.

6.3 Performance Bottlenecks

Choose high‑performance libraries (Protobuf, Cista++), flatten nested structures, cache serialized blobs, and parallelize serialization in multithreaded environments.

performanceSerializationC++Protobufdata transferCerealCista++
Deepin Linux
Written by

Deepin Linux

Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.