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 9b2a2cea0 feat(c++): make shared_ptr track ref by default (#3214)
9b2a2cea0 is described below
commit 9b2a2cea0419923e45d0730f974d0add06e5a36d
Author: Shawn Yang <[email protected]>
AuthorDate: Tue Jan 27 11:02:12 2026 +0800
feat(c++): make shared_ptr track ref by default (#3214)
## Why?
Make C++ shared ownership fields track references by default and extend
ref tracking semantics to SharedWeak to match intended ownership
behavior and reduce required annotations.
## What does this PR do?
- Make `std::shared_ptr<T>` (and `fory::serialization::SharedWeak<T>`)
track refs by default in field metadata and serializers.
- Extend smart-pointer detection and option validation to include
`SharedWeak<T>` alongside `shared_ptr`/`unique_ptr`.
- Update compile-time helpers, resolver logic, and tests to reflect the
new default tracking behavior.
- Refresh C++ and xlang docs to document defaults and valid options for
`SharedWeak`.
## Related issues
#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
---
AGENTS.md | 2 +-
cpp/fory/meta/field.h | 73 +++++++++++++++++--------
cpp/fory/meta/field_test.cc | 18 +++---
cpp/fory/serialization/field_serializer_test.cc | 2 +-
cpp/fory/serialization/struct_serializer.h | 6 +-
cpp/fory/serialization/type_resolver.h | 4 +-
docs/guide/cpp/field-configuration.md | 56 ++++++++++---------
docs/guide/cpp/supported-types.md | 2 +-
docs/guide/xlang/field-reference-tracking.md | 29 +++++-----
9 files changed, 111 insertions(+), 81 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index 8d73f2c7f..5be9cad68 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -14,7 +14,7 @@ While working on Fory, please remember:
- **Cross-Language Consistency**: Maintain consistency across language
implementations while respecting language-specific idioms.
- **GraalVM support using fory codegen**: For GraalVM, use `fory codegen` to
generate the serializer when building a native image. Do not use GraalVM
reflect-related configuration unless for JDK `proxy`.
- **Xlang Type System**: Java `native mode(xlang=false)` shares same type
systems between type id from `Types.BOOL~Types.STRING` with `xlang
mode(xlang=true)`, but for other types, java `native mode` has different type
ids.
-- **Remote git repository**: `[email protected]:apache/fory.git` is remote
repository, do not use other remote repository when you want to check code
under `main` branch.
+- **Remote git repository**: `[email protected]:apache/fory.git` is remote
repository, do not use other remote repository when you want to check code
under `main` branch, **`apache/main`** is the only target main branch instead
of `origin/main`
- **Contributor git repository**: A contributor should fork the
`[email protected]:apache/fory.git` repo, and git push the code changes into
their forked repo, then create a pull request from the branch in their forked
repo into `[email protected]:apache/fory.git`.
- **Debug Test Errors**: always set environment variable
`ENABLE_FORY_DEBUG_OUTPUT` to `1` to see debug output.
diff --git a/cpp/fory/meta/field.h b/cpp/fory/meta/field.h
index 616facbff..43b91dd99 100644
--- a/cpp/fory/meta/field.h
+++ b/cpp/fory/meta/field.h
@@ -33,23 +33,28 @@
namespace fory {
+namespace serialization {
+template <typename T> class SharedWeak;
+} // namespace serialization
+
// ============================================================================
// Field Option Tags
// ============================================================================
-/// Tag to mark a shared_ptr/unique_ptr field as nullable.
-/// Only valid for std::shared_ptr and std::unique_ptr types.
+/// Tag to mark a shared_ptr/SharedWeak/unique_ptr field as nullable.
+/// Only valid for std::shared_ptr, SharedWeak, and std::unique_ptr types.
/// For nullable primitives/strings, use std::optional<T> instead.
struct nullable {};
/// Tag to explicitly mark a pointer field as non-nullable.
/// Useful for future pointer types (e.g., weak_ptr) that might be nullable by
-/// default. For shared_ptr/unique_ptr, non-nullable is already the default.
+/// default. For shared_ptr/SharedWeak/unique_ptr, non-nullable is already the
+/// default.
struct not_null {};
-/// Tag to enable reference tracking for shared_ptr fields.
-/// Only valid for std::shared_ptr types (requires shared ownership for ref
-/// tracking).
+/// Tag to enable reference tracking for shared_ptr/SharedWeak fields.
+/// Only valid for std::shared_ptr or SharedWeak types (requires shared
+/// ownership for ref tracking).
struct ref {};
/// Template tag to control dynamic type dispatch for smart pointer fields.
@@ -108,6 +113,14 @@ struct is_shared_ptr<std::shared_ptr<T>> : std::true_type
{};
template <typename T>
inline constexpr bool is_shared_ptr_v = is_shared_ptr<T>::value;
+template <typename T> struct is_shared_weak : std::false_type {};
+
+template <typename T>
+struct is_shared_weak<serialization::SharedWeak<T>> : std::true_type {};
+
+template <typename T>
+inline constexpr bool is_shared_weak_v = is_shared_weak<T>::value;
+
template <typename T> struct is_unique_ptr : std::false_type {};
template <typename T, typename D>
@@ -123,9 +136,10 @@ template <typename T> struct is_optional<std::optional<T>>
: std::true_type {};
template <typename T>
inline constexpr bool is_optional_v = is_optional<T>::value;
-/// Helper to check if type is shared_ptr or unique_ptr
+/// Helper to check if type is shared_ptr/SharedWeak or unique_ptr
template <typename T>
-inline constexpr bool is_smart_ptr_v = is_shared_ptr_v<T> ||
is_unique_ptr_v<T>;
+inline constexpr bool is_smart_ptr_v =
+ is_shared_ptr_v<T> || is_shared_weak_v<T> || is_unique_ptr_v<T>;
// ============================================================================
// Option Tag Detection
@@ -495,6 +509,7 @@ public:
/// - Primitives/strings: No options allowed (use std::optional for nullable)
/// - std::optional<T>: Inherently nullable, no options needed
/// - std::shared_ptr<T>: Can use nullable and/or ref
+/// - SharedWeak<T>: Can use nullable and/or ref
/// - std::unique_ptr<T>: Can use nullable only (no ref - exclusive
/// ownership)
template <typename T, int16_t Id, typename... Options> class field {
@@ -506,7 +521,8 @@ template <typename T, int16_t Id, typename... Options>
class field {
// Validate: nullable only for smart pointers
static_assert(!detail::has_option_v<nullable, Options...> ||
detail::is_smart_ptr_v<T>,
- "fory::nullable is only valid for shared_ptr/unique_ptr. "
+ "fory::nullable is only valid for shared_ptr/SharedWeak/"
+ "unique_ptr. "
"Use std::optional<T> for nullable primitives/strings.");
// Validate: not_null only for smart pointers (for now)
@@ -514,16 +530,17 @@ template <typename T, int16_t Id, typename... Options>
class field {
detail::is_smart_ptr_v<T>,
"fory::not_null is only valid for pointer types.");
- // Validate: ref only for shared_ptr
+ // Validate: ref only for shared_ptr/SharedWeak
static_assert(!detail::has_option_v<ref, Options...> ||
- detail::is_shared_ptr_v<T>,
- "fory::ref is only valid for shared_ptr "
+ detail::is_shared_ptr_v<T> || detail::is_shared_weak_v<T>,
+ "fory::ref is only valid for shared_ptr/SharedWeak "
"(reference tracking requires shared ownership).");
// Validate: dynamic<V> only for smart pointers
static_assert(!detail::has_dynamic_option_v<Options...> ||
detail::is_smart_ptr_v<T>,
- "fory::dynamic<V> is only valid for shared_ptr/unique_ptr.");
+ "fory::dynamic<V> is only valid for shared_ptr/SharedWeak/"
+ "unique_ptr.");
// Validate: no options for optional (inherently nullable)
static_assert(!detail::is_optional_v<T> || sizeof...(Options) == 0,
@@ -532,7 +549,8 @@ template <typename T, int16_t Id, typename... Options>
class field {
// Validate: no options for non-smart-pointer types
static_assert(detail::is_smart_ptr_v<T> || detail::is_optional_v<T> ||
sizeof...(Options) == 0,
- "Options are only valid for shared_ptr/unique_ptr fields. "
+ "Options are only valid for shared_ptr/SharedWeak/unique_ptr "
+ "fields. "
"Use std::optional<T> for nullable primitives/strings.");
public:
@@ -547,9 +565,11 @@ public:
(detail::is_smart_ptr_v<T> && detail::has_option_v<nullable,
Options...>);
/// Reference tracking is enabled if:
- /// - It's std::shared_ptr with fory::ref option
- static constexpr bool track_ref =
- detail::is_shared_ptr_v<T> && detail::has_option_v<ref, Options...>;
+ /// - It's std::shared_ptr or SharedWeak (default)
+ /// - Or explicitly marked with fory::ref
+ static constexpr bool track_ref = detail::is_shared_ptr_v<T> ||
+ detail::is_shared_weak_v<T> ||
+ detail::has_option_v<ref, Options...>;
/// Dynamic type dispatch control:
/// - -1 (AUTO): Use std::is_polymorphic<T> to decide
@@ -666,7 +686,8 @@ inline constexpr bool field_is_nullable_v =
field_is_nullable<T>::value;
/// Get track_ref from field type
template <typename T> struct field_track_ref {
- static constexpr bool value = false;
+ static constexpr bool value =
+ detail::is_shared_ptr_v<T> || detail::is_shared_weak_v<T>;
};
template <typename T, int16_t Id, typename... Options>
@@ -705,21 +726,25 @@ struct ParseFieldTagEntry {
is_optional_v<FieldType> ||
(is_smart_ptr_v<FieldType> && has_option_v<nullable, Options...>);
- static constexpr bool track_ref =
- is_shared_ptr_v<FieldType> && has_option_v<ref, Options...>;
+ static constexpr bool track_ref = is_shared_ptr_v<FieldType> ||
+ is_shared_weak_v<FieldType> ||
+ has_option_v<ref, Options...>;
static constexpr int dynamic_value = get_dynamic_value_v<Options...>;
// Compile-time validation
static_assert(!has_option_v<nullable, Options...> ||
is_smart_ptr_v<FieldType>,
- "fory::nullable is only valid for shared_ptr/unique_ptr");
+ "fory::nullable is only valid for shared_ptr/SharedWeak/"
+ "unique_ptr");
- static_assert(!has_option_v<ref, Options...> || is_shared_ptr_v<FieldType>,
- "fory::ref is only valid for shared_ptr");
+ static_assert(!has_option_v<ref, Options...> || is_shared_ptr_v<FieldType> ||
+ is_shared_weak_v<FieldType>,
+ "fory::ref is only valid for shared_ptr/SharedWeak");
static_assert(!has_dynamic_option_v<Options...> || is_smart_ptr_v<FieldType>,
- "fory::dynamic<V> is only valid for shared_ptr/unique_ptr");
+ "fory::dynamic<V> is only valid for shared_ptr/SharedWeak/"
+ "unique_ptr");
using type = FieldTagEntry<Id, is_nullable, track_ref, dynamic_value>;
};
diff --git a/cpp/fory/meta/field_test.cc b/cpp/fory/meta/field_test.cc
index 528800f1b..c1bf37731 100644
--- a/cpp/fory/meta/field_test.cc
+++ b/cpp/fory/meta/field_test.cc
@@ -113,7 +113,7 @@ TEST(Field, SharedPtrNonNullable) {
using FieldType = field<std::shared_ptr<int32_t>, 3>;
static_assert(FieldType::tag_id == 3);
static_assert(FieldType::is_nullable == false);
- static_assert(FieldType::track_ref == false);
+ static_assert(FieldType::track_ref == true);
FieldType f;
f = std::make_shared<int32_t>(99);
@@ -126,7 +126,7 @@ TEST(Field, SharedPtrNullable) {
using FieldType = field<std::shared_ptr<int32_t>, 4, nullable>;
static_assert(FieldType::tag_id == 4);
static_assert(FieldType::is_nullable == true);
- static_assert(FieldType::track_ref == false);
+ static_assert(FieldType::track_ref == true);
FieldType f;
EXPECT_EQ(f.value, nullptr); // Default is null
@@ -169,7 +169,7 @@ TEST(Field, SharedPtrNotNull) {
using FieldType = field<std::shared_ptr<int32_t>, 9, not_null>;
static_assert(FieldType::tag_id == 9);
static_assert(FieldType::is_nullable == false);
- static_assert(FieldType::track_ref == false);
+ static_assert(FieldType::track_ref == true);
}
TEST(Field, SharedPtrNotNullWithRef) {
@@ -222,9 +222,9 @@ TEST(FieldTraits, FieldIsNullable) {
TEST(FieldTraits, FieldTrackRef) {
static_assert(field_track_ref_v<int> == false);
- static_assert(field_track_ref_v<std::shared_ptr<int>> == false);
+ static_assert(field_track_ref_v<std::shared_ptr<int>> == true);
static_assert(field_track_ref_v<field<int, 0>> == false);
- static_assert(field_track_ref_v<field<std::shared_ptr<int>, 1>> == false);
+ static_assert(field_track_ref_v<field<std::shared_ptr<int>, 1>> == true);
static_assert(field_track_ref_v<field<std::shared_ptr<int>, 2, ref>> ==
true);
static_assert(
field_track_ref_v<field<std::shared_ptr<int>, 3, nullable, ref>> ==
true);
@@ -315,7 +315,7 @@ FORY_FIELD_TAGS(Document, (title, 0), // string:
non-nullable
(description, 2), // optional: inherently nullable
(author, 3), // shared_ptr: non-nullable (default)
(reviewer, 4, nullable), // shared_ptr: nullable
- (parent, 5, ref), // shared_ptr: non-nullable, with ref
+ (parent, 5, ref), // shared_ptr: non-nullable, ref
(metadata, 6, nullable)); // unique_ptr: nullable
FORY_FIELD_TAGS(Node, (name, 0), (left, 1, nullable, ref),
@@ -371,12 +371,12 @@ TEST(FieldTags, Nullability) {
}
TEST(FieldTags, RefTracking) {
- // Only parent has ref tracking
+ // shared_ptr fields track refs by default
static_assert(detail::GetFieldTagEntry<Document, 0>::track_ref == false);
static_assert(detail::GetFieldTagEntry<Document, 1>::track_ref == false);
static_assert(detail::GetFieldTagEntry<Document, 2>::track_ref == false);
- static_assert(detail::GetFieldTagEntry<Document, 3>::track_ref == false);
- static_assert(detail::GetFieldTagEntry<Document, 4>::track_ref == false);
+ static_assert(detail::GetFieldTagEntry<Document, 3>::track_ref == true);
+ static_assert(detail::GetFieldTagEntry<Document, 4>::track_ref == true);
static_assert(detail::GetFieldTagEntry<Document, 5>::track_ref == true);
static_assert(detail::GetFieldTagEntry<Document, 6>::track_ref == false);
}
diff --git a/cpp/fory/serialization/field_serializer_test.cc
b/cpp/fory/serialization/field_serializer_test.cc
index 142381e2f..72ca3ff68 100644
--- a/cpp/fory/serialization/field_serializer_test.cc
+++ b/cpp/fory/serialization/field_serializer_test.cc
@@ -656,7 +656,7 @@ TEST(FieldSerializerTest, FieldMetadataCompileTime) {
// Ref tracking
static_assert(decltype(FieldRefTrackingHolder::first)::track_ref);
- static_assert(!decltype(FieldSharedPtrHolder::value)::track_ref);
+ static_assert(decltype(FieldSharedPtrHolder::value)::track_ref);
// not_null doesn't change is_nullable for already non-nullable
static_assert(!decltype(FieldNotNullRefHolder::node)::is_nullable);
diff --git a/cpp/fory/serialization/struct_serializer.h
b/cpp/fory/serialization/struct_serializer.h
index 31fe1c0d3..5b92f8cea 100644
--- a/cpp/fory/serialization/struct_serializer.h
+++ b/cpp/fory/serialization/struct_serializer.h
@@ -604,7 +604,7 @@ template <typename T> struct CompileTimeFieldHelpers {
}
/// Returns true if reference tracking is enabled for the field at Index.
- /// Only valid for std::shared_ptr fields with fory::ref tag.
+ /// Defaults to true for std::shared_ptr/SharedWeak fields.
template <size_t Index> static constexpr bool field_track_ref() {
if constexpr (FieldCount == 0) {
return false;
@@ -620,9 +620,9 @@ template <typename T> struct CompileTimeFieldHelpers {
else if constexpr (::fory::detail::has_field_tags_v<T>) {
return ::fory::detail::GetFieldTagEntry<T, Index>::track_ref;
}
- // Default: no reference tracking
+ // Default: shared_ptr/SharedWeak track refs
else {
- return false;
+ return field_track_ref_v<RawFieldType>;
}
}
}
diff --git a/cpp/fory/serialization/type_resolver.h
b/cpp/fory/serialization/type_resolver.h
index 670665607..40f26d2ae 100644
--- a/cpp/fory/serialization/type_resolver.h
+++ b/cpp/fory/serialization/type_resolver.h
@@ -511,7 +511,9 @@ constexpr bool compute_track_ref() {
} else if constexpr (::fory::detail::has_field_tags_v<T>) {
return ::fory::detail::GetFieldTagEntry<T, Index>::track_ref;
} else {
- return false;
+ using UnwrappedFieldType = fory::unwrap_field_t<ActualFieldType>;
+ return ::fory::detail::is_shared_ptr_v<UnwrappedFieldType> ||
+ ::fory::detail::is_shared_weak_v<UnwrappedFieldType>;
}
}
diff --git a/docs/guide/cpp/field-configuration.md
b/docs/guide/cpp/field-configuration.md
index 7469f7bf1..0f78e1ad3 100644
--- a/docs/guide/cpp/field-configuration.md
+++ b/docs/guide/cpp/field-configuration.md
@@ -103,7 +103,7 @@ struct Node {
FORY_STRUCT(Node, name, next);
```
-**Valid for:** `std::shared_ptr<T>`, `std::unique_ptr<T>`
+**Valid for:** `std::shared_ptr<T>`, `fory::serialization::SharedWeak<T>`,
`std::unique_ptr<T>`
**Note:** For nullable primitives or strings, use `std::optional<T>` instead:
@@ -123,7 +123,7 @@ Explicitly marks a pointer field as non-nullable. This is
the default for smart
fory::field<std::shared_ptr<Data>, 0, fory::not_null> data; // Must not be
nullptr
```
-**Valid for:** `std::shared_ptr<T>`, `std::unique_ptr<T>`
+**Valid for:** `std::shared_ptr<T>`, `fory::serialization::SharedWeak<T>`,
`std::unique_ptr<T>`
### fory::ref
@@ -138,7 +138,7 @@ struct Graph {
FORY_STRUCT(Graph, name, left, right);
```
-**Valid for:** `std::shared_ptr<T>` only (requires shared ownership)
+**Valid for:** `std::shared_ptr<T>`, `fory::serialization::SharedWeak<T>`
(requires shared ownership)
### fory::dynamic\<V\>
@@ -171,7 +171,7 @@ FORY_STRUCT(Zoo, animal, fixed_animal);
### Combining Tags
-Multiple tags can be combined for shared pointers:
+Multiple tags can be combined for shared pointers and SharedWeak:
```cpp
// Nullable + ref tracking
@@ -180,12 +180,13 @@ fory::field<std::shared_ptr<Node>, 0, fory::nullable,
fory::ref> link;
## Type Rules
-| Type | Allowed Options | Nullability
|
-| -------------------- | ------------------------------- |
---------------------------------- |
-| Primitives, strings | None | Use
`std::optional<T>` if nullable |
-| `std::optional<T>` | None | Inherently nullable
|
-| `std::shared_ptr<T>` | `nullable`, `ref`, `dynamic<V>` | Non-null by default
|
-| `std::unique_ptr<T>` | `nullable`, `dynamic<V>` | Non-null by default
|
+| Type | Allowed Options |
Nullability |
+| ------------------------------------ | ------------------------------- |
---------------------------------- |
+| Primitives, strings | None | Use
`std::optional<T>` if nullable |
+| `std::optional<T>` | None |
Inherently nullable |
+| `std::shared_ptr<T>` | `nullable`, `ref`, `dynamic<V>` |
Non-null by default |
+| `fory::serialization::SharedWeak<T>` | `nullable`, `ref`, `dynamic<V>` |
Non-null by default |
+| `std::unique_ptr<T>` | `nullable`, `dynamic<V>` |
Non-null by default |
## Complete Example
@@ -510,21 +511,21 @@ FORY_FIELD_CONFIG(DataV2,
### FORY_FIELD_CONFIG Options Reference
-| Method | Description | Valid
For |
-| ----------------- | ------------------------------------------------ |
-------------------------- |
-| `.nullable()` | Mark field as nullable | Smart
pointers, primitives |
-| `.ref()` | Enable reference tracking |
`std::shared_ptr` only |
-| `.dynamic(true)` | Force type info to be written (dynamic dispatch) | Smart
pointers |
-| `.dynamic(false)` | Skip type info (use declared type directly) | Smart
pointers |
-| `.varint()` | Use variable-length encoding |
`uint32_t`, `uint64_t` |
-| `.fixed()` | Use fixed-size encoding |
`uint32_t`, `uint64_t` |
-| `.tagged()` | Use tagged hybrid encoding |
`uint64_t` only |
-| `.compress(v)` | Enable/disable field compression | All
types |
+| Method | Description | Valid
For |
+| ----------------- | ------------------------------------------------ |
---------------------------------------------------- |
+| `.nullable()` | Mark field as nullable | Smart
pointers, primitives |
+| `.ref()` | Enable reference tracking |
`std::shared_ptr`, `fory::serialization::SharedWeak` |
+| `.dynamic(true)` | Force type info to be written (dynamic dispatch) | Smart
pointers |
+| `.dynamic(false)` | Skip type info (use declared type directly) | Smart
pointers |
+| `.varint()` | Use variable-length encoding |
`uint32_t`, `uint64_t` |
+| `.fixed()` | Use fixed-size encoding |
`uint32_t`, `uint64_t` |
+| `.tagged()` | Use tagged hybrid encoding |
`uint64_t` only |
+| `.compress(v)` | Enable/disable field compression | All
types |
## Default Values
- **Nullable**: Only `std::optional<T>` is nullable by default; all other
types (including `std::shared_ptr`) are non-nullable
-- **Ref tracking**: Disabled by default for all types (including
`std::shared_ptr`)
+- **Ref tracking**: Enabled by default for `std::shared_ptr<T>` and
`fory::serialization::SharedWeak<T>`, disabled for other types unless configured
You **need to configure fields** when:
@@ -552,12 +553,13 @@ FORY_FIELD_CONFIG(User,
### Default Values Summary
-| Type | Default Nullable | Default Ref Tracking |
-| -------------------- | ---------------- | -------------------- |
-| Primitives, `string` | `false` | `false` |
-| `std::optional<T>` | `true` | `false` |
-| `std::shared_ptr<T>` | `false` | `true` |
-| `std::unique_ptr<T>` | `false` | `false` |
+| Type | Default Nullable | Default Ref
Tracking |
+| ------------------------------------ | ---------------- |
-------------------- |
+| Primitives, `string` | `false` | `false`
|
+| `std::optional<T>` | `true` | `false`
|
+| `std::shared_ptr<T>` | `false` | `true`
|
+| `fory::serialization::SharedWeak<T>` | `false` | `true`
|
+| `std::unique_ptr<T>` | `false` | `false`
|
## Related Topics
diff --git a/docs/guide/cpp/supported-types.md
b/docs/guide/cpp/supported-types.md
index 3963852f2..ab191a9ad 100644
--- a/docs/guide/cpp/supported-types.md
+++ b/docs/guide/cpp/supported-types.md
@@ -140,7 +140,7 @@ auto bytes = fory.serialize(shared).value();
auto decoded = fory.deserialize<std::shared_ptr<Person>>(bytes).value();
```
-**With reference tracking enabled (`track_ref(true)`):**
+**With reference tracking enabled (default, `track_ref(true)`):**
- Shared objects are serialized once
- References to the same object are preserved
diff --git a/docs/guide/xlang/field-reference-tracking.md
b/docs/guide/xlang/field-reference-tracking.md
index b4d803a07..729f24400 100644
--- a/docs/guide/xlang/field-reference-tracking.md
+++ b/docs/guide/xlang/field-reference-tracking.md
@@ -113,13 +113,13 @@ By default, **most fields do not track references** even
when global `refTrackin
### Default Behavior by Language
-| Language | Default Ref Tracking | Types That Track Refs by Default |
-| -------- | -------------------- | --------------------------------- |
-| Java | No | None (use annotation to enable) |
-| Python | No | None (use annotation to enable) |
-| Go | No | None (use `fory:"ref"` to enable) |
-| C++ | No | `std::shared_ptr<T>` |
-| Rust | No | `Rc<T>`, `Arc<T>`, `Weak<T>` |
+| Language | Default Ref Tracking | Types That Track Refs by Default
|
+| -------- | -------------------- |
---------------------------------------------------------- |
+| Java | No | None (use annotation to enable)
|
+| Python | No | None (use annotation to enable)
|
+| Go | No | None (use `fory:"ref"` to enable)
|
+| C++ | Yes | `std::shared_ptr<T>`,
`fory::serialization::SharedWeak<T>` |
+| Rust | No | `Rc<T>`, `Arc<T>`, `Weak<T>`
|
### Customizing Per-Field Ref Tracking
@@ -146,18 +146,19 @@ public class Document {
struct Document {
std::string title;
- // shared_ptr tracks refs by default
+ // shared_ptr/SharedWeak track refs by default
std::shared_ptr<Author> author;
+ fory::serialization::SharedWeak<Data> data;
- // Explicitly enable ref tracking
- fory::field<std::vector<Tag>, 1, fory::track_ref<true>> tags;
-
- // Explicitly disable ref tracking
- fory::field<std::shared_ptr<Data>, 2, fory::track_ref<false>> data;
+ // Explicitly mark ref tracking when using field wrappers (optional)
+ fory::field<std::shared_ptr<Tag>, 1, fory::ref> tag_owner;
};
-FORY_STRUCT(Document, title, author, tags, data);
+FORY_STRUCT(Document, title, author, data, tag_owner);
```
+To disable reference tracking for C++ entirely, set
+`Fory::builder().track_ref(false)` on the serializer.
+
#### Rust: Field Attributes
```rust
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]