A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/chronoxor/CppSerialization below:

chronoxor/CppSerialization: Performance comparison of the most popular C++ serialization protocols such as Cap'n'Proto, FastBinaryEncoding, Flatbuffers, Protobuf, JSON



C++ Serialization Library provides functionality to serialize/deserialize objects using different protocols such as Cap'n'Proto, FastBinaryEncoding, Flatbuffers, Protobuf, SimpleBinaryEncoding, zpp::bits, JSON.

Performance comparison based on the Domain model with one account, one wallet and three orders total size of 128 bytes:

CppSerialization API reference

Optional:

Linux: install required packages
sudo apt-get install -y binutils-dev uuid-dev
git clone https://github.com/chronoxor/CppSerialization.git
cd CppSerialization
gil update

The first step you should perform to use CppSerialization library is to provide a domain model (aka business objects). Domain model is a set of structures or classes that related to each other and might be aggregated in some hierarchy.

There is an example domain model which describes Account-Balance-Orders relation of some abstract trading platform:

#include <string>
#include <vector>

namespace TradeProto {

enum class OrderSide : uint8_t
{
    BUY,
    SELL
};

enum class OrderType : uint8_t
{
    MARKET,
    LIMIT,
    STOP
};

struct Order
{
    int Id;
    char Symbol[10];
    OrderSide Side;
    OrderType Type;
    double Price;
    double Volume;

    Order() : Order(0, "<\?\?\?>", OrderSide::BUY, OrderType::MARKET, 0.0, 0.0) {}
    Order(int id, const std::string& symbol, OrderSide side, OrderType type, double price, double volume)
    {
        Id = id;
        std::memcpy(Symbol, symbol.c_str(), std::min(symbol.size() + 1, sizeof(Symbol)));
        Side = side;
        Type = type;
        Price = price;
        Volume = volume;
    }
};

struct Balance
{
    char Currency[10];
    double Amount;

    Balance() : Balance("<\?\?\?>", 0.0) {}
    Balance(const std::string& currency, double amount)
    {
        std::memcpy(Currency, currency.c_str(), std::min(currency.size() + 1, sizeof(Currency)));
        Amount = amount;
    }
};

struct Account
{
    int Id;
    std::string Name;
    Balance Wallet;
    std::vector<Order> Orders;

    Account() : Account(0, "<\?\?\?>", "<\?\?\?>", 0.0) {}
    Account(int id, const char* name, const char* currency, double amount) : Wallet(currency, amount)
    {
        Id = id;
        Name = name;
    }
};

} // namespace TradeProto

The next step you should provide serialization methods for the domain model.

Cap'n'Proto serialization

Cap'n'Proto serialization is based on Cap'n'Proto library.

Cap'n'Proto serialization starts with describing a model schema. For our domain model the schema will be the following:

# Unique file ID, generated by 'capnp id'
@0xd4b6e00623bed170;

using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("Trade::capnproto");

enum OrderSide
{
    buy @0;
    sell @1;
}

enum OrderType
{
    market @0;
    limit @1;
    stop @2;
}

struct Order
{
    id @0 : Int32;
    symbol @1 : Text;
    side @2 : OrderSide;
    type @3 : OrderType;
    price @4 : Float64 = 0.0;
    volume @5 : Float64 = 0.0;
}

struct Balance
{
    currency @0 : Text;
    amount @1 : Float64 = 0.0;
}

struct Account
{
    id @0 : Int32;
    name @1 : Text;
    wallet @2 : Balance;
    orders @3 : List(Order);
}
Cap'n'Proto schema compilation

The next step is a schema compilation using 'capnpc' utility which will create a generated code for required programming language.

The following command will create a C++ generated code:

capnp compile -I capnproto/c++/src -oc++ trade.capnp

It is possible to use capnp_generate_cpp() in CMakeLists.txt to generate code using 'cmake' utility:

capnp_generate_cpp(CAPNP_HEADERS CAPNP_SOURCES trade.capnp)

As the result 'trade.capnp.h' and 'trade.capnp.c++' files will be generated.

Cap'n'Proto serialization methods

Finally you should extend your domain model with a Cap'n'Proto serialization methods:

#include "capnp/serialize.h"
#include "capnproto/trade.capnp.h"

#include <algorithm>

namespace TradeProto {

struct Order
{
    ...

    // Cap'n'Proto serialization

    void Serialize(Trade::capnproto::Order::Builder& builder)
    {
        builder.setId(Id);
        builder.setSymbol(Symbol);
        builder.setSide((Trade::capnproto::OrderSide)Side);
        builder.setType((Trade::capnproto::OrderType)Type);
        builder.setPrice(Price);
        builder.setVolume(Volume);
    }

    void Deserialize(const Trade::capnproto::Order::Reader& reader)
    {
        Id = reader.getId();
        std::string symbol = reader.getSymbol();
        std::memcpy(Symbol, symbol.c_str(), std::min(symbol.size() + 1, sizeof(Symbol)));
        Side = (OrderSide)reader.getSide();
        Type = (OrderType)reader.getType();
        Price = reader.getPrice();
        Volume = reader.getVolume();
    }

    ...
};

struct Balance
{
    ...

    // Cap'n'Proto serialization

    void Serialize(Trade::capnproto::Balance::Builder& builder)
    {
        builder.setCurrency(Currency);
        builder.setAmount(Amount);
    }

    void Deserialize(const Trade::capnproto::Balance::Reader& reader)
    {
        std::string currency = reader.getCurrency();
        std::memcpy(Currency, currency.c_str(), std::min(currency.size() + 1, sizeof(Currency)));
        Amount = reader.getAmount();
    }

    ...
};

struct Account
{
    ...

    // Cap'n'Proto serialization

    void Serialize(Trade::capnproto::Account::Builder& builder)
    {
        builder.setId(Id);
        builder.setName(Name);
        auto wallet = builder.initWallet();
        Wallet.Serialize(wallet);
        auto orders = builder.initOrders((unsigned)Orders.size());
        unsigned index = 0;
        for (auto& order : Orders)
        {
            auto o = orders[index++];
            order.Serialize(o);
        }
    }

