This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new c315ccf67 feat(): add c++ user guide doc (#3037)
c315ccf67 is described below

commit c315ccf67287150f79850902c4a1751d21e57185
Author: Shawn Yang <[email protected]>
AuthorDate: Thu Dec 11 09:28:20 2025 +0800

    feat(): add c++ user guide doc (#3037)
    
    ## Why?
    
    
    
    ## What does this PR do?
    
    
    
    ## Related issues
    
    Closes #3038
    
    #2906
    
    
    ## Does this PR introduce any user-facing change?
    
    
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 cpp/README.md                                | 382 +++++++++++++++++++-
 cpp/fory/serialization/BUILD                 |   1 +
 cpp/fory/serialization/CMakeLists.txt        |   1 +
 cpp/fory/serialization/basic_serializer.h    | 243 ++++++++-----
 cpp/fory/serialization/serialization_test.cc |  30 ++
 cpp/fory/serialization/serializer.h          |   1 +
 cpp/fory/serialization/string_serializer.h   | 421 ++++++++++++++++++++++
 cpp/fory/type/type.h                         |   6 +
 docs/guide/cpp/basic-serialization.md        | 238 ++++++++++++
 docs/guide/cpp/configuration.md              | 189 ++++++++++
 docs/guide/cpp/cross-language.md             | 271 ++++++++++++++
 docs/guide/cpp/index.md                      | 245 +++++++++++++
 docs/guide/cpp/row-format.md                 | 516 +++++++++++++++++++++++++++
 docs/guide/cpp/schema-evolution.md           | 404 +++++++++++++++++++++
 docs/guide/cpp/supported-types.md            | 277 ++++++++++++++
 docs/guide/cpp/type-registration.md          | 225 ++++++++++++
 16 files changed, 3354 insertions(+), 96 deletions(-)

diff --git a/cpp/README.md b/cpp/README.md
index 96ec45647..5eab2ebd9 100644
--- a/cpp/README.md
+++ b/cpp/README.md
@@ -1,24 +1,392 @@
 # Apache Fory™ C++
 
-Apache Fory™ is a blazingly-fast multi-language serialization framework 
powered by just-in-time compilation and zero-copy.
+[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/apache/fory/blob/main/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 Modes**: Object graph serialization and zero-copy row-based format
+- **Modern C++17**: Clean API using modern C++ features
+
+## Quick Start
+
+### Basic Example
+
+```cpp
+#include "fory/serialization/fory.h"
+
+// Define your struct with FORY_STRUCT macro
+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_type<Person>(1);
+
+  // Create object
+  Person person{"Alice", 30, "[email protected]"};
+
+  // 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
+
+```cpp
+#include "fory/serialization/fory.h"
+
+struct Address {
+  std::string street;
+  std::string city;
+  std::string country;
+  FORY_STRUCT(Address, street, city, country);
+};
+
+struct Person {
+  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_type<Address>(100);
+fory->register_type<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();
+```
+
+### 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.
+
+```cpp
+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.
+
+```cpp
+// 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_type<PersonV1>(1);
+
+auto fory2 = apache::fory::ForyBuilder()
+    .compatible(true)
+    .build();
+fory2->register_type<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:
+
+```cpp
+// 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);
+
+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:
+
+```cpp
+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.
+
+```cpp
+#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_FIELD_INFO(UserProfile, id, username, email, scores, is_active);
+};
+
+apache::fory::RowEncoder<UserProfile> encoder;
+
+UserProfile profile{12345, "alice", "[email protected]", {95, 87, 92}, true};
+
+// Encode to row format
+encoder.Encode(profile);
+auto& writer = encoder.GetWriter();
+
+// Access fields directly without full deserialization
+auto row = writer.ToRow();
+assert(row.GetInt64(0) == 12345);           // id
+assert(row.GetString(1) == "alice");         // username
+assert(row.GetBool(4) == true);              // is_active
+```
+
+## Cross-Language Serialization
+
+Apache Fory™ supports seamless data exchange across multiple languages:
+
+```cpp
+// Enable cross-language mode
+auto fory = apache::fory::ForyBuilder()
+    .xlang(true)
+    .compatible(true)
+    .build();
+
+// Register types with consistent IDs across languages
+fory->register_type<MyStruct>(1);
+```
+
+See 
[xlang_type_mapping.md](https://fory.apache.org/docs/specification/xlang_type_mapping)
 for type mapping across languages.
+
+## Thread Safety
+
+```cpp
+// 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, LocalDate
+│   ├── 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
 
-- Bazel version: 8.2.1
+- **C++ Standard**: C++17 or later
+- **Build System**: Bazel 8.2.1+ or CMake 3.16+
 
-## Build Apache Fory™ C++
+## Building
+
+### With Bazel
 
 ```bash
 # Build all projects
-bazel build //:all
+bazel build //cpp/...
+
 # Run all tests
-bazel test //:all
+bazel test $(bazel query //cpp/...)
+
 # Run serialization tests
-bazel test //cpp/fory/serialization:all
+bazel test $(bazel query //cpp/fory/serialization/...)
 ```
 
-## Format Code
+### With CMake
 
 ```bash
+mkdir build && cd build
+cmake ..
+make -j$(nproc)
+```
+
+## Code Quality
+
+```bash
+# Format code
+clang-format -i <file>
+
+# Or use the CI script
 bash ci/format.sh --cpp
 ```
+
+## Documentation
+
+- **[User Guide](https://fory.apache.org/docs/guide/cpp)** - Comprehensive 
user documentation
+- **[Protocol 
Specification](https://fory.apache.org/docs/specification/fory_xlang_serialization_spec)**
 - Serialization protocol details
+- **[Type 
Mapping](https://fory.apache.org/docs/specification/xlang_type_mapping)** - 
Cross-language type mappings
+- **[Source](https://github.com/apache/fory/tree/main/docs/guide/cpp)** - 
Documentation source
+
+## 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](https://github.com/apache/fory/blob/main/LICENSE) for details.
+
+## Contributing
+
+We welcome contributions! Please see our [Contributing 
Guide](https://github.com/apache/fory/blob/main/CONTRIBUTING.md) for details.
+
+## Support
+
+- **Issues**: [GitHub Issues](https://github.com/apache/fory/issues)
+- **Discussions**: [GitHub 
Discussions](https://github.com/apache/fory/discussions)
+- **Slack**: [Apache Fory 
Slack](https://join.slack.com/t/fory-project/shared_invite/zt-1u8soj4qc-ieYEu7ciHOqA2mo47llS8A)
+
+---
+
+**Apache Fory™** - Blazingly fast multi-language serialization framework.
diff --git a/cpp/fory/serialization/BUILD b/cpp/fory/serialization/BUILD
index fe51077c3..43f8825cb 100644
--- a/cpp/fory/serialization/BUILD
+++ b/cpp/fory/serialization/BUILD
@@ -21,6 +21,7 @@ cc_library(
         "serializer_traits.h",
         "skip.h",
         "smart_ptr_serializers.h",
+        "string_serializer.h",
         "struct_serializer.h",
         "temporal_serializers.h",
         "tuple_serializer.h",
diff --git a/cpp/fory/serialization/CMakeLists.txt 
b/cpp/fory/serialization/CMakeLists.txt
index 509b2f6ee..b7306b65b 100644
--- a/cpp/fory/serialization/CMakeLists.txt
+++ b/cpp/fory/serialization/CMakeLists.txt
@@ -35,6 +35,7 @@ set(FORY_SERIALIZATION_HEADERS
     serializer_traits.h
     skip.h
     smart_ptr_serializers.h
+    string_serializer.h
     struct_serializer.h
     temporal_serializers.h
     tuple_serializer.h
diff --git a/cpp/fory/serialization/basic_serializer.h 
b/cpp/fory/serialization/basic_serializer.h
index 68d8bb84f..4dd6198bc 100644
--- a/cpp/fory/serialization/basic_serializer.h
+++ b/cpp/fory/serialization/basic_serializer.h
@@ -23,11 +23,7 @@
 #include "fory/serialization/serializer_traits.h"
 #include "fory/type/type.h"
 #include "fory/util/error.h"
-#include "fory/util/result.h"
-#include "fory/util/string_util.h"
 #include <cstdint>
-#include <cstring>
-#include <string>
 #include <type_traits>
 
 namespace fory {
@@ -535,19 +531,12 @@ template <> struct Serializer<double> {
 };
 
 // ============================================================================
-// String Serializer
+// Character Type Serializers (C++ native only, not supported in xlang mode)
 // ============================================================================
 
-/// std::string serializer using UTF-8 encoding per xlang spec
-template <> struct Serializer<std::string> {
-  static constexpr TypeId type_id = TypeId::STRING;
-
-  // String encoding types as per xlang spec
-  enum class StringEncoding : uint8_t {
-    LATIN1 = 0, // Latin1/ISO-8859-1
-    UTF16 = 1,  // UTF-16
-    UTF8 = 2,   // UTF-8
-  };
+/// char serializer (C++ native only, type_id = 68)
+template <> struct Serializer<char> {
+  static constexpr TypeId type_id = TypeId::CHAR;
 
   static inline void write_type_info(WriteContext &ctx) {
     ctx.write_varuint32(static_cast<uint32_t>(type_id));
@@ -564,9 +553,8 @@ template <> struct Serializer<std::string> {
     }
   }
 
-  static inline void write(const std::string &value, WriteContext &ctx,
-                           bool write_ref, bool write_type,
-                           bool has_generics = false) {
+  static inline void write(char value, WriteContext &ctx, bool write_ref,
+                           bool write_type, bool has_generics = false) {
     write_not_null_ref_flag(ctx, write_ref);
     if (write_type) {
       ctx.write_varuint32(static_cast<uint32_t>(type_id));
@@ -574,115 +562,192 @@ template <> struct Serializer<std::string> {
     write_data(value, ctx);
   }
 
-  static inline void write_data(const std::string &value, WriteContext &ctx) {
-    // Always use UTF-8 encoding for cross-language compatibility.
-    // Per xlang spec: write size shifted left by 2 bits, with encoding
-    // (UTF8) in the lower 2 bits. Use varuint36small encoding.
-    uint64_t length = static_cast<uint64_t>(value.size());
-    uint64_t size_with_encoding =
-        (length << 2) | static_cast<uint64_t>(StringEncoding::UTF8);
-    ctx.write_varuint36small(size_with_encoding);
-
-    // Write string bytes
-    if (!value.empty()) {
-      ctx.write_bytes(value.data(), value.size());
-    }
+  static inline void write_data(char value, WriteContext &ctx) {
+    ctx.write_int8(static_cast<int8_t>(value));
   }
 
-  static inline void write_data_generic(const std::string &value,
-                                        WriteContext &ctx, bool has_generics) {
+  static inline void write_data_generic(char value, WriteContext &ctx,
+                                        bool has_generics) {
     write_data(value, ctx);
   }
 
-  static inline std::string read(ReadContext &ctx, bool read_ref,
-                                 bool read_type) {
+  static inline char read(ReadContext &ctx, bool read_ref, bool read_type) {
     bool has_value = consume_ref_flag(ctx, read_ref);
     if (ctx.has_error() || !has_value) {
-      return std::string();
+      return '\0';
     }
     if (read_type) {
       uint32_t type_id_read = ctx.read_varuint32(ctx.error());
       if (FORY_PREDICT_FALSE(ctx.has_error())) {
-        return std::string();
+        return '\0';
       }
       if (type_id_read != static_cast<uint32_t>(type_id)) {
         ctx.set_error(
             Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
-        return std::string();
+        return '\0';
       }
     }
     return read_data(ctx);
   }
 
-  static inline std::string read_data(ReadContext &ctx) {
-    // Read size with encoding using varuint36small
-    uint64_t size_with_encoding = ctx.read_varuint36small(ctx.error());
-    if (FORY_PREDICT_FALSE(ctx.has_error())) {
-      return std::string();
-    }
+  static inline char read_data(ReadContext &ctx) {
+    return static_cast<char>(ctx.read_int8(ctx.error()));
+  }
 
-    // Extract size and encoding from lower 2 bits
-    uint64_t length = size_with_encoding >> 2;
-    StringEncoding encoding =
-        static_cast<StringEncoding>(size_with_encoding & 0x3);
+  static inline char read_data_generic(ReadContext &ctx, bool has_generics) {
+    return read_data(ctx);
+  }
 
-    if (length == 0) {
-      return std::string();
+  static inline char read_with_type_info(ReadContext &ctx, bool read_ref,
+                                         const TypeInfo &type_info) {
+    return read(ctx, read_ref, false);
+  }
+};
+
+/// char16_t serializer (C++ native only, type_id = 69)
+template <> struct Serializer<char16_t> {
+  static constexpr TypeId type_id = TypeId::CHAR16;
+
+  static inline void write_type_info(WriteContext &ctx) {
+    ctx.write_varuint32(static_cast<uint32_t>(type_id));
+  }
+
+  static inline void read_type_info(ReadContext &ctx) {
+    uint32_t actual = ctx.read_varuint32(ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return;
+    }
+    if (actual != static_cast<uint32_t>(type_id)) {
+      ctx.set_error(
+          Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
     }
+  }
 
-    // Validate length against buffer remaining size
-    if (length > ctx.buffer().remaining_size()) {
-      ctx.set_error(Error::invalid_data("String length exceeds buffer size"));
-      return std::string();
+  static inline void write(char16_t value, WriteContext &ctx, bool write_ref,
+                           bool write_type, bool has_generics = false) {
+    write_not_null_ref_flag(ctx, write_ref);
+    if (write_type) {
+      ctx.write_varuint32(static_cast<uint32_t>(type_id));
     }
+    write_data(value, ctx);
+  }
 
-    // Handle different encodings
-    switch (encoding) {
-    case StringEncoding::LATIN1: {
-      std::vector<uint8_t> bytes(length);
-      ctx.read_bytes(bytes.data(), length, ctx.error());
-      if (FORY_PREDICT_FALSE(ctx.has_error())) {
-        return std::string();
-      }
-      return latin1ToUtf8(bytes.data(), length);
+  static inline void write_data(char16_t value, WriteContext &ctx) {
+    ctx.write_bytes(&value, sizeof(char16_t));
+  }
+
+  static inline void write_data_generic(char16_t value, WriteContext &ctx,
+                                        bool has_generics) {
+    write_data(value, ctx);
+  }
+
+  static inline char16_t read(ReadContext &ctx, bool read_ref, bool read_type) 
{
+    bool has_value = consume_ref_flag(ctx, read_ref);
+    if (ctx.has_error() || !has_value) {
+      return u'\0';
     }
-    case StringEncoding::UTF16: {
-      if (length % 2 != 0) {
-        ctx.set_error(Error::invalid_data("UTF-16 length must be even"));
-        return std::string();
-      }
-      std::vector<uint16_t> utf16_chars(length / 2);
-      ctx.read_bytes(reinterpret_cast<uint8_t *>(utf16_chars.data()), length,
-                     ctx.error());
+    if (read_type) {
+      uint32_t type_id_read = ctx.read_varuint32(ctx.error());
       if (FORY_PREDICT_FALSE(ctx.has_error())) {
-        return std::string();
+        return u'\0';
       }
-      return utf16ToUtf8(utf16_chars.data(), utf16_chars.size());
-    }
-    case StringEncoding::UTF8: {
-      // UTF-8: read bytes directly
-      std::string result(length, '\0');
-      ctx.read_bytes(&result[0], length, ctx.error());
-      if (FORY_PREDICT_FALSE(ctx.has_error())) {
-        return std::string();
+      if (type_id_read != static_cast<uint32_t>(type_id)) {
+        ctx.set_error(
+            Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
+        return u'\0';
       }
-      return result;
     }
-    default:
+    return read_data(ctx);
+  }
+
+  static inline char16_t read_data(ReadContext &ctx) {
+    char16_t value;
+    ctx.read_bytes(reinterpret_cast<uint8_t *>(&value), sizeof(char16_t),
+                   ctx.error());
+    return value;
+  }
+
+  static inline char16_t read_data_generic(ReadContext &ctx,
+                                           bool has_generics) {
+    return read_data(ctx);
+  }
+
+  static inline char16_t read_with_type_info(ReadContext &ctx, bool read_ref,
+                                             const TypeInfo &type_info) {
+    return read(ctx, read_ref, false);
+  }
+};
+
+/// char32_t serializer (C++ native only, type_id = 70)
+template <> struct Serializer<char32_t> {
+  static constexpr TypeId type_id = TypeId::CHAR32;
+
+  static inline void write_type_info(WriteContext &ctx) {
+    ctx.write_varuint32(static_cast<uint32_t>(type_id));
+  }
+
+  static inline void read_type_info(ReadContext &ctx) {
+    uint32_t actual = ctx.read_varuint32(ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return;
+    }
+    if (actual != static_cast<uint32_t>(type_id)) {
       ctx.set_error(
-          Error::encoding_error("Unknown string encoding: " +
-                                std::to_string(static_cast<int>(encoding))));
-      return std::string();
+          Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
+    }
+  }
+
+  static inline void write(char32_t value, WriteContext &ctx, bool write_ref,
+                           bool write_type, bool has_generics = false) {
+    write_not_null_ref_flag(ctx, write_ref);
+    if (write_type) {
+      ctx.write_varuint32(static_cast<uint32_t>(type_id));
     }
+    write_data(value, ctx);
+  }
+
+  static inline void write_data(char32_t value, WriteContext &ctx) {
+    ctx.write_bytes(&value, sizeof(char32_t));
+  }
+
+  static inline void write_data_generic(char32_t value, WriteContext &ctx,
+                                        bool has_generics) {
+    write_data(value, ctx);
+  }
+
+  static inline char32_t read(ReadContext &ctx, bool read_ref, bool read_type) 
{
+    bool has_value = consume_ref_flag(ctx, read_ref);
+    if (ctx.has_error() || !has_value) {
+      return U'\0';
+    }
+    if (read_type) {
+      uint32_t type_id_read = ctx.read_varuint32(ctx.error());
+      if (FORY_PREDICT_FALSE(ctx.has_error())) {
+        return U'\0';
+      }
+      if (type_id_read != static_cast<uint32_t>(type_id)) {
+        ctx.set_error(
+            Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
+        return U'\0';
+      }
+    }
+    return read_data(ctx);
+  }
+
+  static inline char32_t read_data(ReadContext &ctx) {
+    char32_t value;
+    ctx.read_bytes(reinterpret_cast<uint8_t *>(&value), sizeof(char32_t),
+                   ctx.error());
+    return value;
   }
 
-  static inline std::string read_data_generic(ReadContext &ctx,
-                                              bool has_generics) {
+  static inline char32_t read_data_generic(ReadContext &ctx,
+                                           bool has_generics) {
     return read_data(ctx);
   }
 
-  static inline std::string read_with_type_info(ReadContext &ctx, bool 
read_ref,
-                                                const TypeInfo &type_info) {
+  static inline char32_t read_with_type_info(ReadContext &ctx, bool read_ref,
+                                             const TypeInfo &type_info) {
     return read(ctx, read_ref, false);
   }
 };
diff --git a/cpp/fory/serialization/serialization_test.cc 
b/cpp/fory/serialization/serialization_test.cc
index bf9d7e17d..c2dd16007 100644
--- a/cpp/fory/serialization/serialization_test.cc
+++ b/cpp/fory/serialization/serialization_test.cc
@@ -182,6 +182,36 @@ TEST(SerializationTest, StringRoundtrip) {
   test_roundtrip(std::string("UTF-8: 你好世界"));
 }
 
+// ============================================================================
+// Character Type Tests (C++ native only)
+// ============================================================================
+
+TEST(SerializationTest, CharRoundtrip) {
+  test_roundtrip<char>('A');
+  test_roundtrip<char>('z');
+  test_roundtrip<char>('0');
+  test_roundtrip<char>('\0');
+  test_roundtrip<char>('\n');
+  test_roundtrip<char>(static_cast<char>(127));
+  test_roundtrip<char>(static_cast<char>(-128));
+}
+
+TEST(SerializationTest, Char16Roundtrip) {
+  test_roundtrip<char16_t>(u'A');
+  test_roundtrip<char16_t>(u'中');
+  test_roundtrip<char16_t>(u'\0');
+  test_roundtrip<char16_t>(static_cast<char16_t>(0xFFFF));
+  test_roundtrip<char16_t>(static_cast<char16_t>(0x4E2D)); // 中
+}
+
+TEST(SerializationTest, Char32Roundtrip) {
+  test_roundtrip<char32_t>(U'A');
+  test_roundtrip<char32_t>(U'中');
+  test_roundtrip<char32_t>(U'\0');
+  test_roundtrip<char32_t>(static_cast<char32_t>(0x10FFFF)); // Max Unicode
+  test_roundtrip<char32_t>(static_cast<char32_t>(0x1F600));  // Emoji 😀
+}
+
 // ============================================================================
 // Enum Tests
 // ============================================================================
diff --git a/cpp/fory/serialization/serializer.h 
b/cpp/fory/serialization/serializer.h
index 1cac3b37b..ea717c8da 100644
--- a/cpp/fory/serialization/serializer.h
+++ b/cpp/fory/serialization/serializer.h
@@ -242,4 +242,5 @@ template <typename T, typename Enable> struct Serializer {
 // Include all specialized serializers
 #include "fory/serialization/basic_serializer.h"
 #include "fory/serialization/enum_serializer.h"
+#include "fory/serialization/string_serializer.h"
 #include "fory/serialization/unsigned_serializer.h"
diff --git a/cpp/fory/serialization/string_serializer.h 
b/cpp/fory/serialization/string_serializer.h
new file mode 100644
index 000000000..f1bfb24c3
--- /dev/null
+++ b/cpp/fory/serialization/string_serializer.h
@@ -0,0 +1,421 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include "fory/serialization/context.h"
+#include "fory/serialization/serializer_traits.h"
+#include "fory/type/type.h"
+#include "fory/util/error.h"
+#include "fory/util/string_util.h"
+#include <cstdint>
+#include <string>
+#include <string_view>
+#include <vector>
+
+namespace fory {
+namespace serialization {
+
+// String encoding types as per xlang spec
+enum class StringEncoding : uint8_t {
+  LATIN1 = 0, // Latin1/ISO-8859-1
+  UTF16 = 1,  // UTF-16
+  UTF8 = 2,   // UTF-8
+};
+
+// ============================================================================
+// Internal helper functions for string serialization
+// ============================================================================
+
+namespace detail {
+
+/// Write string data with UTF-8 encoding
+inline void write_string_data(const char *data, size_t size,
+                              WriteContext &ctx) {
+  // Always use UTF-8 encoding for cross-language compatibility.
+  // Per xlang spec: write size shifted left by 2 bits, with encoding
+  // (UTF8) in the lower 2 bits. Use varuint36small encoding.
+  uint64_t length = static_cast<uint64_t>(size);
+  uint64_t size_with_encoding =
+      (length << 2) | static_cast<uint64_t>(StringEncoding::UTF8);
+  ctx.write_varuint36small(size_with_encoding);
+
+  // Write string bytes
+  if (size > 0) {
+    ctx.write_bytes(data, size);
+  }
+}
+
+/// Write UTF-16 string data, converting to UTF-8 or using native encoding
+inline void write_u16string_data(const char16_t *data, size_t size,
+                                 WriteContext &ctx) {
+  if (size == 0) {
+    // Empty string: write zero length with UTF8 encoding
+    ctx.write_varuint36small(static_cast<uint64_t>(StringEncoding::UTF8));
+    return;
+  }
+
+  const uint16_t *u16_data = reinterpret_cast<const uint16_t *>(data);
+
+  // Check if string can be encoded as Latin1 (more compact)
+  if (isLatin1(u16_data, size)) {
+    // Encode as Latin1 for compactness
+    uint64_t size_with_encoding = (static_cast<uint64_t>(size) << 2) |
+                                  
static_cast<uint64_t>(StringEncoding::LATIN1);
+    ctx.write_varuint36small(size_with_encoding);
+
+    // Write each char16_t as a single byte
+    for (size_t i = 0; i < size; ++i) {
+      ctx.write_uint8(static_cast<uint8_t>(data[i]));
+    }
+  } else {
+    // Convert to UTF-8
+    std::string utf8 = utf16ToUtf8(u16_data, size);
+    uint64_t size_with_encoding = (static_cast<uint64_t>(utf8.size()) << 2) |
+                                  static_cast<uint64_t>(StringEncoding::UTF8);
+    ctx.write_varuint36small(size_with_encoding);
+    if (!utf8.empty()) {
+      ctx.write_bytes(utf8.data(), utf8.size());
+    }
+  }
+}
+
+/// Read string data and return as std::string
+inline std::string read_string_data(ReadContext &ctx) {
+  // Read size with encoding using varuint36small
+  uint64_t size_with_encoding = ctx.read_varuint36small(ctx.error());
+  if (FORY_PREDICT_FALSE(ctx.has_error())) {
+    return std::string();
+  }
+
+  // Extract size and encoding from lower 2 bits
+  uint64_t length = size_with_encoding >> 2;
+  StringEncoding encoding =
+      static_cast<StringEncoding>(size_with_encoding & 0x3);
+
+  if (length == 0) {
+    return std::string();
+  }
+
+  // Validate length against buffer remaining size
+  if (length > ctx.buffer().remaining_size()) {
+    ctx.set_error(Error::invalid_data("String length exceeds buffer size"));
+    return std::string();
+  }
+
+  // Handle different encodings
+  switch (encoding) {
+  case StringEncoding::LATIN1: {
+    std::vector<uint8_t> bytes(length);
+    ctx.read_bytes(bytes.data(), length, ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return std::string();
+    }
+    return latin1ToUtf8(bytes.data(), length);
+  }
+  case StringEncoding::UTF16: {
+    if (length % 2 != 0) {
+      ctx.set_error(Error::invalid_data("UTF-16 length must be even"));
+      return std::string();
+    }
+    std::vector<uint16_t> utf16_chars(length / 2);
+    ctx.read_bytes(reinterpret_cast<uint8_t *>(utf16_chars.data()), length,
+                   ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return std::string();
+    }
+    return utf16ToUtf8(utf16_chars.data(), utf16_chars.size());
+  }
+  case StringEncoding::UTF8: {
+    // UTF-8: read bytes directly
+    std::string result(length, '\0');
+    ctx.read_bytes(&result[0], length, ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return std::string();
+    }
+    return result;
+  }
+  default:
+    ctx.set_error(
+        Error::encoding_error("Unknown string encoding: " +
+                              std::to_string(static_cast<int>(encoding))));
+    return std::string();
+  }
+}
+
+/// Read string data and return as std::u16string
+inline std::u16string read_u16string_data(ReadContext &ctx) {
+  // Read size with encoding using varuint36small
+  uint64_t size_with_encoding = ctx.read_varuint36small(ctx.error());
+  if (FORY_PREDICT_FALSE(ctx.has_error())) {
+    return std::u16string();
+  }
+
+  // Extract size and encoding from lower 2 bits
+  uint64_t length = size_with_encoding >> 2;
+  StringEncoding encoding =
+      static_cast<StringEncoding>(size_with_encoding & 0x3);
+
+  if (length == 0) {
+    return std::u16string();
+  }
+
+  // Validate length against buffer remaining size
+  if (length > ctx.buffer().remaining_size()) {
+    ctx.set_error(Error::invalid_data("String length exceeds buffer size"));
+    return std::u16string();
+  }
+
+  // Handle different encodings
+  switch (encoding) {
+  case StringEncoding::LATIN1: {
+    // Latin1 bytes map directly to char16_t (codepoints 0-255)
+    std::u16string result(length, u'\0');
+    for (size_t i = 0; i < length; ++i) {
+      result[i] = static_cast<char16_t>(ctx.read_uint8(ctx.error()));
+      if (FORY_PREDICT_FALSE(ctx.has_error())) {
+        return std::u16string();
+      }
+    }
+    return result;
+  }
+  case StringEncoding::UTF16: {
+    if (length % 2 != 0) {
+      ctx.set_error(Error::invalid_data("UTF-16 length must be even"));
+      return std::u16string();
+    }
+    std::u16string result(length / 2, u'\0');
+    ctx.read_bytes(reinterpret_cast<uint8_t *>(&result[0]), length,
+                   ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return std::u16string();
+    }
+    return result;
+  }
+  case StringEncoding::UTF8: {
+    // Read UTF-8 bytes and convert to UTF-16
+    std::string utf8(length, '\0');
+    ctx.read_bytes(&utf8[0], length, ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return std::u16string();
+    }
+    return utf8ToUtf16(utf8, true /* little endian */);
+  }
+  default:
+    ctx.set_error(
+        Error::encoding_error("Unknown string encoding: " +
+                              std::to_string(static_cast<int>(encoding))));
+    return std::u16string();
+  }
+}
+
+} // namespace detail
+
+// ============================================================================
+// std::string Serializer
+// ============================================================================
+
+/// std::string serializer using UTF-8 encoding per xlang spec
+template <> struct Serializer<std::string> {
+  static constexpr TypeId type_id = TypeId::STRING;
+
+  static inline void write_type_info(WriteContext &ctx) {
+    ctx.write_varuint32(static_cast<uint32_t>(type_id));
+  }
+
+  static inline void read_type_info(ReadContext &ctx) {
+    uint32_t actual = ctx.read_varuint32(ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return;
+    }
+    if (actual != static_cast<uint32_t>(type_id)) {
+      ctx.set_error(
+          Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
+    }
+  }
+
+  static inline void write(const std::string &value, WriteContext &ctx,
+                           bool write_ref, bool write_type,
+                           bool has_generics = false) {
+    write_not_null_ref_flag(ctx, write_ref);
+    if (write_type) {
+      ctx.write_varuint32(static_cast<uint32_t>(type_id));
+    }
+    write_data(value, ctx);
+  }
+
+  static inline void write_data(const std::string &value, WriteContext &ctx) {
+    detail::write_string_data(value.data(), value.size(), ctx);
+  }
+
+  static inline void write_data_generic(const std::string &value,
+                                        WriteContext &ctx, bool has_generics) {
+    write_data(value, ctx);
+  }
+
+  static inline std::string read(ReadContext &ctx, bool read_ref,
+                                 bool read_type) {
+    bool has_value = consume_ref_flag(ctx, read_ref);
+    if (ctx.has_error() || !has_value) {
+      return std::string();
+    }
+    if (read_type) {
+      uint32_t type_id_read = ctx.read_varuint32(ctx.error());
+      if (FORY_PREDICT_FALSE(ctx.has_error())) {
+        return std::string();
+      }
+      if (type_id_read != static_cast<uint32_t>(type_id)) {
+        ctx.set_error(
+            Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
+        return std::string();
+      }
+    }
+    return read_data(ctx);
+  }
+
+  static inline std::string read_data(ReadContext &ctx) {
+    return detail::read_string_data(ctx);
+  }
+
+  static inline std::string read_data_generic(ReadContext &ctx,
+                                              bool has_generics) {
+    return read_data(ctx);
+  }
+
+  static inline std::string read_with_type_info(ReadContext &ctx, bool 
read_ref,
+                                                const TypeInfo &type_info) {
+    return read(ctx, read_ref, false);
+  }
+};
+
+// ============================================================================
+// std::string_view Serializer (write-only, reads as std::string)
+// ============================================================================
+
+/// std::string_view serializer - write-only for zero-copy serialization
+/// Note: Deserialization returns std::string since string_view requires
+/// stable backing storage
+template <> struct Serializer<std::string_view> {
+  static constexpr TypeId type_id = TypeId::STRING;
+
+  static inline void write_type_info(WriteContext &ctx) {
+    ctx.write_varuint32(static_cast<uint32_t>(type_id));
+  }
+
+  static inline void write(std::string_view value, WriteContext &ctx,
+                           bool write_ref, bool write_type,
+                           bool has_generics = false) {
+    write_not_null_ref_flag(ctx, write_ref);
+    if (write_type) {
+      ctx.write_varuint32(static_cast<uint32_t>(type_id));
+    }
+    write_data(value, ctx);
+  }
+
+  static inline void write_data(std::string_view value, WriteContext &ctx) {
+    detail::write_string_data(value.data(), value.size(), ctx);
+  }
+
+  static inline void write_data_generic(std::string_view value,
+                                        WriteContext &ctx, bool has_generics) {
+    write_data(value, ctx);
+  }
+};
+
+// ============================================================================
+// std::u16string Serializer
+// ============================================================================
+
+/// std::u16string serializer with UTF-16 support
+template <> struct Serializer<std::u16string> {
+  static constexpr TypeId type_id = TypeId::STRING;
+
+  static inline void write_type_info(WriteContext &ctx) {
+    ctx.write_varuint32(static_cast<uint32_t>(type_id));
+  }
+
+  static inline void read_type_info(ReadContext &ctx) {
+    uint32_t actual = ctx.read_varuint32(ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return;
+    }
+    if (actual != static_cast<uint32_t>(type_id)) {
+      ctx.set_error(
+          Error::type_mismatch(actual, static_cast<uint32_t>(type_id)));
+    }
+  }
+
+  static inline void write(const std::u16string &value, WriteContext &ctx,
+                           bool write_ref, bool write_type,
+                           bool has_generics = false) {
+    write_not_null_ref_flag(ctx, write_ref);
+    if (write_type) {
+      ctx.write_varuint32(static_cast<uint32_t>(type_id));
+    }
+    write_data(value, ctx);
+  }
+
+  static inline void write_data(const std::u16string &value,
+                                WriteContext &ctx) {
+    detail::write_u16string_data(value.data(), value.size(), ctx);
+  }
+
+  static inline void write_data_generic(const std::u16string &value,
+                                        WriteContext &ctx, bool has_generics) {
+    write_data(value, ctx);
+  }
+
+  static inline std::u16string read(ReadContext &ctx, bool read_ref,
+                                    bool read_type) {
+    bool has_value = consume_ref_flag(ctx, read_ref);
+    if (ctx.has_error() || !has_value) {
+      return std::u16string();
+    }
+    if (read_type) {
+      uint32_t type_id_read = ctx.read_varuint32(ctx.error());
+      if (FORY_PREDICT_FALSE(ctx.has_error())) {
+        return std::u16string();
+      }
+      if (type_id_read != static_cast<uint32_t>(type_id)) {
+        ctx.set_error(
+            Error::type_mismatch(type_id_read, 
static_cast<uint32_t>(type_id)));
+        return std::u16string();
+      }
+    }
+    return read_data(ctx);
+  }
+
+  static inline std::u16string read_data(ReadContext &ctx) {
+    return detail::read_u16string_data(ctx);
+  }
+
+  static inline std::u16string read_data_generic(ReadContext &ctx,
+                                                 bool has_generics) {
+    return read_data(ctx);
+  }
+
+  static inline std::u16string read_with_type_info(ReadContext &ctx,
+                                                   bool read_ref,
+                                                   const TypeInfo &type_info) {
+    return read(ctx, read_ref, false);
+  }
+};
+
+} // namespace serialization
+} // namespace fory
diff --git a/cpp/fory/type/type.h b/cpp/fory/type/type.h
index 9501e4fc4..75c622f84 100644
--- a/cpp/fory/type/type.h
+++ b/cpp/fory/type/type.h
@@ -116,6 +116,12 @@ enum class TypeId : int32_t {
   U32 = 66,
   // a 64-bit unsigned integer.
   U64 = 67,
+  // 8-bits character.
+  CHAR = 68,
+  // 16-bits character
+  CHAR16 = 69,
+  // 32-bits character
+  CHAR32 = 70,
   // Unsigned integer array types (native mode only)
   // one-dimensional uint16 array.
   U16_ARRAY = 73,
diff --git a/docs/guide/cpp/basic-serialization.md 
b/docs/guide/cpp/basic-serialization.md
new file mode 100644
index 000000000..83182495e
--- /dev/null
+++ b/docs/guide/cpp/basic-serialization.md
@@ -0,0 +1,238 @@
+---
+title: Basic Serialization
+sidebar_position: 2
+id: cpp_basic_serialization
+license: |
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+---
+
+This page covers basic object graph serialization and the core serialization 
APIs.
+
+## Object Graph Serialization
+
+Apache Fory™ provides automatic serialization of complex object graphs, 
preserving the structure and relationships between objects. The `FORY_STRUCT` 
macro generates efficient serialization code at compile time, eliminating 
runtime overhead.
+
+**Key capabilities:**
+
+- Nested struct serialization with arbitrary depth
+- Collection types (vector, set, map)
+- Optional fields with `std::optional<T>`
+- Smart pointers (`std::shared_ptr`, `std::unique_ptr`)
+- Automatic handling of primitive types and strings
+- Efficient binary encoding with variable-length integers
+
+```cpp
+#include "fory/serialization/fory.h"
+#include <vector>
+#include <map>
+
+using namespace fory::serialization;
+
+// Define structs
+struct Address {
+  std::string street;
+  std::string city;
+  std::string country;
+
+  bool operator==(const Address &other) const {
+    return street == other.street && city == other.city &&
+           country == other.country;
+  }
+};
+FORY_STRUCT(Address, street, city, country);
+
+struct Person {
+  std::string name;
+  int32_t age;
+  Address address;
+  std::vector<std::string> hobbies;
+  std::map<std::string, std::string> metadata;
+
+  bool operator==(const Person &other) const {
+    return name == other.name && age == other.age &&
+           address == other.address && hobbies == other.hobbies &&
+           metadata == other.metadata;
+  }
+};
+FORY_STRUCT(Person, name, age, address, hobbies, metadata);
+
+int main() {
+  auto fory = Fory::builder().xlang(true).build();
+  fory.register_struct<Address>(100);
+  fory.register_struct<Person>(200);
+
+  Person person{
+      "John Doe",
+      30,
+      {"123 Main St", "New York", "USA"},
+      {"reading", "coding"},
+      {{"role", "developer"}}
+  };
+
+  auto result = fory.serialize(person);
+  auto decoded = fory.deserialize<Person>(result.value());
+  assert(person == decoded.value());
+}
+```
+
+## Serialization APIs
+
+### Serialize to New Vector
+
+```cpp
+auto fory = Fory::builder().xlang(true).build();
+fory.register_struct<MyStruct>(1);
+
+MyStruct obj{/* ... */};
+
+// Serialize - returns Result<std::vector<uint8_t>, Error>
+auto result = fory.serialize(obj);
+if (result.ok()) {
+  std::vector<uint8_t> bytes = std::move(result).value();
+  // Use bytes...
+} else {
+  // Handle error
+  std::cerr << result.error().to_string() << std::endl;
+}
+```
+
+### Serialize to Existing Buffer
+
+```cpp
+// Serialize to existing Buffer (fastest path)
+Buffer buffer;
+auto result = fory.serialize_to(obj, buffer);
+if (result.ok()) {
+  size_t bytes_written = result.value();
+  // buffer now contains serialized data
+}
+
+// Serialize to existing vector (zero-copy)
+std::vector<uint8_t> output;
+auto result = fory.serialize_to(obj, output);
+if (result.ok()) {
+  size_t bytes_written = result.value();
+  // output now contains serialized data
+}
+```
+
+### Deserialize from Byte Array
+
+```cpp
+// Deserialize from raw pointer
+auto result = fory.deserialize<MyStruct>(data_ptr, data_size);
+if (result.ok()) {
+  MyStruct obj = std::move(result).value();
+}
+
+// Deserialize from vector
+std::vector<uint8_t> data = /* ... */;
+auto result = fory.deserialize<MyStruct>(data);
+
+// Deserialize from Buffer (updates reader_index)
+Buffer buffer(data);
+auto result = fory.deserialize<MyStruct>(buffer);
+```
+
+## Error Handling
+
+Fory uses a `Result<T, Error>` type for error handling:
+
+```cpp
+auto result = fory.serialize(obj);
+
+// Check if operation succeeded
+if (result.ok()) {
+  auto value = std::move(result).value();
+  // Use value...
+} else {
+  Error error = result.error();
+  std::cerr << "Error: " << error.to_string() << std::endl;
+}
+
+// Or use FORY_TRY macro for early return
+FORY_TRY(bytes, fory.serialize(obj));
+// Use bytes directly...
+```
+
+Common error types:
+
+- `Error::type_mismatch` - Type ID mismatch during deserialization
+- `Error::invalid_data` - Invalid or corrupted data
+- `Error::buffer_out_of_bound` - Buffer overflow/underflow
+- `Error::type_error` - Type registration error
+
+## The FORY_STRUCT Macro
+
+The `FORY_STRUCT` macro registers a struct for serialization:
+
+```cpp
+struct MyStruct {
+  int32_t x;
+  std::string y;
+  std::vector<int32_t> z;
+};
+
+// Must be in the same namespace as the struct
+FORY_STRUCT(MyStruct, x, y, z);
+```
+
+The macro:
+
+1. Generates compile-time field metadata
+2. Enables ADL (Argument-Dependent Lookup) for serialization
+3. Creates efficient serialization code via template specialization
+
+**Requirements:**
+
+- Must be placed in the same namespace as the struct (for ADL)
+- All listed fields must be serializable types
+- Field order in the macro determines serialization order
+
+## Nested Structs
+
+Nested structs are fully supported:
+
+```cpp
+struct Inner {
+  int32_t value;
+};
+FORY_STRUCT(Inner, value);
+
+struct Outer {
+  Inner inner;
+  std::string label;
+};
+FORY_STRUCT(Outer, inner, label);
+
+// Both must be registered
+fory.register_struct<Inner>(1);
+fory.register_struct<Outer>(2);
+```
+
+## Performance Tips
+
+- **Buffer Reuse**: Use `serialize_to(obj, buffer)` with pre-allocated buffers
+- **Pre-registration**: Register all types before serialization starts
+- **Single-Threaded**: Use `build()` instead of `build_thread_safe()` when 
possible
+- **Disable Tracking**: Use `track_ref(false)` when references aren't needed
+- **Compact Encoding**: Variable-length encoding for space efficiency
+
+## Related Topics
+
+- [Configuration](configuration.md) - Builder options
+- [Type Registration](type-registration.md) - Registering types
+- [Supported Types](supported-types.md) - All supported types
diff --git a/docs/guide/cpp/configuration.md b/docs/guide/cpp/configuration.md
new file mode 100644
index 000000000..5febe5559
--- /dev/null
+++ b/docs/guide/cpp/configuration.md
@@ -0,0 +1,189 @@
+---
+title: Configuration
+sidebar_position: 1
+id: cpp_configuration
+license: |
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+---
+
+This page covers Fory configuration options and serialization modes.
+
+## Serialization Modes
+
+Apache Fory™ supports two serialization modes:
+
+### SchemaConsistent Mode (Default)
+
+Type declarations must match exactly between peers:
+
+```cpp
+auto fory = Fory::builder().build(); // SchemaConsistent by default
+```
+
+### Compatible Mode
+
+Allows independent schema evolution:
+
+```cpp
+auto fory = Fory::builder().compatible(true).build();
+```
+
+## Builder Pattern
+
+Use `ForyBuilder` to construct Fory instances with custom configuration:
+
+```cpp
+#include "fory/serialization/fory.h"
+
+using namespace fory::serialization;
+
+// Default configuration
+auto fory = Fory::builder().build();
+
+// Compatible mode for schema evolution
+auto fory = Fory::builder()
+    .compatible(true)
+    .build();
+
+// Cross-language mode
+auto fory = Fory::builder()
+    .xlang(true)
+    .build();
+
+// Full configuration
+auto fory = Fory::builder()
+    .compatible(true)
+    .xlang(true)
+    .track_ref(true)
+    .max_dyn_depth(10)
+    .check_struct_version(true)
+    .build();
+```
+
+## Configuration Options
+
+### xlang(bool)
+
+Enable/disable cross-language (xlang) serialization mode.
+
+```cpp
+auto fory = Fory::builder()
+    .xlang(true)  // Enable cross-language compatibility
+    .build();
+```
+
+When enabled, includes metadata for cross-language compatibility with Java, 
Python, Go, Rust, and JavaScript.
+
+**Default:** `true`
+
+### compatible(bool)
+
+Enable/disable compatible mode for schema evolution.
+
+```cpp
+auto fory = Fory::builder()
+    .compatible(true)  // Enable schema evolution
+    .build();
+```
+
+When enabled, supports reading data serialized with different schema versions.
+
+**Default:** `false`
+
+### track_ref(bool)
+
+Enable/disable reference tracking for shared and circular references.
+
+```cpp
+auto fory = Fory::builder()
+    .track_ref(true)  // Enable reference tracking
+    .build();
+```
+
+When enabled, avoids duplicating shared objects and handles cycles.
+
+**Default:** `true`
+
+### max_dyn_depth(uint32_t)
+
+Set maximum allowed nesting depth for dynamically-typed objects.
+
+```cpp
+auto fory = Fory::builder()
+    .max_dyn_depth(10)  // Allow up to 10 levels
+    .build();
+```
+
+This limits the maximum depth for nested polymorphic object serialization 
(e.g., `shared_ptr<Base>`, `unique_ptr<Base>`). This prevents stack overflow 
from deeply nested structures in dynamic serialization scenarios.
+
+**Default:** `5`
+
+**When to adjust:**
+
+- **Increase**: For legitimate deeply nested data structures
+- **Decrease**: For stricter security requirements or shallow data structures
+
+### check_struct_version(bool)
+
+Enable/disable struct version checking.
+
+```cpp
+auto fory = Fory::builder()
+    .check_struct_version(true)  // Enable version checking
+    .build();
+```
+
+When enabled, validates type hashes to detect schema mismatches.
+
+**Default:** `false`
+
+## Thread-Safe vs Single-Threaded
+
+### Single-Threaded (Fastest)
+
+```cpp
+auto fory = Fory::builder()
+    .xlang(true)
+    .build();  // Returns Fory
+```
+
+Single-threaded `Fory` is the fastest option, but NOT thread-safe. Use one 
instance per thread.
+
+### Thread-Safe
+
+```cpp
+auto fory = Fory::builder()
+    .xlang(true)
+    .build_thread_safe();  // Returns ThreadSafeFory
+```
+
+`ThreadSafeFory` uses a pool of Fory instances to provide thread-safe 
serialization. Slightly slower due to pool overhead, but safe to use from 
multiple threads concurrently.
+
+## Configuration Summary
+
+| Option                       | Description                             | 
Default |
+| ---------------------------- | --------------------------------------- | 
------- |
+| `xlang(bool)`                | Enable cross-language mode              | 
`true`  |
+| `compatible(bool)`           | Enable schema evolution                 | 
`false` |
+| `track_ref(bool)`            | Enable reference tracking               | 
`true`  |
+| `max_dyn_depth(uint32_t)`    | Maximum nesting depth for dynamic types | `5` 
    |
+| `check_struct_version(bool)` | Enable struct version checking          | 
`false` |
+
+## Related Topics
+
+- [Basic Serialization](basic-serialization.md) - Using configured Fory
+- [Cross-Language](cross-language.md) - XLANG mode details
+- [Type Registration](type-registration.md) - Registering types
diff --git a/docs/guide/cpp/cross-language.md b/docs/guide/cpp/cross-language.md
new file mode 100644
index 000000000..3bf81cdae
--- /dev/null
+++ b/docs/guide/cpp/cross-language.md
@@ -0,0 +1,271 @@
+---
+title: Cross-Language Serialization
+sidebar_position: 6
+id: cpp_cross_language
+license: |
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+---
+
+This page explains how to use Fory for cross-language serialization between 
C++ and other languages.
+
+## Overview
+
+Apache Fory™ enables seamless data exchange between C++, Java, Python, Go, 
Rust, and JavaScript. The xlang (cross-language) mode ensures binary 
compatibility across all supported languages.
+
+## Enabling Cross-Language Mode
+
+```cpp
+#include "fory/serialization/fory.h"
+
+using namespace fory::serialization;
+
+auto fory = Fory::builder()
+    .xlang(true)  // Enable cross-language mode
+    .build();
+```
+
+## Cross-Language Example
+
+### C++ Producer
+
+```cpp
+#include "fory/serialization/fory.h"
+#include <fstream>
+
+using namespace fory::serialization;
+
+struct Message {
+  std::string topic;
+  int64_t timestamp;
+  std::map<std::string, std::string> headers;
+  std::vector<uint8_t> payload;
+
+  bool operator==(const Message &other) const {
+    return topic == other.topic && timestamp == other.timestamp &&
+           headers == other.headers && payload == other.payload;
+  }
+};
+FORY_STRUCT(Message, topic, timestamp, headers, payload);
+
+int main() {
+  auto fory = Fory::builder().xlang(true).build();
+  fory.register_struct<Message>(100);
+
+  Message msg{
+      "events.user",
+      1699999999000,
+      {{"content-type", "application/json"}},
+      {'h', 'e', 'l', 'l', 'o'}
+  };
+
+  auto result = fory.serialize(msg);
+  if (result.ok()) {
+    auto bytes = std::move(result).value();
+    // Write to file, send over network, etc.
+    std::ofstream file("message.bin", std::ios::binary);
+    file.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
+  }
+  return 0;
+}
+```
+
+### Java Consumer
+
+```java
+import org.apache.fory.Fory;
+import org.apache.fory.config.Language;
+
+public class Message {
+    public String topic;
+    public long timestamp;
+    public Map<String, String> headers;
+    public byte[] payload;
+}
+
+public class Consumer {
+    public static void main(String[] args) throws Exception {
+        Fory fory = Fory.builder()
+            .withLanguage(Language.XLANG)
+            .build();
+        fory.register(Message.class, 100);  // Same ID as C++
+
+        byte[] bytes = Files.readAllBytes(Path.of("message.bin"));
+        Message msg = (Message) fory.deserialize(bytes);
+
+        System.out.println("Topic: " + msg.topic);
+        System.out.println("Timestamp: " + msg.timestamp);
+    }
+}
+```
+
+### Python Consumer
+
+```python
+import pyfory
+
+class Message:
+    topic: str
+    timestamp: int
+    headers: dict[str, str]
+    payload: bytes
+
+fory = pyfory.Fory()
+fory.register(Message, type_id=100)  # Same ID as C++
+
+with open("message.bin", "rb") as f:
+    data = f.read()
+
+msg = fory.deserialize(data)
+print(f"Topic: {msg.topic}")
+print(f"Timestamp: {msg.timestamp}")
+```
+
+## Type Mapping
+
+### Primitive Types
+
+| C++ Type  | Java Type | Python Type | Go Type   | Rust Type |
+| --------- | --------- | ----------- | --------- | --------- |
+| `bool`    | `boolean` | `bool`      | `bool`    | `bool`    |
+| `int8_t`  | `byte`    | `int`       | `int8`    | `i8`      |
+| `int16_t` | `short`   | `int`       | `int16`   | `i16`     |
+| `int32_t` | `int`     | `int`       | `int32`   | `i32`     |
+| `int64_t` | `long`    | `int`       | `int64`   | `i64`     |
+| `float`   | `float`   | `float`     | `float32` | `f32`     |
+| `double`  | `double`  | `float`     | `float64` | `f64`     |
+
+### String Types
+
+| C++ Type      | Java Type | Python Type | Go Type  | Rust Type |
+| ------------- | --------- | ----------- | -------- | --------- |
+| `std::string` | `String`  | `str`       | `string` | `String`  |
+
+### Collection Types
+
+| C++ Type         | Java Type  | Python Type | Go Type          |
+| ---------------- | ---------- | ----------- | ---------------- |
+| `std::vector<T>` | `List<T>`  | `list`      | `[]T`            |
+| `std::set<T>`    | `Set<T>`   | `set`       | `map[T]struct{}` |
+| `std::map<K,V>`  | `Map<K,V>` | `dict`      | `map[K]V`        |
+
+### Temporal Types
+
+| C++ Type    | Java Type   | Python Type     | Go Type         |
+| ----------- | ----------- | --------------- | --------------- |
+| `Timestamp` | `Instant`   | `datetime`      | `time.Time`     |
+| `Duration`  | `Duration`  | `timedelta`     | `time.Duration` |
+| `LocalDate` | `LocalDate` | `datetime.date` | `time.Time`     |
+
+## Field Order Requirements
+
+**Critical:** Field will be sorted by their snake_cased field name, converted 
name must be considten across langauges
+
+### C++
+
+```cpp
+struct Person {
+  std::string name;   // Field 0
+  int32_t age;        // Field 1
+  std::string email;  // Field 2
+};
+FORY_STRUCT(Person, name, age, email);  // Order matters!
+```
+
+### Java
+
+```java
+public class Person {
+    public String name;   // Field 0
+    public int age;       // Field 1
+    public String email;  // Field 2
+}
+```
+
+### Python
+
+```python
+class Person:
+    name: str    # Field 0
+    age: int     # Field 1
+    email: str   # Field 2
+```
+
+## Type ID Consistency
+
+All languages must use the same type IDs:
+
+```cpp
+// C++
+fory.register_struct<Person>(100);
+fory.register_struct<Address>(101);
+fory.register_struct<Order>(102);
+```
+
+```java
+// Java
+fory.register(Person.class, 100);
+fory.register(Address.class, 101);
+fory.register(Order.class, 102);
+```
+
+```python
+# Python
+fory.register(Person, type_id=100)
+fory.register(Address, type_id=101)
+fory.register(Order, type_id=102)
+```
+
+## Compatible Mode
+
+For schema evolution across language boundaries:
+
+```cpp
+// C++ with compatible mode
+auto fory = Fory::builder()
+    .xlang(true)
+    .compatible(true)  // Enable schema evolution
+    .build();
+```
+
+Compatible mode allows:
+
+- Adding new fields (with defaults)
+- Removing unused fields
+- Reordering fields
+
+## Troubleshooting
+
+### Type Mismatch Errors
+
+```
+Error: Type mismatch: expected 100, got 101
+```
+
+**Solution:** Ensure type IDs match across all languages.
+
+### Encoding Errors
+
+```
+Error: Invalid UTF-8 sequence
+```
+
+**Solution:** Ensure strings are valid UTF-8 in all languages.
+
+## Related Topics
+
+- [Configuration](configuration.md) - Builder options
+- [Type Registration](type-registration.md) - Registering types
+- [Supported Types](supported-types.md) - Type compatibility
diff --git a/docs/guide/cpp/index.md b/docs/guide/cpp/index.md
new file mode 100644
index 000000000..19c40d992
--- /dev/null
+++ b/docs/guide/cpp/index.md
@@ -0,0 +1,245 @@
+---
+title: C++ Serialization Guide
+sidebar_position: 0
+id: cpp_serialization_index
+license: |
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the 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 safety.
+
+The C++ implementation provides high-performance serialization with 
compile-time type safety using modern C++17 features and template 
metaprogramming.
+
+## 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 macro-based struct 
registration
+- **🔄 Reference Tracking**: Automatic tracking of shared and circular 
references
+- **📦 Schema Evolution**: Compatible mode for independent schema changes
+- **⚡ Two Modes**: Object graph serialization and zero-copy row-based format
+- **🧵 Thread Safety**: Both single-threaded (fastest) and thread-safe variants
+
+## Installation
+
+The C++ implementation supports both CMake and Bazel build systems.
+
+### Prerequisites
+
+- CMake 3.16+ (for CMake build) or Bazel 8+ (for Bazel build)
+- C++17 compatible compiler (GCC 7+, Clang 5+, MSVC 2017+)
+
+### Using CMake (Recommended)
+
+The easiest way to use Fory is with CMake's `FetchContent` module:
+
+```cmake
+cmake_minimum_required(VERSION 3.16)
+project(my_project LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+include(FetchContent)
+FetchContent_Declare(
+    fory
+    GIT_REPOSITORY https://github.com/apache/fory.git
+    GIT_TAG        v0.14.0
+    SOURCE_SUBDIR  cpp
+)
+FetchContent_MakeAvailable(fory)
+
+add_executable(my_app main.cc)
+target_link_libraries(my_app PRIVATE fory::serialization)
+```
+
+Then build and run:
+
+```bash
+mkdir build && cd build
+cmake .. -DCMAKE_BUILD_TYPE=Release
+cmake --build . --parallel
+./my_app
+```
+
+### Using Bazel
+
+Create a `MODULE.bazel` file in your project root:
+
+```bazel
+module(
+    name = "my_project",
+    version = "1.0.0",
+)
+
+bazel_dep(name = "rules_cc", version = "0.1.1")
+
+bazel_dep(name = "fory", version = "0.14.0")
+git_override(
+    module_name = "fory",
+    remote = "https://github.com/apache/fory.git";,
+    commit = "v0.14.0",  # Or use a specific commit hash for reproducibility
+)
+```
+
+Create a `BUILD` file for your application:
+
+```bazel
+cc_binary(
+    name = "my_app",
+    srcs = ["main.cc"],
+    deps = ["@fory//cpp/fory/serialization:fory_serialization"],
+)
+```
+
+Then build and run:
+
+```bash
+bazel build //:my_app
+bazel run //:my_app
+```
+
+For local development, you can use `local_path_override` instead:
+
+```bazel
+bazel_dep(name = "fory", version = "0.14.0")
+local_path_override(
+    module_name = "fory",
+    path = "/path/to/fory",
+)
+```
+
+### Examples
+
+See the [examples/cpp](https://github.com/apache/fory/tree/main/examples/cpp) 
directory for complete working examples:
+
+- 
[hello_world](https://github.com/apache/fory/tree/main/examples/cpp/hello_world)
 - Object graph serialization
+- [hello_row](https://github.com/apache/fory/tree/main/examples/cpp/hello_row) 
- Row format encoding
+
+## Quick Start
+
+### Basic Example
+
+```cpp
+#include "fory/serialization/fory.h"
+
+using namespace fory::serialization;
+
+// Define a struct
+struct Person {
+  std::string name;
+  int32_t age;
+  std::vector<std::string> hobbies;
+
+  bool operator==(const Person &other) const {
+    return name == other.name && age == other.age && hobbies == other.hobbies;
+  }
+};
+
+// Register the struct with Fory (must be in the same namespace)
+FORY_STRUCT(Person, name, age, hobbies);
+
+int main() {
+  // Create a Fory instance
+  auto fory = Fory::builder()
+      .xlang(true)          // Enable cross-language mode
+      .track_ref(false)     // Disable reference tracking for simple types
+      .build();
+
+  // Register the type with a unique ID
+  fory.register_struct<Person>(1);
+
+  // Create an object
+  Person person{"Alice", 30, {"reading", "coding"}};
+
+  // Serialize
+  auto result = fory.serialize(person);
+  if (!result.ok()) {
+    // Handle error
+    return 1;
+  }
+  std::vector<uint8_t> bytes = std::move(result).value();
+
+  // Deserialize
+  auto deser_result = fory.deserialize<Person>(bytes);
+  if (!deser_result.ok()) {
+    // Handle error
+    return 1;
+  }
+  Person decoded = std::move(deser_result).value();
+
+  assert(person == decoded);
+  return 0;
+}
+```
+
+## Thread Safety
+
+Apache Fory™ C++ provides two variants for different threading needs:
+
+### Single-Threaded (Fastest)
+
+```cpp
+// Single-threaded Fory - fastest, NOT thread-safe
+auto fory = Fory::builder()
+    .xlang(true)
+    .build();
+```
+
+### Thread-Safe
+
+```cpp
+// Thread-safe Fory - uses context pools
+auto fory = Fory::builder()
+    .xlang(true)
+    .build_thread_safe();
+
+// Can be used from multiple threads safely
+std::thread t1([&]() {
+  auto result = fory.serialize(obj1);
+});
+std::thread t2([&]() {
+  auto result = fory.serialize(obj2);
+});
+```
+
+**Tip:** Perform type registrations before spawning threads so every worker 
sees the same metadata.
+
+## 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
+
+## Next Steps
+
+- [Configuration](configuration.md) - Builder options and modes
+- [Basic Serialization](basic-serialization.md) - Object graph serialization
+- [Schema Evolution](schema-evolution.md) - Compatible mode and schema changes
+- [Type Registration](type-registration.md) - Registering types
+- [Supported Types](supported-types.md) - All supported types
+- [Cross-Language](cross-language.md) - XLANG mode
+- [Row Format](row-format.md) - Zero-copy row-based format
diff --git a/docs/guide/cpp/row-format.md b/docs/guide/cpp/row-format.md
new file mode 100644
index 000000000..62e405dd4
--- /dev/null
+++ b/docs/guide/cpp/row-format.md
@@ -0,0 +1,516 @@
+---
+title: Row Format
+sidebar_position: 7
+id: cpp_row_format
+license: |
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+---
+
+This page covers the row-based serialization format for high-performance, 
cache-friendly data access.
+
+## Overview
+
+Apache Fory™ Row Format is a binary format optimized for:
+
+- **Random Access**: Read any field without deserializing the entire object
+- **Zero-Copy**: Direct memory access without data copying
+- **Cache-Friendly**: Contiguous memory layout for CPU cache efficiency
+- **Columnar Conversion**: Easy conversion to Apache Arrow format
+- **Partial Serialization**: Serialize only needed fields
+
+## When to Use Row Format
+
+| Use Case                      | Row Format | Object Graph |
+| ----------------------------- | ---------- | ------------ |
+| Analytics/OLAP                | ✅         | ❌           |
+| Random field access           | ✅         | ❌           |
+| Full object serialization     | ❌         | ✅           |
+| Complex object graphs         | ❌         | ✅           |
+| Reference tracking            | ❌         | ✅           |
+| Cross-language (simple types) | ✅         | ✅           |
+
+## Quick Start
+
+```cpp
+#include "fory/encoder/row_encoder.h"
+#include "fory/row/writer.h"
+
+using namespace fory::row;
+using namespace fory::row::encoder;
+
+// Define a struct
+struct Person {
+  int32_t id;
+  std::string name;
+  float score;
+};
+
+// Register field metadata (required for row encoding)
+FORY_FIELD_INFO(Person, id, name, score);
+
+int main() {
+  // Create encoder
+  RowEncoder<Person> encoder;
+
+  // Encode a person
+  Person person{1, "Alice", 95.5f};
+  encoder.Encode(person);
+
+  // Get the encoded row
+  auto row = encoder.GetWriter().ToRow();
+
+  // Random access to fields
+  int32_t id = row->GetInt32(0);
+  std::string name = row->GetString(1);
+  float score = row->GetFloat(2);
+
+  assert(id == 1);
+  assert(name == "Alice");
+  assert(score == 95.5f);
+
+  return 0;
+}
+```
+
+## Row Encoder
+
+### Basic Usage
+
+The `RowEncoder<T>` template class provides type-safe encoding:
+
+```cpp
+#include "fory/encoder/row_encoder.h"
+
+// Define struct with FORY_FIELD_INFO
+struct Point {
+  double x;
+  double y;
+};
+FORY_FIELD_INFO(Point, x, y);
+
+// Create encoder
+RowEncoder<Point> encoder;
+
+// Access schema (for inspection)
+const Schema& schema = encoder.GetSchema();
+std::cout << "Fields: " << schema.field_names().size() << std::endl;
+
+// Encode value
+Point p{1.0, 2.0};
+encoder.Encode(p);
+
+// Get result as Row
+auto row = encoder.GetWriter().ToRow();
+```
+
+### Nested Structs
+
+```cpp
+struct Address {
+  std::string city;
+  std::string country;
+};
+FORY_FIELD_INFO(Address, city, country);
+
+struct Person {
+  std::string name;
+  Address address;
+};
+FORY_FIELD_INFO(Person, name, address);
+
+// Encode nested struct
+RowEncoder<Person> encoder;
+Person person{"Alice", {"New York", "USA"}};
+encoder.Encode(person);
+
+auto row = encoder.GetWriter().ToRow();
+std::string name = row->GetString(0);
+
+// Access nested struct
+auto address_row = row->GetStruct(1);
+std::string city = address_row->GetString(0);
+std::string country = address_row->GetString(1);
+```
+
+### Arrays / Lists
+
+```cpp
+struct Record {
+  std::vector<int32_t> values;
+  std::string label;
+};
+FORY_FIELD_INFO(Record, values, label);
+
+RowEncoder<Record> encoder;
+Record record{{1, 2, 3, 4, 5}, "test"};
+encoder.Encode(record);
+
+auto row = encoder.GetWriter().ToRow();
+auto array = row->GetArray(0);
+
+int count = array->num_elements();
+for (int i = 0; i < count; i++) {
+  int32_t value = array->GetInt32(i);
+}
+```
+
+### Encoding Arrays Directly
+
+```cpp
+// Encode a vector directly (not inside a struct)
+std::vector<Person> people{
+    {"Alice", {"NYC", "USA"}},
+    {"Bob", {"London", "UK"}}
+};
+
+RowEncoder<decltype(people)> encoder;
+encoder.Encode(people);
+
+// Get array data
+auto array = encoder.GetWriter().CopyToArrayData();
+auto first_person = array->GetStruct(0);
+std::string first_name = first_person->GetString(0);
+```
+
+## Row Data Access
+
+### Row Class
+
+The `Row` class provides random access to struct fields:
+
+```cpp
+class Row {
+public:
+  // Null check
+  bool IsNullAt(int i) const;
+
+  // Primitive getters
+  bool GetBoolean(int i) const;
+  int8_t GetInt8(int i) const;
+  int16_t GetInt16(int i) const;
+  int32_t GetInt32(int i) const;
+  int64_t GetInt64(int i) const;
+  float GetFloat(int i) const;
+  double GetDouble(int i) const;
+
+  // String/binary getter
+  std::string GetString(int i) const;
+  std::vector<uint8_t> GetBinary(int i) const;
+
+  // Nested types
+  std::shared_ptr<Row> GetStruct(int i) const;
+  std::shared_ptr<ArrayData> GetArray(int i) const;
+  std::shared_ptr<MapData> GetMap(int i) const;
+
+  // Metadata
+  int num_fields() const;
+  SchemaPtr schema() const;
+
+  // Debug
+  std::string ToString() const;
+};
+```
+
+### ArrayData Class
+
+The `ArrayData` class provides access to list/array elements:
+
+```cpp
+class ArrayData {
+public:
+  // Null check
+  bool IsNullAt(int i) const;
+
+  // Element count
+  int num_elements() const;
+
+  // Primitive getters (same as Row)
+  int32_t GetInt32(int i) const;
+  // ... other primitives
+
+  // String getter
+  std::string GetString(int i) const;
+
+  // Nested types
+  std::shared_ptr<Row> GetStruct(int i) const;
+  std::shared_ptr<ArrayData> GetArray(int i) const;
+  std::shared_ptr<MapData> GetMap(int i) const;
+
+  // Type info
+  ListTypePtr type() const;
+};
+```
+
+### MapData Class
+
+The `MapData` class provides access to map key-value pairs:
+
+```cpp
+class MapData {
+public:
+  // Element count
+  int num_elements();
+
+  // Access keys and values as arrays
+  std::shared_ptr<ArrayData> keys_array();
+  std::shared_ptr<ArrayData> values_array();
+
+  // Type info
+  MapTypePtr type();
+};
+```
+
+## Schema and Types
+
+### Schema Definition
+
+Schemas define the structure of row data:
+
+```cpp
+#include "fory/row/schema.h"
+
+using namespace fory::row;
+
+// Create schema programmatically
+auto person_schema = schema({
+    field("id", int32()),
+    field("name", utf8()),
+    field("score", float32()),
+    field("active", boolean())
+});
+
+// Access schema info
+for (const auto& f : person_schema->fields()) {
+  std::cout << f->name() << ": " << f->type()->name() << std::endl;
+}
+```
+
+### Type System
+
+Available types for row format:
+
+```cpp
+// Primitive types
+DataTypePtr boolean();    // bool
+DataTypePtr int8();       // int8_t
+DataTypePtr int16();      // int16_t
+DataTypePtr int32();      // int32_t
+DataTypePtr int64();      // int64_t
+DataTypePtr float32();    // float
+DataTypePtr float64();    // double
+
+// String and binary
+DataTypePtr utf8();       // std::string
+DataTypePtr binary();     // std::vector<uint8_t>
+
+// Complex types
+DataTypePtr list(DataTypePtr element_type);
+DataTypePtr map(DataTypePtr key_type, DataTypePtr value_type);
+DataTypePtr struct_(std::vector<FieldPtr> fields);
+```
+
+### Type Inference
+
+The `RowEncodeTrait` template automatically infers types:
+
+```cpp
+// Type inference for primitives
+RowEncodeTrait<int32_t>::Type();  // Returns int32()
+RowEncodeTrait<float>::Type();    // Returns float32()
+RowEncodeTrait<std::string>::Type();  // Returns utf8()
+
+// Type inference for collections
+RowEncodeTrait<std::vector<int32_t>>::Type();  // Returns list(int32())
+
+// Type inference for maps
+RowEncodeTrait<std::map<std::string, int32_t>>::Type();
+// Returns map(utf8(), int32())
+
+// Type inference for structs (requires FORY_FIELD_INFO)
+RowEncodeTrait<Person>::Type();  // Returns struct_({...})
+RowEncodeTrait<Person>::Schema();  // Returns schema({...})
+```
+
+## Row Writer
+
+### RowWriter
+
+For manual row construction:
+
+```cpp
+#include "fory/row/writer.h"
+
+// Create schema
+auto my_schema = schema({
+    field("x", int32()),
+    field("y", float64()),
+    field("name", utf8())
+});
+
+// Create writer
+RowWriter writer(my_schema);
+writer.Reset();
+
+// Write fields
+writer.Write(0, 42);          // x = 42
+writer.Write(1, 3.14);        // y = 3.14
+writer.WriteString(2, "test"); // name = "test"
+
+// Get result
+auto row = writer.ToRow();
+```
+
+### ArrayWriter
+
+For manual array construction:
+
+```cpp
+// Create array type
+auto array_type = list(int32());
+
+// Create writer
+ArrayWriter writer(array_type);
+writer.Reset(5);  // 5 elements
+
+// Write elements
+for (int i = 0; i < 5; i++) {
+  writer.Write(i, i * 10);
+}
+
+// Get result
+auto array = writer.CopyToArrayData();
+```
+
+### Null Values
+
+```cpp
+// Set null at specific index
+writer.SetNullAt(2);  // Field 2 is null
+
+// Check null when reading
+if (!row->IsNullAt(2)) {
+  std::string value = row->GetString(2);
+}
+```
+
+## Memory Layout
+
+### Row Layout
+
+```
++------------------+--------------------+--------------------+
+|   Null Bitmap    |  Fixed-Size Data   | Variable-Size Data |
++------------------+--------------------+--------------------+
+|   ceil(n/8) B    |     8 * n bytes    |      variable      |
++------------------+--------------------+--------------------+
+```
+
+- **Null Bitmap**: One bit per field, indicates null values
+- **Fixed-Size Data**: 8 bytes per field (primitives stored directly, 
offset+size for variable)
+- **Variable-Size Data**: Strings, arrays, nested structs
+
+### Array Layout
+
+```
++------------+------------------+--------------------+--------------------+
+| Num Elems  |   Null Bitmap    |  Fixed-Size Data   | Variable-Size Data |
++------------+------------------+--------------------+--------------------+
+|   8 bytes  |  ceil(n/8) bytes |   elem_size * n    |      variable      |
++------------+------------------+--------------------+--------------------+
+```
+
+### Map Layout
+
+```
++------------------+------------------+
+|    Keys Array    |   Values Array   |
++------------------+------------------+
+```
+
+## Performance Tips
+
+### 1. Reuse Encoders
+
+```cpp
+RowEncoder<Person> encoder;
+
+// Encode multiple records
+for (const auto& person : people) {
+  encoder.Encode(person);
+  auto row = encoder.GetWriter().ToRow();
+  // Process row...
+}
+```
+
+### 2. Pre-allocate Buffer
+
+```cpp
+// Get buffer reference for pre-allocation
+auto& buffer = encoder.GetWriter().buffer();
+buffer->Reserve(expected_size);
+```
+
+### 3. Batch Processing
+
+```cpp
+// Process in batches for better cache utilization
+std::vector<Person> batch;
+batch.reserve(BATCH_SIZE);
+
+while (hasMore()) {
+  batch.clear();
+  fillBatch(batch);
+
+  for (const auto& person : batch) {
+    encoder.Encode(person);
+    process(encoder.GetWriter().ToRow());
+  }
+}
+```
+
+### 4. Zero-Copy Reading
+
+```cpp
+// Point to existing buffer (zero-copy)
+Row row(schema);
+row.PointTo(buffer, offset, size);
+
+// Access fields directly from buffer
+int32_t id = row.GetInt32(0);
+```
+
+## Supported Types Summary
+
+| C++ Type                 | Row Type         | Fixed Size |
+| ------------------------ | ---------------- | ---------- |
+| `bool`                   | `boolean()`      | 1 byte     |
+| `int8_t`                 | `int8()`         | 1 byte     |
+| `int16_t`                | `int16()`        | 2 bytes    |
+| `int32_t`                | `int32()`        | 4 bytes    |
+| `int64_t`                | `int64()`        | 8 bytes    |
+| `float`                  | `float32()`      | 4 bytes    |
+| `double`                 | `float64()`      | 8 bytes    |
+| `std::string`            | `utf8()`         | Variable   |
+| `std::vector<T>`         | `list(T)`        | Variable   |
+| `std::map<K,V>`          | `map(K,V)`       | Variable   |
+| `std::optional<T>`       | Inner type       | Nullable   |
+| Struct (FORY_FIELD_INFO) | `struct_({...})` | Variable   |
+
+## Related Topics
+
+- [Basic Serialization](basic-serialization.md) - Object graph serialization
+- [Configuration](configuration.md) - Builder options
+- [Supported Types](supported-types.md) - All supported types
diff --git a/docs/guide/cpp/schema-evolution.md 
b/docs/guide/cpp/schema-evolution.md
new file mode 100644
index 000000000..21e6a5098
--- /dev/null
+++ b/docs/guide/cpp/schema-evolution.md
@@ -0,0 +1,404 @@
+---
+title: Schema Evolution
+sidebar_position: 3
+id: cpp_schema_evolution
+license: |
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+---
+
+Apache Fory™ supports schema evolution in **Compatible mode**, allowing 
serialization and deserialization peers to have different type definitions.
+
+## Compatible Mode
+
+Enable schema evolution with `compatible(true)`:
+
+```cpp
+#include "fory/serialization/fory.h"
+
+using namespace fory::serialization;
+
+// Version 1: Original schema
+struct PersonV1 {
+  std::string name;
+  int32_t age;
+};
+FORY_STRUCT(PersonV1, name, age);
+
+// Version 2: Added email field
+struct PersonV2 {
+  std::string name;
+  int32_t age;
+  std::string email;  // NEW FIELD
+};
+FORY_STRUCT(PersonV2, name, age, email);
+
+int main() {
+  // Create separate Fory instances for each schema version
+  auto fory_v1 = Fory::builder()
+      .compatible(true)  // Enable schema evolution
+      .xlang(true)
+      .build();
+
+  auto fory_v2 = Fory::builder()
+      .compatible(true)
+      .xlang(true)
+      .build();
+
+  // Register with the SAME type ID for schema evolution
+  constexpr uint32_t PERSON_TYPE_ID = 100;
+  fory_v1.register_struct<PersonV1>(PERSON_TYPE_ID);
+  fory_v2.register_struct<PersonV2>(PERSON_TYPE_ID);
+
+  // Serialize with V1
+  PersonV1 v1{"Alice", 30};
+  auto bytes = fory_v1.serialize(v1).value();
+
+  // Deserialize as V2 - email gets default value (empty string)
+  auto v2 = fory_v2.deserialize<PersonV2>(bytes).value();
+  assert(v2.name == "Alice");
+  assert(v2.age == 30);
+  assert(v2.email == "");  // Default value for missing field
+
+  return 0;
+}
+```
+
+## Schema Evolution Features
+
+Compatible mode supports the following schema changes:
+
+| Change Type        | Support | Behavior                                |
+| ------------------ | ------- | --------------------------------------- |
+| Add new fields     | ✅      | Missing fields use default values       |
+| Remove fields      | ✅      | Extra fields are skipped                |
+| Reorder fields     | ✅      | Fields matched by name, not position    |
+| Change nullability | ✅      | `T` ↔ `std::optional<T>`                |
+| Change field types | ❌      | Types must be compatible                |
+| Rename fields      | ❌      | Field names must match (case-sensitive) |
+
+## Adding Fields (Backward Compatibility)
+
+When deserializing old data with a new schema that has additional fields:
+
+```cpp
+// Old schema (V1)
+struct ProductV1 {
+  std::string name;
+  double price;
+};
+FORY_STRUCT(ProductV1, name, price);
+
+// New schema (V2) with additional fields
+struct ProductV2 {
+  std::string name;
+  double price;
+  std::vector<std::string> tags;       // NEW
+  std::map<std::string, std::string> attributes;  // NEW
+};
+FORY_STRUCT(ProductV2, name, price, tags, attributes);
+
+// Serialize V1
+ProductV1 v1{"Laptop", 999.99};
+auto bytes = fory_v1.serialize(v1).value();
+
+// Deserialize as V2
+auto v2 = fory_v2.deserialize<ProductV2>(bytes).value();
+assert(v2.name == "Laptop");
+assert(v2.price == 999.99);
+assert(v2.tags.empty());        // Default: empty vector
+assert(v2.attributes.empty());  // Default: empty map
+```
+
+## Removing Fields (Forward Compatibility)
+
+When deserializing new data with an old schema that has fewer fields:
+
+```cpp
+// Full schema
+struct UserFull {
+  int64_t id;
+  std::string username;
+  std::string email;
+  std::string password_hash;
+  int32_t login_count;
+};
+FORY_STRUCT(UserFull, id, username, email, password_hash, login_count);
+
+// Minimal schema (removed 3 fields)
+struct UserMinimal {
+  int64_t id;
+  std::string username;
+};
+FORY_STRUCT(UserMinimal, id, username);
+
+// Serialize full version
+UserFull full{12345, "johndoe", "[email protected]", "hash123", 42};
+auto bytes = fory_full.serialize(full).value();
+
+// Deserialize as minimal - extra fields are skipped
+auto minimal = fory_minimal.deserialize<UserMinimal>(bytes).value();
+assert(minimal.id == 12345);
+assert(minimal.username == "johndoe");
+// email, password_hash, login_count are skipped
+```
+
+## Field Reordering
+
+In compatible mode, fields are matched by name, not by position:
+
+```cpp
+// Original field order
+struct ConfigOriginal {
+  std::string host;
+  int32_t port;
+  bool enable_ssl;
+  std::string protocol;
+};
+FORY_STRUCT(ConfigOriginal, host, port, enable_ssl, protocol);
+
+// Reordered fields
+struct ConfigReordered {
+  bool enable_ssl;      // Moved to first
+  std::string protocol; // Moved to second
+  std::string host;     // Moved to third
+  int32_t port;         // Moved to last
+};
+FORY_STRUCT(ConfigReordered, enable_ssl, protocol, host, port);
+
+// Serialize with original order
+ConfigOriginal orig{"localhost", 8080, true, "https"};
+auto bytes = fory_orig.serialize(orig).value();
+
+// Deserialize with different field order - works correctly
+auto reordered = fory_reord.deserialize<ConfigReordered>(bytes).value();
+assert(reordered.host == "localhost");
+assert(reordered.port == 8080);
+assert(reordered.enable_ssl == true);
+assert(reordered.protocol == "https");
+```
+
+## Nested Struct Evolution
+
+Schema evolution works recursively for nested structs:
+
+```cpp
+// V1 Address
+struct AddressV1 {
+  std::string street;
+  std::string city;
+};
+FORY_STRUCT(AddressV1, street, city);
+
+// V2 Address with new fields
+struct AddressV2 {
+  std::string street;
+  std::string city;
+  std::string country;  // NEW
+  std::string zipcode;  // NEW
+};
+FORY_STRUCT(AddressV2, street, city, country, zipcode);
+
+// V1 Employee with V1 Address
+struct EmployeeV1 {
+  std::string name;
+  AddressV1 home_address;
+};
+FORY_STRUCT(EmployeeV1, name, home_address);
+
+// V2 Employee with V2 Address and new field
+struct EmployeeV2 {
+  std::string name;
+  AddressV2 home_address;  // Nested struct evolved
+  std::string employee_id; // NEW
+};
+FORY_STRUCT(EmployeeV2, name, home_address, employee_id);
+
+// Register types with same IDs
+constexpr uint32_t ADDRESS_TYPE_ID = 100;
+constexpr uint32_t EMPLOYEE_TYPE_ID = 101;
+
+fory_v1.register_struct<AddressV1>(ADDRESS_TYPE_ID);
+fory_v1.register_struct<EmployeeV1>(EMPLOYEE_TYPE_ID);
+fory_v2.register_struct<AddressV2>(ADDRESS_TYPE_ID);
+fory_v2.register_struct<EmployeeV2>(EMPLOYEE_TYPE_ID);
+
+// Serialize V1
+EmployeeV1 emp_v1{"Jane Doe", {"123 Main St", "NYC"}};
+auto bytes = fory_v1.serialize(emp_v1).value();
+
+// Deserialize as V2
+auto emp_v2 = fory_v2.deserialize<EmployeeV2>(bytes).value();
+assert(emp_v2.name == "Jane Doe");
+assert(emp_v2.home_address.street == "123 Main St");
+assert(emp_v2.home_address.city == "NYC");
+assert(emp_v2.home_address.country == "");  // Default
+assert(emp_v2.home_address.zipcode == "");  // Default
+assert(emp_v2.employee_id == "");           // Default
+```
+
+## Bidirectional Evolution
+
+Schema evolution works in both directions:
+
+```cpp
+// V2 -> V1 (downgrade)
+PersonV2 v2{"Charlie", 35, "[email protected]"};
+auto bytes = fory_v2.serialize(v2).value();
+
+auto v1 = fory_v1.deserialize<PersonV1>(bytes).value();
+assert(v1.name == "Charlie");
+assert(v1.age == 35);
+// email field is discarded during deserialization
+```
+
+## Default Values
+
+When fields are missing, C++ default initialization is used:
+
+| Type                   | Default Value       |
+| ---------------------- | ------------------- |
+| `int8_t`, `int16_t`... | `0`                 |
+| `float`, `double`      | `0.0`               |
+| `bool`                 | `false`             |
+| `std::string`          | `""`                |
+| `std::vector<T>`       | Empty vector        |
+| `std::map<K,V>`        | Empty map           |
+| `std::set<T>`          | Empty set           |
+| `std::optional<T>`     | `std::nullopt`      |
+| Struct types           | Default-constructed |
+
+## Schema Consistent Mode (Default)
+
+Without compatible mode, schemas must match exactly:
+
+```cpp
+// Strict mode (default)
+auto fory = Fory::builder()
+    .compatible(false)  // Default: schema must match
+    .xlang(true)
+    .build();
+
+// Serialization/deserialization requires identical schemas
+// Schema mismatches may cause errors or undefined behavior
+```
+
+**Use SchemaConsistent mode when:**
+
+- Schemas are guaranteed to match (same binary version)
+- Maximum performance is required (less metadata overhead)
+- You control both serialization and deserialization
+
+**Use Compatible mode when:**
+
+- Schemas may evolve independently
+- Cross-version compatibility is required
+- Different services may have different schema versions
+
+## Type ID Requirements
+
+For schema evolution to work:
+
+1. **Same Type ID**: Different versions of the same struct must use the same 
type ID
+2. **Consistent IDs**: Type IDs must be consistent across all Fory instances
+3. **Register All Versions**: Each Fory instance registers its own struct 
version
+
+```cpp
+constexpr uint32_t PERSON_TYPE_ID = 100;
+
+// Instance 1 uses PersonV1
+fory_v1.register_struct<PersonV1>(PERSON_TYPE_ID);
+
+// Instance 2 uses PersonV2
+fory_v2.register_struct<PersonV2>(PERSON_TYPE_ID);
+
+// Same type ID enables schema evolution
+```
+
+## Best Practices
+
+### 1. Plan for Evolution
+
+Design schemas with future changes in mind:
+
+```cpp
+// Good: Use optional for fields that might be removed
+struct Config {
+  std::string host;
+  int32_t port;
+  std::optional<std::string> deprecated_field;  // Can be removed later
+};
+```
+
+### 2. Use Meaningful Default Values
+
+Consider what default values make sense for new fields:
+
+```cpp
+struct Settings {
+  int32_t timeout_ms;      // Default: 0 (might want a sensible default)
+  bool enabled;            // Default: false
+  std::string mode;        // Default: "" (might want "default")
+};
+```
+
+### 3. Document Schema Versions
+
+Track schema changes for debugging:
+
+```cpp
+// V1: Initial schema (2024-01-01)
+// V2: Added email field (2024-02-01)
+// V3: Added phone, address fields (2024-03-01)
+```
+
+### 4. Test Evolution Paths
+
+Test both upgrade and downgrade scenarios:
+
+```cpp
+// Test V1 -> V2
+// Test V2 -> V1
+// Test V1 -> V3
+// Test V3 -> V1
+```
+
+## Cross-Language Schema Evolution
+
+Schema evolution works across languages when using xlang mode:
+
+```cpp
+// C++ with compatible mode
+auto fory = Fory::builder()
+    .compatible(true)
+    .xlang(true)
+    .build();
+```
+
+```java
+// Java with compatible mode
+Fory fory = Fory.builder()
+    .withCompatibleMode(CompatibleMode.COMPATIBLE)
+    .withLanguage(Language.XLANG)
+    .build();
+```
+
+Both instances can exchange data even with different schema versions.
+
+## Related Topics
+
+- [Configuration](configuration.md) - Enabling compatible mode
+- [Type Registration](type-registration.md) - Type ID management
+- [Cross-Language](cross-language.md) - Cross-language considerations
diff --git a/docs/guide/cpp/supported-types.md 
b/docs/guide/cpp/supported-types.md
new file mode 100644
index 000000000..fead3785b
--- /dev/null
+++ b/docs/guide/cpp/supported-types.md
@@ -0,0 +1,277 @@
+---
+title: Supported Types
+sidebar_position: 5
+id: cpp_supported_types
+license: |
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+---
+
+This page documents all types supported by Fory C++ serialization.
+
+## Primitive Types
+
+All C++ primitive types are supported with efficient binary encoding:
+
+| Type       | Size   | Fory TypeId | Notes                 |
+| ---------- | ------ | ----------- | --------------------- |
+| `bool`     | 1 byte | BOOL        | True/false            |
+| `int8_t`   | 1 byte | INT8        | Signed byte           |
+| `uint8_t`  | 1 byte | INT8        | Unsigned byte         |
+| `int16_t`  | 2 byte | INT16       | Signed short          |
+| `uint16_t` | 2 byte | INT16       | Unsigned short        |
+| `int32_t`  | 4 byte | INT32       | Signed integer        |
+| `uint32_t` | 4 byte | INT32       | Unsigned integer      |
+| `int64_t`  | 8 byte | INT64       | Signed long           |
+| `uint64_t` | 8 byte | INT64       | Unsigned long         |
+| `float`    | 4 byte | FLOAT32     | IEEE 754 single       |
+| `double`   | 8 byte | FLOAT64     | IEEE 754 double       |
+| `char`     | 1 byte | INT8        | Character (as signed) |
+| `char16_t` | 2 byte | INT16       | 16-bits characters    |
+| `char32_t` | 4 byte | INT32       | 32-bits characters    |
+
+```cpp
+int32_t value = 42;
+auto bytes = fory.serialize(value).value();
+auto decoded = fory.deserialize<int32_t>(bytes).value();
+assert(value == decoded);
+```
+
+## String Types
+
+| Type               | Fory TypeId | Notes                    |
+| ------------------ | ----------- | ------------------------ |
+| `std::string`      | STRING      | UTF-8 encoded            |
+| `std::string_view` | STRING      | Zero-copy view (read)    |
+| `std::u16string`   | STRING      | UTF-16 (converted)       |
+| `binary`           | BINARY      | Raw bytes without length |
+
+```cpp
+std::string text = "Hello, World!";
+auto bytes = fory.serialize(text).value();
+auto decoded = fory.deserialize<std::string>(bytes).value();
+assert(text == decoded);
+```
+
+## Collection Types
+
+### Vector / List
+
+`std::vector<T>` for any serializable element type:
+
+```cpp
+std::vector<int32_t> numbers{1, 2, 3, 4, 5};
+auto bytes = fory.serialize(numbers).value();
+auto decoded = fory.deserialize<std::vector<int32_t>>(bytes).value();
+
+// Nested vectors
+std::vector<std::vector<std::string>> nested{
+    {"a", "b"},
+    {"c", "d", "e"}
+};
+```
+
+### Set
+
+`std::set<T>` and `std::unordered_set<T>`:
+
+```cpp
+std::set<std::string> tags{"cpp", "serialization", "fory"};
+auto bytes = fory.serialize(tags).value();
+auto decoded = fory.deserialize<std::set<std::string>>(bytes).value();
+
+std::unordered_set<int32_t> ids{1, 2, 3};
+```
+
+### Map
+
+`std::map<K, V>` and `std::unordered_map<K, V>`:
+
+```cpp
+std::map<std::string, int32_t> scores{
+    {"Alice", 100},
+    {"Bob", 95}
+};
+auto bytes = fory.serialize(scores).value();
+auto decoded = fory.deserialize<std::map<std::string, int32_t>>(bytes).value();
+
+// Unordered map
+std::unordered_map<int32_t, std::string> lookup{
+    {1, "one"},
+    {2, "two"}
+};
+```
+
+## Smart Pointers
+
+### std::optional
+
+Nullable wrapper for any type:
+
+```cpp
+std::optional<int32_t> maybe_value = 42;
+std::optional<int32_t> empty_value = std::nullopt;
+
+auto bytes = fory.serialize(maybe_value).value();
+auto decoded = fory.deserialize<std::optional<int32_t>>(bytes).value();
+assert(decoded.has_value() && *decoded == 42);
+```
+
+### std::shared_ptr
+
+Shared ownership with reference tracking:
+
+```cpp
+auto shared = std::make_shared<Person>("Alice", 30);
+
+auto bytes = fory.serialize(shared).value();
+auto decoded = fory.deserialize<std::shared_ptr<Person>>(bytes).value();
+```
+
+**With reference tracking enabled (`track_ref(true)`):**
+
+- Shared objects are serialized once
+- References to the same object are preserved
+- Circular references are handled automatically
+
+### std::unique_ptr
+
+Exclusive ownership:
+
+```cpp
+auto unique = std::make_unique<Person>("Bob", 25);
+
+auto bytes = fory.serialize(unique).value();
+auto decoded = fory.deserialize<std::unique_ptr<Person>>(bytes).value();
+```
+
+## Variant Type
+
+`std::variant<Ts...>` for type-safe unions:
+
+```cpp
+using MyVariant = std::variant<int32_t, std::string, double>;
+
+MyVariant v1 = 42;
+MyVariant v2 = std::string("hello");
+MyVariant v3 = 3.14;
+
+auto bytes = fory.serialize(v1).value();
+auto decoded = fory.deserialize<MyVariant>(bytes).value();
+assert(std::get<int32_t>(decoded) == 42);
+```
+
+### std::monostate
+
+Empty variant alternative:
+
+```cpp
+using OptionalInt = std::variant<std::monostate, int32_t>;
+
+OptionalInt empty = std::monostate{};
+OptionalInt value = 42;
+```
+
+## Temporal Types
+
+### Duration
+
+`std::chrono::nanoseconds`:
+
+```cpp
+using Duration = std::chrono::nanoseconds;
+
+Duration d = std::chrono::seconds(30);
+auto bytes = fory.serialize(d).value();
+auto decoded = fory.deserialize<Duration>(bytes).value();
+```
+
+### Timestamp
+
+Point in time since Unix epoch:
+
+```cpp
+using Timestamp = std::chrono::time_point<std::chrono::system_clock,
+                                          std::chrono::nanoseconds>;
+
+Timestamp now = std::chrono::system_clock::now();
+auto bytes = fory.serialize(now).value();
+auto decoded = fory.deserialize<Timestamp>(bytes).value();
+```
+
+### LocalDate
+
+Days since Unix epoch:
+
+```cpp
+LocalDate date{18628};  // Days since 1970-01-01
+
+auto bytes = fory.serialize(date).value();
+auto decoded = fory.deserialize<LocalDate>(bytes).value();
+```
+
+## User-Defined Structs
+
+Any struct can be made serializable with `FORY_STRUCT`:
+
+```cpp
+struct Point {
+  double x;
+  double y;
+  double z;
+};
+FORY_STRUCT(Point, x, y, z);
+
+struct Line {
+  Point start;
+  Point end;
+  std::string label;
+};
+FORY_STRUCT(Line, start, end, label);
+```
+
+## Enum Types
+
+Both scoped and unscoped enums are supported with `FORY_ENUM`:
+
+```cpp
+// Scoped enum (C++11 enum class)
+enum class Color { RED = 0, GREEN = 1, BLUE = 2 };
+
+// Unscoped enum with incontinuous values
+enum Priority : int32_t { LOW = -10, NORMAL = 0, HIGH = 10 };
+FORY_ENUM(Priority, LOW, NORMAL, HIGH);
+
+// Usage
+Color c = Color::GREEN;
+auto bytes = fory.serialize(c).value();
+auto decoded = fory.deserialize<Color>(bytes).value();
+```
+
+## Unsupported Types
+
+Currently not supported:
+
+- Raw pointers (`T*`) - use smart pointers instead
+- `std::tuple<Ts...>` - use structs instead
+- `std::array<T, N>` - use `std::vector<T>` instead
+- Function pointers
+- References (`T&`, `const T&`) - by value only
+
+## Related Topics
+
+- [Basic Serialization](basic-serialization.md) - Using these types
+- [Type Registration](type-registration.md) - Registering types
+- [Cross-Language](cross-language.md) - Cross-language compatibility
diff --git a/docs/guide/cpp/type-registration.md 
b/docs/guide/cpp/type-registration.md
new file mode 100644
index 000000000..7b930c060
--- /dev/null
+++ b/docs/guide/cpp/type-registration.md
@@ -0,0 +1,225 @@
+---
+title: Type Registration
+sidebar_position: 4
+id: cpp_type_registration
+license: |
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+---
+
+This page explains how to register types for serialization.
+
+## Overview
+
+Apache Fory™ requires explicit type registration for struct types. This design 
enables:
+
+- **Cross-Language Compatibility**: Registered type IDs are used across 
language boundaries
+- **Type Safety**: Detects type mismatches at deserialization time
+- **Polymorphic Serialization**: Enables serialization of polymorphic objects 
via smart pointers
+
+## Registering Structs
+
+Use `register_struct<T>(type_id)` to register a struct type:
+
+```cpp
+#include "fory/serialization/fory.h"
+
+using namespace fory::serialization;
+
+struct Person {
+  std::string name;
+  int32_t age;
+};
+FORY_STRUCT(Person, name, age);
+
+int main() {
+  auto fory = Fory::builder().xlang(true).build();
+
+  // Register with a unique type ID
+  fory.register_struct<Person>(100);
+
+  Person person{"Alice", 30};
+  auto bytes = fory.serialize(person).value();
+  auto decoded = fory.deserialize<Person>(bytes).value();
+}
+```
+
+## Type ID Guidelines
+
+Type IDs must be:
+
+1. **Unique**: Each type must have a unique ID within a Fory instance
+2. **Consistent**: Same ID must be used across all languages and versions
+
+User-registered type IDs are in a separate namespace from built-in type IDs, 
so you can start from 0:
+
+```cpp
+// User type IDs can start from 0
+fory.register_struct<Address>(0);
+fory.register_struct<Person>(1);
+fory.register_struct<Order>(2);
+```
+
+## Registering Enums
+
+For simple enums with continuous values starting from 0, no macro is needed:
+
+```cpp
+// Simple continuous enum - no FORY_ENUM needed
+enum class Color { RED, GREEN, BLUE };  // Values: 0, 1, 2
+
+// Just register directly
+fory.register_struct<Color>(0);
+```
+
+For enums with non-continuous values, use the `FORY_ENUM` macro to map values 
to ordinals:
+
+```cpp
+// Non-continuous enum values - FORY_ENUM required
+enum class Priority { LOW = 10, MEDIUM = 50, HIGH = 100 };
+FORY_ENUM(Priority, LOW, MEDIUM, HIGH);
+
+// Global namespace enum (prefix with ::)
+enum LegacyStatus { UNKNOWN = -1, OK = 0, ERROR = 1 };
+FORY_ENUM(::LegacyStatus, UNKNOWN, OK, ERROR);
+
+// Register after FORY_ENUM
+fory.register_struct<Priority>(1);
+fory.register_struct<LegacyStatus>(2);
+```
+
+**When to use `FORY_ENUM`:**
+
+- Enum values don't start from 0
+- Enum values are not continuous (e.g., 10, 50, 100)
+- You need name-to-value mapping at compile time
+
+## Thread-Safe Registration
+
+For `ThreadSafeFory`, register types before spawning threads:
+
+```cpp
+auto fory = Fory::builder().xlang(true).build_thread_safe();
+
+// Register all types first
+fory.register_struct<TypeA>(100);
+fory.register_struct<TypeB>(101);
+
+// Now safe to use from multiple threads
+std::thread t1([&]() {
+  auto result = fory.serialize(obj_a);
+});
+std::thread t2([&]() {
+  auto result = fory.serialize(obj_b);
+});
+```
+
+## Cross-Language Registration
+
+For cross-language compatibility, ensure:
+
+1. **Same Type ID**: Use identical IDs in all languages
+2. **Compatible Types**: Use equivalent types across languages
+
+### Java
+
+```java
+Fory fory = Fory.builder().build();
+fory.register(Person.class, 100);
+fory.register(Address.class, 101);
+```
+
+### Python
+
+```python
+import pyfory
+
+fory = pyfory.Fory()
+fory.register(Person, 100)
+fory.register(Address, 101)
+```
+
+### C++
+
+```cpp
+auto fory = Fory::builder().xlang(true).build();
+fory.register_struct<Person>(100);
+fory.register_struct<Address>(101);
+```
+
+## Built-in Type IDs
+
+Built-in types have pre-assigned type IDs and don't need registration:
+
+| Type ID | Type                 |
+| ------- | -------------------- |
+| 0       | NONE                 |
+| 1       | BOOL                 |
+| 2       | INT8                 |
+| 3       | INT16                |
+| 4       | INT32                |
+| 5       | VAR_INT32            |
+| 6       | INT64                |
+| 7       | VAR_INT64            |
+| 8       | SLI_INT64            |
+| 9       | FLOAT16              |
+| 10      | FLOAT32              |
+| 11      | FLOAT64              |
+| 12      | STRING               |
+| 13      | LIST                 |
+| 14      | MAP                  |
+| 15      | SET                  |
+| 16      | TIMESTAMP            |
+| 17      | DURATION             |
+| 18      | LOCAL_DATE           |
+| 19      | DECIMAL              |
+| 20      | BINARY               |
+| 21      | ARRAY                |
+| 22      | BOOL_ARRAY           |
+| 23-28   | INT_ARRAY variants   |
+| 29-31   | FLOAT_ARRAY variants |
+| 32      | STRUCT               |
+| 33      | COMPATIBLE_STRUCT    |
+| 34      | NAMED_STRUCT         |
+| 35      | NAMED*COMPATIBLE*... |
+| 36      | EXT                  |
+| 37      | NAMED_EXT            |
+| 63      | UNKNOWN              |
+
+## Error Handling
+
+Registration errors are checked at serialization/deserialization time:
+
+```cpp
+// Attempting to serialize unregistered type
+auto result = fory.serialize(unregistered_obj);
+if (!result.ok()) {
+  // Error: "Type not registered: ..."
+  std::cerr << result.error().to_string() << std::endl;
+}
+
+// Type ID mismatch during deserialization
+auto result = fory.deserialize<WrongType>(bytes);
+if (!result.ok()) {
+  // Error: "Type mismatch: expected X, got Y"
+  std::cerr << result.error().to_string() << std::endl;
+}
+```
+
+## Related Topics
+
+- [Basic Serialization](basic-serialization.md) - Using registered types
+- [Cross-Language](cross-language.md) - Cross-language considerations
+- [Supported Types](supported-types.md) - All supported types


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to