Skip to main content
Version: 1.1

Native Serialization

C++ native serialization is the C++-only wire mode selected with .xlang(false). Use it when every writer and reader is C++ and the payload should follow C++ type behavior instead of the portable xlang type system.

Use Xlang Serialization, the default C++ mode, when bytes must be read by Java, Python, Go, Rust, JavaScript/TypeScript, C#, Swift, Dart, Scala, Kotlin, or another non-C++ Fory runtime.

When To Use Native Serialization​

Use native serialization when:

  • A payload is produced and consumed only by C++ applications.
  • The data model uses C++-specific types such as character types, unsigned-native type IDs, std::tuple, smart pointers, or C++ polymorphic models.
  • You want schema-consistent C++ payloads for lockstep services.
  • You need compatible schema evolution for C++-only rolling deployments.
  • You want to avoid portable xlang type-mapping constraints for a C++ boundary.

Create a Native Runtime​

#include "fory/serialization/fory.h"
#include <cassert>
#include <cstdint>
#include <string>

using namespace fory::serialization;

struct Order {
int64_t id;
double amount;

bool operator==(const Order &other) const {
return id == other.id && amount == other.amount;
}
};
FORY_STRUCT(Order, id, amount);

int main() {
auto fory = Fory::builder()
.xlang(false)
.build();
fory.register_struct<Order>(100);

Order order{1, 42.5};
auto bytes = fory.serialize(order).value();
auto decoded = fory.deserialize<Order>(bytes).value();
assert(order == decoded);
}

Use one configured Fory instance per thread, or build a thread-safe runtime when the same runtime is shared by multiple threads:

auto fory = Fory::builder()
.xlang(false)
.track_ref(true)
.build_thread_safe();

Register types before concurrent serialization starts.

Schema Evolution​

Native serialization defaults to schema-consistent mode. Enable compatible mode when C++-only writer and reader schemas can differ:

auto fory = Fory::builder()
.xlang(false)
.compatible(true)
.build();

Compatible mode writes schema metadata so readers can tolerate added, removed, or reordered fields when field identity remains compatible. See Schema Evolution.

Registration​

Register structs with stable IDs or names before serialization:

fory.register_struct<Order>(100);
fory.register_struct<Order>("example", "Order");

Use numeric IDs for compact payloads. Use namespace/type-name registration when independent teams coordinate type identity by names.

C++ Object Surface​

Native serialization owns the C++-specific object surface:

  • Structs and classes described by FORY_STRUCT.
  • Standard containers such as std::vector, std::map, std::unordered_map, std::set, and std::unordered_set.
  • std::optional, std::variant, and tuple-like values.
  • std::shared_ptr and std::unique_ptr.
  • Character types such as char, char16_t, and char32_t.
  • Unsigned integer types with native-mode type IDs.
  • Polymorphic serialization registered through the C++ runtime.

Use Supported Types for the full type surface and xlang mapping notes.

References And Smart Pointers​

Native serialization supports smart pointers and reference tracking:

auto fory = Fory::builder()
.xlang(false)
.track_ref(true)
.build();

When reference tracking is enabled, shared pointer identity can be preserved and cyclic object graphs can be represented through supported pointer patterns. Disable reference tracking for value-shaped data when identity is not part of the model.

Native-Only Scalar Shapes​

Some C++ scalar shapes are not portable xlang payloads. Use native serialization when these shapes must round-trip as C++ values:

auto fory = Fory::builder().xlang(false).build();

auto char_bytes = fory.serialize(char32_t{U'A'}).value();
auto value = fory.deserialize<char32_t>(char_bytes).value();

auto unsigned_bytes = fory.serialize(uint64_t{42}).value();
auto unsigned_value = fory.deserialize<uint64_t>(unsigned_bytes).value();

For xlang payloads, use schema metadata and the shared xlang type mapping instead of relying on C++ native-only type IDs.

Performance Guidelines​

  • Reuse configured Fory instances.
  • Use single-threaded Fory per thread for the fastest path; use build_thread_safe() for shared concurrent use.
  • Keep native schema-consistent mode for lockstep C++ services.
  • Enable .compatible(true) only when C++-only schema evolution is required.
  • Register structs with explicit numeric IDs for compact payloads.
  • Disable reference tracking for value-shaped graphs.
  • Prefer concrete types over polymorphic/dynamic fields on hot paths.

Native And Xlang Comparison​

RequirementUse native serializationUse xlang serialization
C++-only payloadsYesOptional
Non-C++ readers or writersNoYes
C++ native character and unsigned shapesYesLimited
Smart pointers and C++ object graphsYesLimited
Schema-consistent same-language payloadsYesNo
Compatible schema evolution by defaultNoYes
Portable type mapping across runtimesNoYes

Troubleshooting​

A non-C++ runtime cannot read the payload​

The writer is using native serialization. Rebuild it with .xlang(true) and align type registration with every peer runtime.

A rolling deployment fails after a field change​

Native serialization defaults to schema-consistent mode. Use .compatible(true) on both writer and reader when schemas can differ.

A native-only scalar does not map to another language​

Use xlang serialization with explicit schema metadata for portable payloads. Native C++ type IDs are only for C++ readers.

A shared pointer graph loses identity​

Enable .track_ref(true) and verify the graph uses supported pointer patterns.