    void Deserialize(const Trade::capnproto::Account::Reader& reader)
    {
        Id = reader.getId();
        Name = reader.getName().cStr();
        Wallet.Deserialize(reader.getWallet());
        Orders.clear();
        for (auto o : reader.getOrders())
        {
            Order order;
            order.Deserialize(o);
            Orders.emplace_back(order);
        }
    }

    ...
};

} // namespace TradeProto

Here comes the usage example of Cap'n'Proto serialize/deserialize functionality:

#include "../proto/trade.h"

#include <iostream>

int main(int argc, char** argv)
{
    // Create a new account with some orders
    TradeProto::Account account(1, "Test", "USD", 1000);
    account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000));
    account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100));
    account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10));

    // Serialize the account to the Cap'n'Proto buffer
    capnp::MallocMessageBuilder output;
    Trade::capnproto::Account::Builder builder = output.initRoot<Trade::capnproto::Account>();
    account.Serialize(builder);
    kj::VectorOutputStream buffer;
    writeMessage(buffer, output);

    // Show original and Cap'n'Proto serialized sizes
    std::cout << "Original size: " << account.size() << std::endl;
    std::cout << "Cap'n'Proto size: " << buffer.getArray().size() << std::endl;

    // Deserialize the account from the Cap'n'Proto buffer
    kj::ArrayInputStream array(buffer.getArray());
    capnp::InputStreamMessageReader input(array);
    TradeProto::Account deserialized;
    deserialized.Deserialize(input.getRoot<Trade::capnproto::Account>());

    // Show account content
    std::cout << std::endl;
    std::cout << "Account.Id = " << deserialized.Id << std::endl;
    std::cout << "Account.Name = " << deserialized.Name << std::endl;
    std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl;
    std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl;
    for (auto& order : deserialized.Orders)
    {
        std::cout << "Account.Order => Id: " << order.Id
            << ", Symbol: " << order.Symbol
            << ", Side: " << (int)order.Side
            << ", Type: " << (int)order.Type
            << ", Price: " << order.Price
            << ", Volume: " << order.Volume
            << std::endl;
    }

    return 0;
}

Output of the example is the following:

Original size: 128
Cap'n'Proto size: 208

Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10

Cap'n'Proto serialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.136 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:05:35 2025
UTC timestamp: Wed Jul 16 17:05:35 2025
===============================================================================
Benchmark: Cap'n'Proto-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: Cap'n'Proto-Serialize
Average time: 247 ns/op
Minimal time: 244 ns/op
Maximal time: 254 ns/op
Total time: 4.762 s
Total operations: 19242126
Total bytes: 4.484 GiB
Operations throughput: 4040127 ops/s
Bytes throughput: 961.717 MiB/s
Custom values:
        MessageSize: 208
        OriginalSize: 128
===============================================================================

Cap'n'Proto deserialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.116 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:06:37 2025
UTC timestamp: Wed Jul 16 17:06:37 2025
===============================================================================
Benchmark: Cap'n'Proto-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: Cap'n'Proto-Deserialize
Average time: 184 ns/op
Minimal time: 183 ns/op
Maximal time: 184 ns/op
Total time: 4.959 s
Total operations: 26913647
Total bytes: 6.262 GiB
Operations throughput: 5426756 ops/s
Bytes throughput: 1.267 GiB/s
Custom values:
        MessageSize: 208
        OriginalSize: 128
===============================================================================
FastBinaryEncoding serialization

FastBinaryEncoding serialization is based on FastBinaryEncoding library.

FastBinaryEncoding schema

FastBinaryEncoding serialization starts with describing a model schema. For our domain model the schema will be the following:

package trade

enum OrderSide : byte
{
    buy;
    sell;
}

enum OrderType : byte
{
    market;
    limit;
    stop;
}

struct Order
{
    [key] int32 id;
    string symbol;
    OrderSide side;
    OrderType type;
    double price = 0.0;
    double volume = 0.0;
}

struct Balance
{
    [key] string currency;
    double amount = 0.0;
}

struct Account
{
    [key] int32 id;
    string name;
    Balance wallet;
    Order[] orders;
}
FastBinaryEncoding schema compilation

The next step is a schema compilation using 'fbec' utility which will create a generated code for required programming language.

The following command will create a C++ generated code:

fbec --cpp --input=trade.fbe --output=.

It is possible to use add_custom_command() in CMakeLists.txt to generate code using 'cmake' utility:

add_custom_command(TARGET example POST_BUILD COMMAND fbec --cpp --input=trade.fbe --output=.)

As the result 'fbe.h' and 'trade.h' files will be generated.

FastBinaryEncoding serialization methods

Finally you should extend your domain model with a FastBinaryEncoding serialization methods:

#include "fbe/trade_models.h"

#include <algorithm>

namespace TradeProto {

struct Order
{
    ...

    // FastBinaryEncoding serialization

    template <class TBuffer>
    void Serialize(FBE::FieldModel<TBuffer, trade::Order>& model)
    {
        size_t model_begin = model.set_begin();
        model.id.set(Id);
        model.symbol.set(Symbol);
        model.side.set((trade::OrderSide)Side);
        model.type.set((trade::OrderType)Type);
        model.price.set(Price);
        model.volume.set(Volume);
        model.set_end(model_begin);
    }

    template <class TBuffer>
    void Deserialize(const FBE::FieldModel<TBuffer, trade::Order>& model)
    {
        size_t model_begin = model.get_begin();
        model.id.get(Id);
        model.symbol.get(Symbol);
        trade::OrderSide side;
        model.side.get(side);
        Side = (OrderSide)side;
        trade::OrderType type;
        model.type.get(type);
        Type = (OrderType)type;
        model.price.get(Price);
        model.volume.get(Volume);
        model.get_end(model_begin);
    }

