tree: ce9821ec207ff67fa65779fb15cd3fd0543510ae [path history] [tgz]
  1. cmake/
  2. fory/
  3. CMakeLists.txt
  4. README.md
cpp/README.md

Apache Fory™ C++

License

Apache Fory™ is a blazing fast multi-language serialization framework powered by JIT compilation and zero-copy techniques, providing up to ultra-fast performance while maintaining ease of use and type safety.

The C++ implementation provides high-performance serialization with compile-time type safety through template metaprogramming and zero-copy row format for analytics workloads.

Why Apache Fory™ C++?

  • Blazingly Fast: Fast serialization and optimized binary protocols
  • Cross-Language: Seamlessly serialize/deserialize data across Java, Python, C++, Go, JavaScript, and Rust
  • Type-Safe: Compile-time type checking with template specialization
  • Shared References: Automatic tracking of shared and circular references
  • Schema Evolution: Compatible mode for independent schema changes
  • Two Formats: Object graph serialization and zero-copy row-based format
  • Modern C++17: Clean API using modern C++ features

Benchmark

Fory C++ demonstrates competitive performance compared to protobuf c++ serialization framework.

For more detailed benchmarks and methodology, see C++ Benchmarks.

Quick Start

Basic Example

#include "fory/serialization/fory.h"

// Define your class with FORY_STRUCT macro (struct works the same way).
// Place it after all fields.
// When used inside a class, it must be placed in a public: section.
struct Person {
  std::string name;
  int32_t age;
  std::string email;
  FORY_STRUCT(Person, name, age, email);
};

int main() {
  // Create Fory instance
  auto fory = apache::fory::ForyBuilder()
      .xlang(true)
      .track_ref(true)
      .build();

  // Register type
  fory->register_struct<Person>(1);

  // Create object
  Person person{"Alice", 30, "alice@example.com"};

  // Serialize
  auto result = fory->serialize(person);
  if (!result.ok()) {
    std::cerr << "Serialization failed: " << result.error().message() << std::endl;
    return 1;
  }
  std::vector<uint8_t> bytes = std::move(result.value());

  // Deserialize
  auto decoded = fory->deserialize<Person>(bytes);
  if (!decoded.ok()) {
    std::cerr << "Deserialization failed: " << decoded.error().message() << std::endl;
    return 1;
  }
  Person restored = decoded.value();

  assert(restored.name == "Alice");
  assert(restored.age == 30);
  return 0;
}

Core Features

1. Object Graph Serialization

Apache Fory™ provides automatic serialization of complex object graphs, preserving the structure and relationships between objects. The FORY_STRUCT macro enables compile-time reflection without runtime overhead.

Key capabilities:

  • Nested struct serialization with arbitrary depth
  • Collection types (vector, set, unordered_set)
  • Map types (map, unordered_map)
  • Optional fields with std::optional<T>
  • Smart pointers (shared_ptr, unique_ptr, weak_ptr)
  • Efficient binary encoding with variable-length integers
#include "fory/serialization/fory.h"

class Address {
public:
  std::string street;
  std::string city;
  std::string country;
  FORY_STRUCT(Address, street, city, country);
};

class Person {
public:
  std::string name;
  int32_t age;
  Address address;
  std::vector<std::string> hobbies;
  std::map<std::string, std::string> metadata;
  FORY_STRUCT(Person, name, age, address, hobbies, metadata);
};

auto fory = apache::fory::ForyBuilder().build();
fory->register_struct<Address>(100);
fory->register_struct<Person>(200);

Person person{
    "John Doe",
    30,
    Address{"123 Main St", "New York", "USA"},
    {"reading", "coding"},
    {{"role", "developer"}}
};

auto bytes = fory->serialize(person).value();
auto decoded = fory->deserialize<Person>(bytes).value();

1.1 External/Third-Party Types

For third-party types where you cannot modify the class definition, use FORY_STRUCT at namespace scope. This works for public fields only.

namespace thirdparty {
struct Foo {
  int32_t id;
  std::string name;
};

FORY_STRUCT(Foo, id, name);
} // namespace thirdparty

auto fory = apache::fory::ForyBuilder().build();
fory->register_struct<thirdparty::Foo>(1);

1.2 Inherited Fields

To include base-class fields in a derived type, use FORY_BASE(Base) inside FORY_STRUCT. The base must define its own FORY_STRUCT so its fields can be referenced.

struct Base {
  int32_t id;
  FORY_STRUCT(Base, id);
};

struct Derived : Base {
  std::string name;
  FORY_STRUCT(Derived, FORY_BASE(Base), name);
};

2. Shared References

Apache Fory™ automatically tracks and preserves reference identity for shared objects using std::shared_ptr<T>. When the same object is referenced multiple times, Fory serializes it only once and uses reference IDs for subsequent occurrences.

auto fory = apache::fory::ForyBuilder()
    .track_ref(true)
    .build();

// Create a shared value
auto shared = std::make_shared<std::string>("shared_value");

// Reference it multiple times
std::vector<std::shared_ptr<std::string>> data = {shared, shared, shared};

// The shared value is serialized only once
auto bytes = fory->serialize(data).value();
auto decoded = fory->deserialize<std::vector<std::shared_ptr<std::string>>>(bytes).value();

// Verify reference identity is preserved
assert(decoded[0].get() == decoded[1].get());
assert(decoded[1].get() == decoded[2].get());

3. Schema Evolution

Apache Fory™ supports schema evolution in Compatible mode, allowing serialization and deserialization peers to have different type definitions.

// Version 1
struct PersonV1 {
  std::string name;
  int32_t age;
  std::string address;
  FORY_STRUCT(PersonV1, name, age, address);
};

