Protocol Buffers was originally developed by Google for C++, making it the most mature and optimized implementation. If you need the absolute best performance for serialization, C++ with Protobuf is hard to beat. It's used in high-frequency trading, game servers, telecommunications infrastructure, and anywhere microseconds matter.
This guide walks you through using Protocol Buffers in C++. We'll use simple examples while covering the powerful features that make C++ protobuf the gold standard for performance-critical applications.
What You'll Need
- •C++ Compiler: GCC 7+, Clang 6+, or MSVC 2017+
- •CMake: Version 3.15+ for building
- •Protocol Buffer Compiler: We'll install this next
Step 1: Install Protocol Buffers
On Ubuntu/Debian:
sudo apt update sudo apt install -y protobuf-compiler libprotobuf-dev # Verify installation protoc --version
On macOS with Homebrew:
brew install protobuf
On Windows, download from GitHub releases. For vcpkg users:
vcpkg install protobuf protobuf:x64-windows
Step 2: Set Up Your Project
Create a C++ project structure:
mkdir cpp-protobuf-demo cd cpp-protobuf-demo mkdir protos src build
Create a CMakeLists.txt
:
cmake_minimum_required(VERSION 3.15) project(ProtobufDemo) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Find Protobuf find_package(Protobuf REQUIRED) # Generate protobuf files protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS protos/subscriber.proto) # Include directories include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${Protobuf_INCLUDE_DIRS}) # Create executable add_executable(demo src/main.cpp ${PROTO_SRCS} ${PROTO_HDRS}) target_link_libraries(demo ${Protobuf_LIBRARIES})
Step 3: Create Your .proto File
Create protos/subscriber.proto
:
syntax = "proto3"; package telecom; // Mobile subscriber information message Subscriber { string msisdn = 1; // Mobile number string name = 2; // Subscriber name string email = 3; // Email address SubscriptionType type = 4; // Plan type bool active = 5; // Account status repeated string services = 6; // Active services } enum SubscriptionType { PREPAID = 0; POSTPAID = 1; CORPORATE = 2; }
Step 4: Use Protocol Buffers in C++
Create src/main.cpp
:
#include <iostream> #include <fstream> #include "subscriber.pb.h" int main() { // Verify that the version of the library we linked against is // compatible with the version of the headers we compiled against GOOGLE_PROTOBUF_VERIFY_VERSION; // Create a new subscriber telecom::Subscriber subscriber; subscriber.set_msisdn("+91-9876543210"); subscriber.set_name("Telecom Customer"); subscriber.set_email("customer@telecom.com"); subscriber.set_type(telecom::SubscriptionType::POSTPAID); subscriber.set_active(true); subscriber.add_services("Voice"); subscriber.add_services("Data"); subscriber.add_services("4G LTE"); std::cout << "Created subscriber: " << subscriber.name() << std::endl; std::cout << "MSISDN: " << subscriber.msisdn() << std::endl; std::cout << "Type: " << subscriber.type() << std::endl; // Serialize to binary std::string binary_data; subscriber.SerializeToString(&binary_data); std::cout << "Serialized to " << binary_data.size() << " bytes\n" << std::endl; // Deserialize from binary telecom::Subscriber decoded; decoded.ParseFromString(binary_data); std::cout << "Deserialized subscriber: " << decoded.name() << std::endl; std::cout << "Email: " << decoded.email() << std::endl; std::cout << "Active: " << (decoded.active() ? "Yes" : "No") << std::endl; // Access repeated fields std::cout << "\nActive services:" << std::endl; for (int i = 0; i < decoded.services_size(); i++) { std::cout << " - " << decoded.services(i) << std::endl; } // Clean up google::protobuf::ShutdownProtobufLibrary(); return 0; }
Step 5: Build and Run
Build the project:
cd build cmake .. make # Run ./demo
You should see output like:
Created subscriber: Telecom Customer MSISDN: +91-9876543210 Type: 1 Serialized to 68 bytes Deserialized subscriber: Telecom Customer Email: customer@telecom.com Active: Yes Active services: - Voice - Data - 4G LTE
Saving and Loading from Files
Save and load protobuf data to/from files:
#include <fstream> #include "subscriber.pb.h" void saveToFile() { telecom::Subscriber subscriber; subscriber.set_msisdn("+91-9123456789"); subscriber.set_name("Network Admin"); subscriber.set_email("admin@telecom.com"); subscriber.set_type(telecom::SubscriptionType::CORPORATE); subscriber.set_active(true); // Save to file std::fstream output("subscriber.bin", std::ios::out | std::ios::binary); if (!subscriber.SerializeToOstream(&output)) { std::cerr << "Failed to write subscriber" << std::endl; return; } output.close(); std::cout << "Saved to file" << std::endl; } void loadFromFile() { telecom::Subscriber subscriber; // Load from file std::fstream input("subscriber.bin", std::ios::in | std::ios::binary); if (!subscriber.ParseFromIstream(&input)) { std::cerr << "Failed to parse subscriber" << std::endl; return; } input.close(); std::cout << "Loaded: " << subscriber.name() << std::endl; std::cout << "MSISDN: " << subscriber.msisdn() << std::endl; }
Convert Between JSON and Protobuf
C++ protobuf supports JSON conversion (requires protobuf 3.0+):
#include <google/protobuf/util/json_util.h> #include "subscriber.pb.h" void jsonExample() { telecom::Subscriber subscriber; subscriber.set_msisdn("+91-9876543210"); subscriber.set_name("Mobile User"); subscriber.set_type(telecom::SubscriptionType::PREPAID); subscriber.set_active(true); // Convert to JSON std::string json_string; google::protobuf::util::MessageToJsonString(subscriber, &json_string); std::cout << "JSON output:" << std::endl; std::cout << json_string << std::endl; // Parse from JSON std::string json_data = R"({ "msisdn": "+91-9111111111", "name": "New Subscriber", "type": "POSTPAID", "active": true })"; telecom::Subscriber parsed; google::protobuf::util::JsonStringToMessage(json_data, &parsed); std::cout << "\nParsed from JSON: " << parsed.name() << std::endl; std::cout << "Type: " << parsed.type() << std::endl; }
High-Performance Arena Allocation
For maximum performance, use Arena allocation to reduce memory allocations:
#include <google/protobuf/arena.h> #include "subscriber.pb.h" void arenaExample() { // Create arena google::protobuf::Arena arena; // Allocate messages on arena (no manual delete needed) telecom::Subscriber* subscriber = google::protobuf::Arena::CreateMessage<telecom::Subscriber>(&arena); subscriber->set_msisdn("+91-9876543210"); subscriber->set_name("Arena User"); subscriber->set_type(telecom::SubscriptionType::POSTPAID); subscriber->add_services("Voice"); subscriber->add_services("Data"); std::cout << "Created: " << subscriber->name() << std::endl; std::cout << "Services: " << subscriber->services_size() << std::endl; // Arena automatically cleans up all messages }
Performance Tip: Arena allocation can be 2-5x faster for workloads with many message allocations because it eliminates individual deallocations.
Best Practices for C++
Use Arena Allocation
For high-performance scenarios with many messages, arena allocation dramatically reduces allocation overhead and improves cache locality.
Reuse Message Objects
Call Clear()
on messages instead of creating new ones repeatedly. This reuses allocated memory.
Use Binary Format
The binary format is 3-10x faster than JSON. Only use JSON for debugging or API compatibility.
Check Return Values
Always check return values from ParseFromString()
andSerializeToString()
to handle errors.
Call ShutdownProtobufLibrary()
Always call this before your program exits to free global resources and avoid memory leak warnings.
Common Issues
Issue: Linker errors with protobuf library
Solution: Make sure you're linking against the correct protobuf library. Use find_package(Protobuf REQUIRED)
in CMake and link with ${Protobuf_LIBRARIES}
.
Issue: Version mismatch warnings
Solution: Make sure the protoc compiler version matches your libprotobuf version. Check with protoc --version
.
Issue: Parse errors with binary data
Solution: Always check return values from Parse methods. Use binary mode (std::ios::binary
) when working with files.
Related Tools
Additional Resources
Official Documentation & References
- Official C++ Protobuf Tutorial - Google's official C++ guide
- Protobuf on GitHub - Main repository and examples
- C++ API Documentation - Complete API reference
Conclusion
Protocol Buffers in C++ delivers unmatched performance for serialization. As the original implementation, it's the most mature, optimized, and feature-complete. If your application needs the absolute best performance, C++ with protobuf is the way to go.
Start with these examples and explore advanced features like arena allocation, zero-copy parsing, and custom allocators. The performance benefits make protobuf essential for high-throughput, low-latency C++ applications.