    ...
};

struct Balance
{
    ...

    // FastBinaryEncoding serialization

    template <class TBuffer>
    void Serialize(FBE::FieldModel<TBuffer, trade::Balance>& model)
    {
        size_t model_begin = model.set_begin();
        model.currency.set(Currency);
        model.amount.set(Amount);
        model.set_end(model_begin);
    }

    template <class TBuffer>
    void Deserialize(const FBE::FieldModel<TBuffer, trade::Balance>& model)
    {
        size_t model_begin = model.get_begin();
        model.currency.get(Currency);
        model.amount.get(Amount);
        model.get_end(model_begin);
    }

    ...
};

struct Account
{
    ...

    // FastBinaryEncoding serialization

    template <class TBuffer>
    void Serialize(FBE::FieldModel<TBuffer, trade::Account>& model)
    {
        size_t model_begin = model.set_begin();
        model.id.set(Id);
        model.name.set(Name);
        Wallet.Serialize(model.wallet);
        auto order_model = model.orders.resize(Orders.size());
        for (auto& order : Orders)
        {
            order.Serialize(order_model);
            order_model.fbe_shift(order_model.fbe_size());
        }
        model.set_end(model_begin);
    }

    template <class TBuffer>
    void Deserialize(const FBE::FieldModel<TBuffer, trade::Account>& model)
    {
        size_t model_begin = model.get_begin();
        model.id.get(Id);
        model.name.get(Name);
        Wallet.Deserialize(model.wallet);
        Orders.clear();
        for (size_t i = 0; i < model.orders.size(); ++i)
        {
            Order order;
            order.Deserialize(model.orders[i]);
            Orders.emplace_back(order);
        }
        model.get_end(model_begin);
    }

    ...
};

} // namespace TradeProto
FastBinaryEncoding example

Here comes the usage example of FastBinaryEncoding serialize/deserialize functionality:

#include "../proto/trade.h"

#include <iostream>

int main(int argc, char** argv)
{
    // Create a new account with some orders
    TradeProto::Account account(1, "Test", "USD", 1000);
    account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000));
    account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100));
    account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10));

    // Serialize the account to the FBE buffer
    FBE::trade::AccountModel<FBE::WriteBuffer> writer;
    size_t model_begin = writer.create_begin();
    account.Serialize(writer.model);
    size_t serialized = writer.create_end(model_begin);
    assert(writer.verify() && "Model is broken!");

    // Show original and FBE serialized sizes
    std::cout << "Original size: " << account.size() << std::endl;
    std::cout << "FBE size: " << serialized << std::endl;

    // Deserialize the account from the FBE buffer
    TradeProto::Account deserialized;
    FBE::trade::AccountModel<FBE::ReadBuffer> reader;
    reader.attach(writer.buffer());
    assert(reader.verify() && "Model is broken!");
    deserialized.Deserialize(reader.model);

    // Show account content
    std::cout << std::endl;
    std::cout << "Account.Id = " << deserialized.Id << std::endl;
    std::cout << "Account.Name = " << deserialized.Name << std::endl;
    std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl;
    std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl;
    for (auto& order : deserialized.Orders)
    {
        std::cout << "Account.Order => Id: " << order.Id
            << ", Symbol: " << order.Symbol
            << ", Side: " << (int)order.Side
            << ", Type: " << (int)order.Type
            << ", Price: " << order.Price
            << ", Volume: " << order.Volume
            << std::endl;
    }

    return 0;
}

Output of the example is the following:

Original size: 128
FBE size: 234

Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10
FastBinaryEncoding performance

FastBinaryEncoding serialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.179 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:08:14 2025
UTC timestamp: Wed Jul 16 17:08:14 2025
===============================================================================
Benchmark: FastBinaryEncoding-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: FastBinaryEncoding-Serialize
Average time: 77 ns/op
Minimal time: 77 ns/op
Maximal time: 81 ns/op
Total time: 5.204 s
Total operations: 66877702
Total bytes: 17.501 GiB
Operations throughput: 12849579 ops/s
Bytes throughput: 3.369 GiB/s
Custom values:
        MessageSize: 234
        OriginalSize: 128
===============================================================================

FastBinaryEncoding deserialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.194 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:10:03 2025
UTC timestamp: Wed Jul 16 17:10:03 2025
===============================================================================
Benchmark: FastBinaryEncoding-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: FastBinaryEncoding-Deserialize
Average time: 84 ns/op
Minimal time: 82 ns/op
Maximal time: 85 ns/op
Total time: 5.271 s
Total operations: 62074518
Total bytes: 16.239 GiB
Operations throughput: 11775531 ops/s
Bytes throughput: 3.081 GiB/s
Custom values:
        MessageSize: 234
        OriginalSize: 128
===============================================================================
FlatBuffers serialization

FlatBuffers serialization is based on FlatBuffers library.

FlatBuffers serialization starts with describing a model schema. For our domain model the schema will be the following:

namespace Trade.flatbuf;

enum OrderSide : byte
{
    buy,
    sell
}

enum OrderType : byte
{
    market,
    limit,
    stop
}

table Order
{
    id : int;
    symbol : string;
    side : OrderSide;
    type : OrderType;
    price : double = 0.0;
    volume : double = 0.0;
}

table Balance
{
    currency : string;
    amount : double = 0.0;
}

table Account
{
    id : int;
    name : string;
    wallet : Balance;
    orders : [Order];
}

root_type Account;
FlatBuffers schema compilation

The next step is a schema compilation using 'flatc' utility which will create a generated code for required programming language.

The following command will create a C++ generated code:

flatc --cpp --scoped-enums -o . trade.fbs

It is possible to use add_custom_command() in CMakeLists.txt to generate code using 'cmake' utility:

add_custom_command(TARGET example POST_BUILD COMMAND flatc --cpp --scoped-enums -o . trade.fbs)

As the result 'domain_generated.h' file will be generated.

