tonda-kriz/simple-protobuf

Lightweight C++ Protocol Buffers & JSON library

23

stars

103

commits

C++

primary language

Jul 19, 2026

updated

cpp20
embedded
json
json-parser
json-schema
json-serialization
protobuf
protocol-buffers
protocol-compiler
serialization

README

simple-protobuf

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

A lightweight C++20 library for Protocol Buffers and JSON serialization.

  • Simple native C++ structs with zero boilerplate
  • Fast as Google Protocol Buffers, tiny as nanopb
  • Fully self-contained — No protoc, no Google toolchain, no external dependencies

Usage

  1. Write standard .proto files (proto2 or proto3).
  2. Run the included sprotoc tool to generate clean C++ source files (.pb.h + .pb.cc).
  3. Serialize to Protobuf (wire-compatible) or JSON (Google-compatible) with one function call.

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

  • Fully self-contained, no protoc, no Google libs, no external dependencies - instead it uses its own proto-compiler called sprotoc.
  • Full proto2/proto3 support (no editions)
  • Generates clean, modern C++ with std::optional, std::vector, and enum class.
  • Protobuf wire format is 100% compatible with Google libraries, Python, Go, Java...
  • JSON serialization is compatible with Google's JSON mapping
  • Embedded-friendly: the library itself performs zero heap allocations - only user data (dynamic strings/vectors) may allocate, which can be eliminated with for example ETL or static strings/vectors (std::array<...>)
  • Highly configurable via options
    • max_count for repeated fields and max_size for bytes/string
    • Supports user-defined types (including bitfields and embedded containers), see spb_options.proto with generated spb_options.pb.h

Dependencies

  • C++ compiler with C++20 support
  • CMake (for build integration)
  • Standard C++ library
  • Optional: clang-format for code formatting

Type mapping

ProtoCPP typeNotes
boolbool
float/doublefloat/double
int32/sint32/sfixed32int32_t
fixed32/uint32uint32_t
int64/sint64/sfixed64int64_t
fixed64/uint64uint64_t
stringstd::stringUTF-8 (Validated during de/serialize)
bytesstd::vector<std::byte>Base64 in JSON
messagestruct
enumenum class
repeatedstd::vector<MessageT>
optionalstd::optional<MessageT>Or std::unique_ptr<MessageT> for cycle dependencies
mapstd::map<KeyT, ValueT>
oneofstd::variant<std::monostate, ...>

All types can be user-specified, see options.

Examples

See the example directory.

Doc

Performance

Fast as Google Protocol Buffers, tiny as nanopb

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

Speed benchmark Size benchmark See the benchmark directory for more details.

Missing features

  • gRPC is not implemented

Status

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

Contributors

tonda-kriz

97 commits

egilll

4 commits

seanstone

1 commits

wjtje

1 commits

tonda-kriz/simple-protobuf

Lightweight C++ Protocol Buffers & JSON library

23

stars

103

commits

C++

primary language

Jul 19, 2026

updated

cpp20
embedded
json
json-parser
json-schema
json-serialization
protobuf
protocol-buffers
protocol-compiler
serialization

README

simple-protobuf

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

A lightweight C++20 library for Protocol Buffers and JSON serialization.

  • Simple native C++ structs with zero boilerplate
  • Fast as Google Protocol Buffers, tiny as nanopb
  • Fully self-contained — No protoc, no Google toolchain, no external dependencies

Usage

  1. Write standard .proto files (proto2 or proto3).
  2. Run the included sprotoc tool to generate clean C++ source files (.pb.h + .pb.cc).
  3. Serialize to Protobuf (wire-compatible) or JSON (Google-compatible) with one function call.

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

  • Fully self-contained, no protoc, no Google libs, no external dependencies - instead it uses its own proto-compiler called sprotoc.
  • Full proto2/proto3 support (no editions)
  • Generates clean, modern C++ with std::optional, std::vector, and enum class.
  • Protobuf wire format is 100% compatible with Google libraries, Python, Go, Java...
  • JSON serialization is compatible with Google's JSON mapping
  • Embedded-friendly: the library itself performs zero heap allocations - only user data (dynamic strings/vectors) may allocate, which can be eliminated with for example ETL or static strings/vectors (std::array<...>)
  • Highly configurable via options
    • max_count for repeated fields and max_size for bytes/string
    • Supports user-defined types (including bitfields and embedded containers), see spb_options.proto with generated spb_options.pb.h

Dependencies

  • C++ compiler with C++20 support
  • CMake (for build integration)
  • Standard C++ library
  • Optional: clang-format for code formatting

Type mapping

ProtoCPP typeNotes
boolbool
float/doublefloat/double
int32/sint32/sfixed32int32_t
fixed32/uint32uint32_t
int64/sint64/sfixed64int64_t
fixed64/uint64uint64_t
stringstd::stringUTF-8 (Validated during de/serialize)
bytesstd::vector<std::byte>Base64 in JSON
messagestruct
enumenum class
repeatedstd::vector<MessageT>
optionalstd::optional<MessageT>Or std::unique_ptr<MessageT> for cycle dependencies
mapstd::map<KeyT, ValueT>
oneofstd::variant<std::monostate, ...>

All types can be user-specified, see options.

Examples

See the example directory.

Doc

Performance

Fast as Google Protocol Buffers, tiny as nanopb

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

Speed benchmark Size benchmark See the benchmark directory for more details.

Missing features

  • gRPC is not implemented

Status

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

Contributors

tonda-kriz

97 commits

egilll

4 commits

seanstone

1 commits

wjtje

1 commits

Languages

C++

95.6%

CMake

3.1%