simple-protobuf

June 17, 2026 ยท View on GitHub

License: MIT C++20 CMake Linux-build Windows-build Mac-build Library-coverage

Lightweight C++ protobuf & JSON serialization library

  • Where simplicity meets usability
  • Without protoc and without Google toolchain dependency

Usage

  1. Define your data in standard .proto files.
  2. Generate clean, native C++ structs.
  3. Serialize/deserialize to protobuf (GPB-wire-compatible) and JSON (GPB-compatible) with minimal effort.

Example

// proto/person.proto, hand written
package PhoneBook;

message Person {
  optional string name = 1;
  optional int32 id = 2;  // Unique ID number for this person.
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1; // phone number is always required
    optional PhoneType type = 2;
  }

  // all registered phones
  repeated PhoneNumber phones = 4;
}
# CMakeLists.txt, hand written
add_subdirectory(external/simple-protobuf) # or FetchContent
add_executable(myapp main.cpp proto/person.proto)
spb_protobuf_generate(TARGET myapp)
// proto/person.pb.h, generated
namespace PhoneBook
{
struct Person {
    enum class PhoneType : int32_t {
        MOBILE = 0,
        HOME   = 1,
        WORK   = 2,
    };
    struct PhoneNumber {
        // phone number is always required
        std::string number;
        std::optional<PhoneType> type;
    };
    std::optional<std::string> name;
    // Unique ID number for this person.
    std::optional<int32_t> id;
    std::optional<std::string> email;
    // all registered phones
    std::vector<PhoneNumber> phones;
};
} // namespace PhoneBook
// main.cpp, hand written
#include <iostream>
#include <proto/person.pb.h>   // <- generated

int main() {
    const auto john = phonebook::Person{
        .name   = "John Doe",
        .id     = 1234,
        .email  = "john@example.com",
        .phones = {{.number = "123456789", .type = phonebook::Person::PhoneType::MOBILE}}
    };

    // JSON round-trip
    const auto json = spb::json::serialize<std::string>(john);
    std::cout << "JSON:\n" << json << "\n\n";

    // Protobuf binary round-trip
    const auto pb_bytes = spb::pb::serialize<std::vector<std::byte>>(john);

    const auto decoded_json = spb::json::deserialize<phonebook::Person>(json);
    const auto decoded_pb = spb::pb::deserialize<phonebook::Person>(pb_bytes);

    // All equal: john == decoded_json == decoded_pb
}

Features

  • No protoc or Google libs dependency - instead it uses its own proto-compiler called sprotoc.
  • Supports .proto files with proto2 or proto3 syntax (no edition syntax).
  • Generates clean, modern C++ with std::optional, std::vector, and enum class.
  • Bundles protobuf and JSON support in a single library.
    • Serialized protobuf and JSON are compatible with official protoc, Python, Go, Java.
  • Embedded-friendly, zero heap allocations when paired with ETL or fixed-size strings/vectors.
    • See options for user-specified types and advanced usage.

Dependencies

  • C++ compiler with C++20 support
  • CMake
  • Standard C++ library
  • (optional) clang-format for code formatting

Type mapping

proto typeCPP typeGPB encoding
boolboolvarint
floatfloat4 bytes
doubledouble8 bytes
int32int32_tvarint
sint32int32_tzig-zag varint
uint32uint32_tvarint
int64int64_tvarint
sint64int64_tzig-zag varint
uint64uint64_tvarint
fixed32uint32_t4 bytes
sfixed32int32_t4 bytes
fixed64uint64_t8 bytes
sfixed64int64_t8 bytes
stringstd::stringUTF-8 string
bytesstd::vector<std::byte>base64 encoded in JSON
messagestructlength delimited
enumenum classvarint
mapstd::map<,>
oneofstd::variant<>
proto type modifierCPP type modifierNotes
optionalstd::optional<Message>
optionalstd::unique_ptr<Message>for cyclic message dependencies (A -> B, B -> A)
repeatedstd::vector<Message>

See also options for user-specified types and advanced usage.

Examples

See the example directory.

Doc

Performance benchmark

Tiny and Fast

See the benchmark directory.

Speed

Measured on Linux/i7-8650U CPU @ 1.90GHz with GCC 16.1.1 -flto -O2 using nanobench.

SPB protobuf serializer/deserializer has the same speed as google GPB. SPB JSON serializer/deserializer is about 4x faster than google GPB.

ns/opop/serr%ins/opcyc/opIPCbra/opmiss%benchmark
321.883,106,725.780.7%1,958.00670.072.922441.000.0%gpb-pb-serialize
314.333,181,404.470.6%1,946.00650.522.991435.000.0%gpb-lite-pb-serialize
330.463,026,072.901.0%2,107.00674.663.123480.000.0%spb-pb-serialize
920.771,086,048.030.7%4,887.001,922.732.5421,159.000.0%gpb-pb-init-serialize
949.201,053,516.290.7%4,875.001,966.392.4791,153.000.0%gpb-lite-pb-init-serialize
733.351,363,600.010.8%4,156.001,509.862.7531,006.000.0%spb-pb-init-serialize
1,002.70997,308.510.6%5,208.002,098.152.4821,204.000.1%gpb-pb-deserialize
1,017.12983,171.450.7%5,209.002,115.232.4631,204.000.0%gpb-lite-pb-deserialize
862.481,159,452.340.7%4,523.001,772.132.5521,051.000.0%spb-pb-deserialize
10,478.9595,429.370.7%51,398.0121,751.672.36313,117.010.4%gpb-json-serialize
2,316.65431,657.250.7%10,848.004,750.242.2843,005.000.2%spb-json-serialize
11,229.1989,053.660.5%54,660.3623,453.172.33113,913.090.4%gpb-json-init-serialize
2,799.49357,208.270.9%12,898.005,742.872.2463,531.000.2%spb-json-init-serialize
20,054.9349,863.050.6%93,949.6341,858.712.24422,892.550.3%gpb-json-deserialize
3,132.97319,186.410.7%16,691.006,439.462.5923,194.000.3%spb-json-deserialize

Binary size

Measured with

$ ls -alh ./build/benchmark

SPB is tiny compared to google GPB, which makes it ideal for Embedded systems.

Binary size (bytes)Binary nameDescription
3,0Mgpb-pb-serializeSerialize single PB message with libprotobuf
822Kgpb-lite-pb-serializeSerialize single PB message with libprotobuf-lite
22Kspb-pb-serializeSerialize single PB message with simple-protobuf
3,0Mgpb-pb-deserializeDeserialize single PB message with libprotobuf
822Kgpb-lite-pb-deserializeDeserialize single PB message with libprotobuf-lite
31Kspb-pb-deserializeDeserialize single PB message with simple-protobuf
3,5Mgpb-json-serializeSerialize single JSON message with libprotobuf
27Kspb-json-serializeSerialize single JSON message with simple-protobuf
3,5Mgpb-json-deserializeDeserialize single JSON message with libprotobuf
81Kspb-json-deserializeDeserialize single JSON message with simple-protobuf

Status

  • Make it work
  • Make it right
  • Make it fast

Roadmap

  • Parser for proto files (supported syntax: proto2 and proto3)
  • Compile proto messages to C++ data structs
  • JSON de/serializer for generated C++ data structs (serialized JSON is GPB-compatible)
  • Protobuf de/serializer for generated C++ data structs (serialized protobuf is GPB-compatible)
  • Implement options and user-specified types/containers
  • Benchmarks for size and speed, direct comparison with other libraries (official protobuf, nanopb)

Missing features

  • gRPC is not implemented