FlatBuffers serialization methods

Finally you should extend your domain model with a FlatBuffers serialization methods:

#include "flatbuffers/trade_generated.h"

#include <algorithm>

namespace TradeProto {

struct Order
{
    ...

    // FlatBuffers serialization

    flatbuffers::Offset<Trade::flatbuf::Order> Serialize(flatbuffers::FlatBufferBuilder& builder)
    {
        return Trade::flatbuf::CreateOrderDirect(builder, Id, Symbol, (Trade::flatbuf::OrderSide)Side, (Trade::flatbuf::OrderType)Type, Price, Volume);
    }

    void Deserialize(const Trade::flatbuf::Order& value)
    {
        Id = value.id();
        std::string symbol = value.symbol()->str();
        std::memcpy(Symbol, symbol.c_str(), std::min(symbol.size() + 1, sizeof(Symbol)));
        Side = (OrderSide)value.side();
        Type = (OrderType)value.type();
        Price = value.price();
        Volume = value.volume();
    }

    ...
};

struct Balance
{
    ...

    // FlatBuffers serialization

    flatbuffers::Offset<Trade::flatbuf::Balance> Serialize(flatbuffers::FlatBufferBuilder& builder)
    {
        return Trade::flatbuf::CreateBalanceDirect(builder, Currency, Amount);
    }

    void Deserialize(const Trade::flatbuf::Balance& value)
    {
        std::string currency = value.currency()->str();
        std::memcpy(Currency, currency.c_str(), std::min(currency.size() + 1, sizeof(Currency)));
        Amount = value.amount();
    }

    ...
};

struct Account
{
    ...

    // FlatBuffers serialization

    flatbuffers::Offset<Trade::flatbuf::Account> Serialize(flatbuffers::FlatBufferBuilder& builder)
    {
        auto wallet = Wallet.Serialize(builder);
        std::vector<flatbuffers::Offset<Trade::flatbuf::Order>> orders;
        for (auto& order : Orders)
            orders.emplace_back(order.Serialize(builder));
        return Trade::flatbuf::CreateAccountDirect(builder, Id, Name.c_str(), wallet, &orders);
    }

    void Deserialize(const Trade::flatbuf::Account& value)
    {
        Id = value.id();
        Name = value.name()->str();
        Wallet.Deserialize(*value.wallet());
        Orders.clear();
        for (auto o : *value.orders())
        {
            Order order;
            order.Deserialize(*o);
            Orders.emplace_back(order);
        }
    }

    ...
};

} // namespace TradeProto

Here comes the usage example of FlatBuffers serialize/deserialize functionality:

#include "../proto/trade.h"

#include <iostream>

int main(int argc, char** argv)
{
    // Create a new account with some orders
    TradeProto::Account account(1, "Test", "USD", 1000);
    account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000));
    account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100));
    account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10));

    // Serialize the account to the FlatBuffer buffer
    flatbuffers::FlatBufferBuilder builder;
    builder.Finish(account.Serialize(builder));

    // Show original and FlatBuffer serialized sizes
    std::cout << "Original size: " << account.size() << std::endl;
    std::cout << "FlatBuffer size: " << builder.GetSize() << std::endl;

    // Deserialize the account from the FlatBuffer buffer
    TradeProto::Account deserialized;
    deserialized.Deserialize(*Trade::flatbuf::GetAccount(builder.GetBufferPointer()));

    // Show account content
    std::cout << std::endl;
    std::cout << "Account.Id = " << deserialized.Id << std::endl;
    std::cout << "Account.Name = " << deserialized.Name << std::endl;
    std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl;
    std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl;
    for (auto& order : deserialized.Orders)
    {
        std::cout << "Account.Order => Id: " << order.Id
            << ", Symbol: " << order.Symbol
            << ", Side: " << (int)order.Side
            << ", Type: " << (int)order.Type
            << ", Price: " << order.Price
            << ", Volume: " << order.Volume
            << std::endl;
    }

    return 0;
}

Output of the example is the following:

Original size: 128
FlatBuffer size: 280

Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10

FlatBuffers serialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.270 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:11:16 2025
UTC timestamp: Wed Jul 16 17:11:16 2025
===============================================================================
Benchmark: FlatBuffers-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: FlatBuffers-Serialize
Average time: 272 ns/op
Minimal time: 272 ns/op
Maximal time: 273 ns/op
Total time: 4.947 s
Total operations: 18131562
Total bytes: 5.689 GiB
Operations throughput: 3664742 ops/s
Bytes throughput: 1.150 GiB/s
Custom values:
        MessageSize: 280
        OriginalSize: 128
===============================================================================

FlatBuffers deserialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.323 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:13:23 2025
UTC timestamp: Wed Jul 16 17:13:23 2025
===============================================================================
Benchmark: FlatBuffers-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: FlatBuffers-Deserialize
Average time: 81 ns/op
Minimal time: 77 ns/op
Maximal time: 81 ns/op
Total time: 5.320 s
Total operations: 65603873
Total bytes: 20.541 GiB
Operations throughput: 12329743 ops/s
Bytes throughput: 3.878 GiB/s
Custom values:
        MessageSize: 280
        OriginalSize: 128
===============================================================================

Protobuf serialization is based on Protobuf library.

Protobuf serialization starts with describing a model schema. For our domain model the schema will be the following:

syntax = "proto3";
package Trade.protobuf;

enum OrderSide
{
    buy = 0;
    sell = 1;
}

enum OrderType
{
    market = 0;
    limit = 1;
    stop = 2;
}

message Order
{
    int32 id = 1;
    string symbol = 2;
    OrderSide side = 3;
    OrderType type = 4;
    double price = 5;
    double volume = 6;
}

message Balance
{
    string currency = 1;
    double amount = 2;
}

message Account
{
    int32 id = 1;
    string name = 2;
    Balance wallet = 3;
    repeated Order orders = 4;
}
Protobuf schema compilation