// Version 2 - address removed, phone added
struct PersonV2 {
  std::string name;
  int32_t age;
  std::optional<std::string> phone;
  FORY_STRUCT(PersonV2, name, age, phone);
};

auto fory1 = apache::fory::ForyBuilder()
    .compatible(true)
    .build();
fory1->register_struct<PersonV1>(1);

auto fory2 = apache::fory::ForyBuilder()
    .compatible(true)
    .build();
fory2->register_struct<PersonV2>(1);

PersonV1 v1{"Alice", 30, "123 Main St"};
auto bytes = fory1->serialize(v1).value();

// Deserialize with V2 - missing fields get default values
auto v2 = fory2->deserialize<PersonV2>(bytes).value();
assert(v2.name == "Alice");
assert(v2.phone == std::nullopt);

4. Enum Support

Apache Fory™ supports enum serialization with automatic ordinal mapping:

// Continuous enum - works automatically
enum class Color { Red, Green, Blue };

// Non-continuous enum - needs FORY_ENUM
enum class LegacyStatus { Active = 1, Inactive = 5, Pending = 10 };
FORY_ENUM(LegacyStatus, Active, Inactive, Pending);
// FORY_ENUM must be defined at namespace scope.

struct Item {
  std::string name;
  Color color;
  LegacyStatus status;
  FORY_STRUCT(Item, name, color, status);
};

5. Variant Support

Apache Fory™ supports std::variant for type-safe union types:

using Value = std::variant<std::monostate, bool, int32_t, std::string>;

struct Config {
  std::string key;
  Value value;
  FORY_STRUCT(Config, key, value);
};

Config config{"timeout", 30};
auto bytes = fory->serialize(config).value();

6. Row-Based Serialization

Apache Fory™ provides a high-performance row format for zero-copy deserialization, enabling random access to fields directly from binary data.

#include "fory/encoder/row_encoder.h"

struct UserProfile {
  int64_t id;
  std::string username;
  std::string email;
  std::vector<int32_t> scores;
  bool is_active;
  FORY_STRUCT(UserProfile, id, username, email, scores, is_active);
};

apache::fory::RowEncoder<UserProfile> encoder;

UserProfile profile{12345, "alice", "alice@example.com", {95, 87, 92}, true};

// encode to row format
encoder.encode(profile);
auto& writer = encoder.get_writer();

// Access fields directly without full deserialization
auto row = writer.to_row();
assert(row.get_int64(0) == 12345);           // id
assert(row.get_string(1) == "alice");         // username
assert(row.get_bool(4) == true);              // is_active

Cross-Language Serialization

Apache Fory™ supports seamless data exchange across multiple languages:

// Enable cross-language mode
auto fory = apache::fory::ForyBuilder()
    .xlang(true)
    .compatible(true)
    .build();

// Register types with consistent IDs across languages
fory->register_struct<MyStruct>(1);

See xlang_type_mapping.md for type mapping across languages.

Thread Safety

// Single-threaded (fastest performance)
auto fory = apache::fory::ForyBuilder().build();

// Thread-safe with internal Fory pool
auto fory = apache::fory::ForyBuilder().build_thread_safe();

Architecture

The C++ implementation consists of these main components:

cpp/fory/
├── serialization/           # Object graph serialization
│   ├── fory.h              # Main entry point (Fory, ThreadSafeFory)
│   ├── config.h            # Configuration options
│   ├── serializer.h        # Core serializer API
│   ├── basic_serializer.h  # Primitive type serializers
│   ├── string_serializer.h # String type serializers
│   ├── struct_serializer.h # Struct serialization with FORY_STRUCT
│   ├── collection_serializer.h  # vector, set serializers
│   ├── map_serializer.h    # map serializers
│   ├── smart_ptr_serializers.h  # optional, shared_ptr, unique_ptr
│   ├── temporal_serializers.h   # Duration, Timestamp, Date
│   ├── variant_serializer.h     # std::variant support
│   ├── type_resolver.h     # Type resolution and registration
│   └── context.h           # Read/write context
├── encoder/                 # Row format encoding
│   ├── row_encoder.h       # Row format encoder
│   └── row_encode_trait.h  # Encoding traits
├── row/                     # Row format data structures
│   ├── row.h               # Row, ArrayData, MapData
│   ├── writer.h            # RowWriter, ArrayWriter
│   ├── schema.h            # Schema definitions
│   └── type.h              # Type definitions
├── meta/                    # Compile-time reflection
│   ├── field_info.h        # Field metadata extraction
│   └── type_traits.h       # Type traits utilities
└── util/                    # Common utilities
    ├── buffer.h            # Binary buffer management
    ├── error.h             # Error handling
    └── status.h            # Status codes

Environment

  • C++ Standard: C++17 or later
  • Build System: Bazel 8.2.1+ or CMake 3.16+

Building

With Bazel

# Build all projects
bazel build //cpp/...

# Run all tests
bazel test $(bazel query //cpp/...)

# Run serialization tests
bazel test $(bazel query //cpp/fory/serialization/...)

With CMake

mkdir build && cd build
cmake ..
make -j$(nproc)

Code Quality

# Format code
clang-format -i <file>

# Or use the CI script
bash ci/format.sh --cpp

Documentation

Use Cases

Object Serialization

  • Complex data structures with nested objects and references
  • Cross-language communication in microservices
  • General-purpose serialization with full type safety
  • Schema evolution with compatible mode

Row-Based Serialization

  • High-throughput data processing
  • Analytics workloads requiring fast field access
  • Memory-constrained environments
  • Zero-copy scenarios

License

Licensed under the Apache License, Version 2.0. See LICENSE for details.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Support


Apache Fory™ - Blazingly fast multi-language serialization framework.