The next step is a schema compilation using 'protoc' utility which will create a generated code for required programming language.

The following command will create a C++ generated code:

protoc --proto_path=. --cpp_out=. trade.proto

It is possible to use add_custom_command() in CMakeLists.txt to generate code using 'cmake' utility:

add_custom_command(TARGET example POST_BUILD COMMAND protoc --proto_path=. --cpp_out=. trade.proto)

As the result 'trade.pb.h' and 'trade.pb.cc' files will be generated.

Protobuf serialization methods

Finally you should extend your domain model with a FlatBuffers serialization methods:

#include "protobuf/trade.pb.h"

#include <algorithm>

namespace TradeProto {

struct Order
{
    ...

    // Protobuf serialization

    Trade::protobuf::Order& Serialize(Trade::protobuf::Order& value)
    {
        value.set_id(Id);
        value.set_symbol(Symbol);
        value.set_side((Trade::protobuf::OrderSide)Side);
        value.set_type((Trade::protobuf::OrderType)Type);
        value.set_price(Price);
        value.set_volume(Volume);
        return value;
    }

    void Deserialize(const Trade::protobuf::Order& value)
    {
        Id = value.id();
        std::string symbol = value.symbol();
        std::memcpy(Symbol, symbol.c_str(), std::min(symbol.size() + 1, sizeof(Symbol)));
        Side = (OrderSide)value.side();
        Type = (OrderType)value.type();
        Price = value.price();
        Volume = value.volume();
    }

    ...
};

struct Balance
{
    ...

    // Protobuf serialization

    Trade::protobuf::Balance& Serialize(Trade::protobuf::Balance& value)
    {
        value.set_currency(Currency);
        value.set_amount(Amount);
        return value;
    }

    void Deserialize(const Trade::protobuf::Balance& value)
    {
        std::string currency = value.currency();
        std::memcpy(Currency, currency.c_str(), std::min(currency.size() + 1, sizeof(Currency)));
        Amount = value.amount();
    }

    ...
};

struct Account
{
    ...

    // Protobuf serialization

    Trade::protobuf::Account& Serialize(Trade::protobuf::Account& value)
    {
        value.set_id(id);
        value.set_name(Name);
        value.set_allocated_wallet(&Wallet.Serialize(*value.wallet().New(value.GetArena())));
        for (auto& order : Orders)
            order.Serialize(*value.add_orders());
        return value;
    }

    void Deserialize(const Trade::protobuf::Account& value)
    {
        Id = value.id();
        Name = value.name();
        Wallet.Deserialize(value.wallet());
        Orders.clear();
        for (int i = 0; i < value.orders_size(); ++i)
        {
            Order order;
            order.Deserialize(value.orders(i));
            Orders.emplace_back(order);
        }
    }

    ...
};

} // namespace TradeProto

Here comes the usage example of Protobuf serialize/deserialize functionality:

#include "../proto/trade.h"

#include <iostream>

int main(int argc, char** argv)
{
    // Create a new account with some orders
    TradeProto::Account account(1, "Test", "USD", 1000);
    account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000));
    account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100));
    account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10));

    // Serialize the account to the Protobuf buffer
    Trade::protobuf::Account output;
    account.Serialize(output);
    auto buffer = output.SerializeAsString();

    // Show original and Protobuf serialized sizes
    std::cout << "Original size: " << account.size() << std::endl;
    std::cout << "Protobuf size: " << buffer.size() << std::endl;

    // Deserialize the account from the Protobuf buffer
    Trade::protobuf::Account input;
    input.ParseFromString(buffer);
    TradeProto::Account deserialized;
    deserialized.Deserialize(input);

    // Show account content
    std::cout << std::endl;
    std::cout << "Account.Id = " << deserialized.Id << std::endl;
    std::cout << "Account.Name = " << deserialized.Name << std::endl;
    std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl;
    std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl;
    for (auto& order : deserialized.Orders)
    {
        std::cout << "Account.Order => Id: " << order.Id
            << ", Symbol: " << order.Symbol
            << ", Side: " << (int)order.Side
            << ", Type: " << (int)order.Type
            << ", Price: " << order.Price
            << ", Volume: " << order.Volume
            << std::endl;
    }

    // Delete all global objects allocated by Protobuf
    google::protobuf::ShutdownProtobufLibrary();

    return 0;
}

Output of the example is the following:

Original size: 128
Protobuf size: 120

Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10

Protobuf serialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.339 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:15:26 2025
UTC timestamp: Wed Jul 16 17:15:26 2025
===============================================================================
Benchmark: Protobuf-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: Protobuf-Serialize
Average time: 322 ns/op
Minimal time: 322 ns/op
Maximal time: 323 ns/op
Total time: 4.989 s
Total operations: 15458464
Total bytes: 2.074 GiB
Operations throughput: 3098338 ops/s
Bytes throughput: 425.503 MiB/s
Custom values:
        MessageSize: 120
        OriginalSize: 128
===============================================================================

Protobuf deserialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.248 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:16:37 2025
UTC timestamp: Wed Jul 16 17:16:37 2025
===============================================================================
Benchmark: Protobuf-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: Protobuf-Deserialize
Average time: 351 ns/op
Minimal time: 351 ns/op
Maximal time: 352 ns/op
Total time: 4.959 s
Total operations: 14111610
Total bytes: 1.913 GiB
Operations throughput: 2845506 ops/s
Bytes throughput: 390.789 MiB/s
Custom values:
        MessageSize: 120
        OriginalSize: 128
===============================================================================
SimpleBinaryEncoding serialization

SimpleBinaryEncoding serialization is based on SimpleBinaryEncoding library.

SimpleBinaryEncoding schema

SimpleBinaryEncoding serialization starts with describing a model schema. For our domain model the schema will be the following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sbe:messageSchema xmlns:sbe="http://fixprotocol.io/2016/sbe" package="sbe" id="1" version="1" semanticVersion="5.2" description="Trade schema" byteOrder="littleEndian">
    <types>
        <composite name="messageHeader" description="Message identifiers and length of message root">
            <type name="blockLength" primitiveType="uint16"/>
            <type name="templateId" primitiveType="uint16"/>
            <type name="schemaId" primitiveType="uint16"/>
            <type name="version" primitiveType="uint16"/>
        </composite>
        <composite name="groupSizeEncoding" description="Repeating group dimensions">
            <type name="blockLength" primitiveType="uint16"/>
            <type name="numInGroup" primitiveType="uint16"/>
        </composite>
        <composite name="varStringEncoding">
            <type name="length" primitiveType="uint32" maxValue="1073741824"/>
            <type name="varData" primitiveType="uint8" length="0" characterEncoding="UTF-8"/>
        </composite>
    </types>
    <types>
        <enum name="OrderSide" encodingType="uint8">
            <validValue name="buy">0</validValue>
            <validValue name="sell">1</validValue>
        </enum>
        <enum name="OrderType" encodingType="uint8">
            <validValue name="market">0</validValue>
            <validValue name="limit">1</validValue>
            <validValue name="stop">2</validValue>
        </enum>
        <composite name="Order">
            <type name="id" primitiveType="int32"/>
            <type name="symbol" primitiveType="char" length="10" characterEncoding="UTF-8"/>
            <ref name="side" type="OrderSide"/>
            <ref name="type" type="OrderType"/>
            <type name="price" primitiveType="double"/>
            <type name="volume" primitiveType="double"/>
        </composite>
        <composite name="Balance">
            <type name="currency" primitiveType="char" length="10" characterEncoding="UTF-8"/>
            <type name="amount" primitiveType="double"/>
        </composite>
        <type name="AccountId" primitiveType="int32"/>
    </types>
    <sbe:message name="Account" id="1">
        <field name="id" id="1" type="AccountId"/>
        <field name="wallet" id="2" type="Balance"/>
        <group name="orders" id="3" dimensionType="groupSizeEncoding">
            <field name="order" id="4" type="Order"/>
        </group>
        <data name="name" id="5" type="varStringEncoding"/>
    </sbe:message>
</sbe:messageSchema>
SimpleBinaryEncoding schema compilation

The next step is a schema compilation using 'sbe' utility which will create a generated code for required programming language.

The following command will create a C++ generated code:

java -Dsbe.target.language=cpp -jar sbe-all-1.29.0.jar trade.sbe.xml

As the result required C++ header files will be generated.

SimpleBinaryEncoding serialization methods

Finally you should extend your domain model with a SimpleBinaryEncoding serialization methods:

#include "fbe/trade_models.h"

#include <algorithm>

namespace TradeProto {

struct Order
{
    ...

    // SimpleBinaryEncoding serialization

    void Serialize(sbe::Order& model)
    {
        model.id(Id);
        model.putSymbol(Symbol);
        model.side((sbe::OrderSide::Value)Side);
        model.type((sbe::OrderType::Value)Type);
        model.price(Price);
        model.volume(Volume);
    }

    void Deserialize(sbe::Order& model)
    {
        Id = model.id();
        model.getSymbol(Symbol, sizeof(Symbol));
        Side = (OrderSide)model.side();
        Type = (OrderType)model.type();
        Price = model.price();
        Volume = model.volume();
    }

    ...
};

struct Balance
{
    ...

    // SimpleBinaryEncoding serialization

    void Serialize(sbe::Balance& model)
    {
        model.putCurrency(Currency);
        model.amount(Amount);
    }

    void Deserialize(sbe::Balance& model)
    {
        model.getCurrency(Currency, sizeof(Currency));
        Amount = model.amount();
    }

    ...
};

struct Account
{
    ...

    // SimpleBinaryEncoding serialization

    void Serialize(sbe::Account& model)
    {
        model.id(Id);
        model.putName(Name);
        Wallet.Serialize(model.wallet());
        auto orders = model.ordersCount((uint16_t)Orders.size());
        for (auto& order : Orders)
            order.Serialize(orders.next().order());
    }

    void Deserialize(sbe::Account& model)
    {
        Id = model.id();
        Name = model.getNameAsString();
        Wallet.Deserialize(model.wallet());
        Orders.clear();
        auto orders = model.orders();
        for (int i = 0; i < orders.count(); ++i)
        {
            Order order;
            order.Deserialize(orders.next().order());
            Orders.emplace_back(order);
        }
    }

    ...
};

} // namespace TradeProto
SimpleBinaryEncoding example

Here comes the usage example of SimpleBinaryEncoding serialize/deserialize functionality:

#include "../proto/trade.h"

#include <iostream>

int main(int argc, char** argv)
{
    // Create a new account with some orders
    TradeProto::Account account(1, "Test", "USD", 1000);
    account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000));
    account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100));
    account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10));

    // Serialize the account to the SBE buffer
    char buffer[1024];
    sbe::MessageHeader header;
    header.wrap(buffer, 0, 1, sizeof(buffer))
       .blockLength(sbe::Account::sbeBlockLength())
       .templateId(sbe::Account::sbeTemplateId())
       .schemaId(sbe::Account::sbeSchemaId())
       .version(sbe::Account::sbeSchemaVersion());
    sbe::Account message;
    message.wrapForEncode(buffer, header.encodedLength(), sizeof(buffer));
    account.Serialize(message);

    // Show original and SBE serialized sizes
    std::cout << "Original size: " << account.size() << std::endl;
    std::cout << "SBE size: " << header.encodedLength() + message.encodedLength() << std::endl;

    // Deserialize the account from the SBE buffer
    header.wrap(buffer, 0, 1, sizeof(buffer));
    int actingVersion = header.version();
    int actingBlockLength = header.blockLength();
    message.wrapForDecode(buffer, header.encodedLength(), actingBlockLength, actingVersion, sizeof(buffer));
    TradeProto::Account deserialized;
    deserialized.Deserialize(message);

    // Show account content
    std::cout << std::endl;
    std::cout << "Account.Id = " << deserialized.Id << std::endl;
    std::cout << "Account.Name = " << deserialized.Name << std::endl;
    std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl;
    std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl;
    for (auto& order : deserialized.Orders)
    {
        std::cout << "Account.Order => Id: " << order.Id
            << ", Symbol: " << order.Symbol
            << ", Side: " << (int)order.Side
            << ", Type: " << (int)order.Type
            << ", Price: " << order.Price
            << ", Volume: " << order.Volume
            << std::endl;
    }

    return 0;
}

Output of the example is the following:

Original size: 128
SBE size: 138

Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10
SimpleBinaryEncoding performance

SimpleBinaryEncoding serialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.311 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:17:49 2025
UTC timestamp: Wed Jul 16 17:17:49 2025
===============================================================================
Benchmark: SimpleBinaryEncoding-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: SimpleBinaryEncoding-Serialize
Average time: 35 ns/op
Minimal time: 35 ns/op
Maximal time: 35 ns/op
Total time: 5.926 s
Total operations: 168709597
Total bytes: 26.020 GiB
Operations throughput: 28467195 ops/s
Bytes throughput: 4.399 GiB/s
Custom values:
        MessageSize: 138
        OriginalSize: 128
===============================================================================

SimpleBinaryEncoding deserialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.261 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:19:22 2025
UTC timestamp: Wed Jul 16 17:19:22 2025
===============================================================================
Benchmark: SimpleBinaryEncoding-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: SimpleBinaryEncoding-Deserialize
Average time: 52 ns/op
Minimal time: 52 ns/op
Maximal time: 52 ns/op
Total time: 5.432 s
Total operations: 104359134
Total bytes: 16.097 GiB
Operations throughput: 19211002 ops/s
Bytes throughput: 2.985 GiB/s
Custom values:
        MessageSize: 138
        OriginalSize: 128
===============================================================================

zpp::bits serialization is based on zpp::bits library.

zpp::bits serialization methods

Finally you should extend your domain model with a zpp::bits serialization methods:

#include "fbe/trade_models.h"

#include <algorithm>

namespace TradeProto {

struct Order
{
    ...

    // zpp::bits serialization

    using serialize = zpp::bits::members<6>;

    ...
};

struct Balance
{
    ...

    // zpp::bits serialization

    using serialize = zpp::bits::members<2>;

    ...
};

struct Account
{
    ...

    // zpp::bits serialization

    using serialize = zpp::bits::members<4>;

    ...
};

} // namespace TradeProto

Here comes the usage example of zpp::bits serialize/deserialize functionality:

#include "../proto/trade.h"

#include <iostream>

int main(int argc, char** argv)
{
    // Create a new account with some orders
    TradeProto::Account account(1, "Test", "USD", 1000);
    account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000));
    account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100));
    account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10));

    // Serialize the account to the zpp::bits buffer
    auto [buffer, out] = zpp::bits::data_out();
    (void) out(account);

    // Show original and zpp::bits serialized sizes
    std::cout << "Original size: " << account.size() << std::endl;
    std::cout << "zpp::bits size: " << buffer.size() << std::endl;

    // Deserialize the account from the zpp::bits buffer
    TradeProto::Account deserialized;
    (void) zpp::bits::in{buffer}(deserialized);

    // Show account content
    std::cout << std::endl;
    std::cout << "Account.Id = " << deserialized.Id << std::endl;
    std::cout << "Account.Name = " << deserialized.Name << std::endl;
    std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl;
    std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl;
    for (const auto& order : deserialized.Orders)
    {
        std::cout << "Account.Order => Id: " << order.Id
            << ", Symbol: " << order.Symbol
            << ", Side: " << (int)order.Side
            << ", Type: " << (int)order.Type
            << ", Price: " << order.Price
            << ", Volume: " << order.Volume
            << std::endl;
    }

    return 0;
}

Output of the example is the following:

Original size: 128
zpp::bits size: 130

Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10

zpp::bits serialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.142 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:38:54 2025
UTC timestamp: Wed Jul 16 17:38:54 2025
===============================================================================
Benchmark: zpp::bits-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: zpp::bits-Serialize
Average time: 34 ns/op
Minimal time: 33 ns/op
Maximal time: 35 ns/op
Total time: 5.790 s
Total operations: 168487238
Total bytes: 24.490 GiB
Operations throughput: 29095978 ops/s
Bytes throughput: 4.232 GiB/s
Custom values:
        MessageSize: 130
        OriginalSize: 128
===============================================================================

zpp::bits deserialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.159 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:39:52 2025
UTC timestamp: Wed Jul 16 17:39:52 2025
===============================================================================
Benchmark: zpp::bits-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: zpp::bits-Deserialize
Average time: 37 ns/op
Minimal time: 36 ns/op
Maximal time: 37 ns/op
Total time: 5.652 s
Total operations: 152512801
Total bytes: 22.161 GiB
Operations throughput: 26982853 ops/s
Bytes throughput: 3.942 GiB/s
Custom values:
        MessageSize: 130
        OriginalSize: 128
===============================================================================

JSON serialization is based on RapidJSON library.

JSON serialization methods

Finally you should extend your domain model with a JSON serialization methods:

#include "serialization/json/serializer.h"
#include "serialization/json/deserializer.h"

namespace TradeProto {

struct Order
{
    ...

    // JSON serialization

    template<typename OutputStream>
    void Serialize(CppSerialization::JSON::Serializer<OutputStream>& serializer)
    {
        serializer.StartObject();
        serializer.Pair("id", Id);
        serializer.Pair("symbol", Symbol);
        serializer.Pair("side", (int)Side);
        serializer.Pair("type", (int)Type);
        serializer.Pair("price", Price);
        serializer.Pair("volume", Volume);
        serializer.EndObject();
    }

    template<typename JSON>
    void Deserialize(const JSON& json)
    {
        using namespace CppSerialization::JSON;

        Deserializer::Find(json, "id", Id);
        Deserializer::Find(json, "symbol", Symbol);
        int side = 0; Deserializer::Find(json, "side", side); Side = (OrderSide)side;
        int type = 0; Deserializer::Find(json, "type", type); Type = (OrderType)type;
        Deserializer::Find(json, "price", Price);
        Deserializer::Find(json, "volume", Volume);
    }

    ...
};

struct Balance
{
    ...

    // JSON serialization

    template<typename OutputStream>
    void Serialize(CppSerialization::JSON::Serializer<OutputStream>& serializer)
    {
        serializer.StartObject();
        serializer.Pair("currency", Currency);
        serializer.Pair("amount", Amount);
        serializer.EndObject();
    }

    template<typename JSON>
    void Deserialize(const JSON& json)
    {
        using namespace CppSerialization::JSON;

        Deserializer::Find(json, "currency", Currency);
        Deserializer::Find(json, "amount", Amount);
    }

    ...
};

struct Account
{
    ...

    // JSON serialization

    template<typename OutputStream>
    void Serialize(CppSerialization::JSON::Serializer<OutputStream>& serializer)
    {
        serializer.StartObject();
        serializer.Pair("id", Id);
        serializer.Pair("name", Name);
        serializer.Key("wallet");
        Wallet.Serialize(serializer);
        serializer.Key("orders");
        serializer.StartArray();
        for (auto& order : Orders)
            order.Serialize(serializer);
        serializer.EndArray();
        serializer.EndObject();
    }

    template<typename JSON>
    void Deserialize(const JSON& json)
    {
        using namespace CppSerialization::JSON;

        Deserializer::Find(json, "id", Id);
        Deserializer::Find(json, "name", Name);
        Deserializer::FindObject(json, "wallet", [this](const Value::ConstObject& object)
        {
            Wallet.Deserialize(object);
        });
        Orders.clear();
        Deserializer::FindArray(json, "orders", [this](const Value& item)
        {
            Order order;
            order.Deserialize(item);
            Orders.emplace_back(order);
        });
    }

    ...
};

} // namespace TradeProto

Here comes the usage example of JSON serialize/deserialize functionality:

#include "../proto/trade.h"

#include "serialization/json/parser.h"

#include <iostream>

int main(int argc, char** argv)
{
    // Create a new account with some orders
    TradeProto::Account account(1, "Test", "USD", 1000);
    account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000));
    account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100));
    account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10));

    // Serialize the account to the JSON buffer
    CppSerialization::JSON::StringBuffer buffer;
    CppSerialization::JSON::Serializer<CppSerialization::JSON::StringBuffer> serializer(buffer);
    account.Serialize(serializer);

    // Show original and JSON serialized sizes
    std::cout << "Original size: " << account.size() << std::endl;
    std::cout << "JSON content: " << buffer.GetString() << std::endl;
    std::cout << "JSON size: " << buffer.GetSize() << std::endl;

    // Parse JSON string
    CppSerialization::JSON::Document json = CppSerialization::JSON::Parser::Parse(buffer.GetString());

    // Deserialize the account from the JSON buffer
    TradeProto::Account deserialized;
    deserialized.Deserialize(json);

    // Show account content
    std::cout << std::endl;
    std::cout << "Account.Id = " << deserialized.Id << std::endl;
    std::cout << "Account.Name = " << deserialized.Name << std::endl;
    std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl;
    std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl;
    for (auto& order : deserialized.Orders)
    {
        std::cout << "Account.Order => Id: " << order.Id
            << ", Symbol: " << order.Symbol
            << ", Side: " << (int)order.Side
            << ", Type: " << (int)order.Type
            << ", Price: " << order.Price
            << ", Volume: " << order.Volume
            << std::endl;
    }

    return 0;
}

Output of the example is the following:

Original size: 128
JSON content: {"id":1,"name":"Test","wallet":{"currency":"USD","amount":1000.0},"orders":[{"id":1,"symbol":"EURUSD","side":0,"type":0,"price":1.23456,"volume":1000.0},{"id":2,"symbol":"EURUSD","side":1,"type":1,"price":1.0,"volume":100.0},{"id":3,"symbol":"EURUSD","side":0,"type":2,"price":1.5,"volume":10.0}]}
JSON size: 297

Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10

JSON serialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.041 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:29:18 2025
UTC timestamp: Wed Jul 16 17:29:18 2025
===============================================================================
Benchmark: JSON-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: JSON-Serialize
Average time: 696 ns/op
Minimal time: 695 ns/op
Maximal time: 710 ns/op
Total time: 4.932 s
Total operations: 7078563
Total bytes: 2.357 GiB
Operations throughput: 1435099 ops/s
Bytes throughput: 487.794 MiB/s
Custom values:
        MessageSize: 297
        OriginalSize: 128
===============================================================================

JSON document parsing performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.154 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:30:48 2025
UTC timestamp: Wed Jul 16 17:30:48 2025
===============================================================================
Benchmark: JSON-Parse
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: JSON-Parse
Average time: 771 ns/op
Minimal time: 771 ns/op
Maximal time: 779 ns/op
Total time: 4.914 s
Total operations: 6372779
Total bytes: 2.118 GiB
Operations throughput: 1296734 ops/s
Bytes throughput: 440.764 MiB/s
Custom values:
        MessageSize: 297
===============================================================================

JSON deserialization performance of the provided domain model is the following:

===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.240 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:31:50 2025
UTC timestamp: Wed Jul 16 17:31:50 2025
===============================================================================
Benchmark: JSON-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: JSON-Deserialize
Average time: 291 ns/op
Minimal time: 291 ns/op
Maximal time: 292 ns/op
Total time: 4.924 s
Total operations: 16884318
Total bytes: 77.297 MiB
Operations throughput: 3428637 ops/s
Bytes throughput: 15.711 MiB/s
Custom values:
        MessageSize: 297
        OriginalSize: 128
===============================================================================

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4