You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fory.apache.org by ch...@apache.org on 2026/04/29 13:32:59 UTC

(fory) branch main updated: feat(cpp): add nested field codec support (#3636)

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 112a6e4a4 feat(cpp): add nested field codec support (#3636)
112a6e4a4 is described below

commit 112a6e4a4f01ebcd8064e64175163814e78cdadb
Author: Shawn Yang <sh...@gmail.com>
AuthorDate: Wed Apr 29 21:32:55 2026 +0800

    feat(cpp): add nested field codec support (#3636)
    
    ## Why?
    
    C++ needs field metadata that can describe nested container key, value,
    and element encodings so generated C++ xlang structs can match IDL and
    Java metadata for maps, lists, optional wrappers, and integer encodings.
    
    ## What does this PR do?
    
    - Embeds field metadata directly in `FORY_STRUCT` entries with
    `fory::F(...)` and adds recursive `fory::T` value-node specs for scalar,
    list, set, map, and inner carrier nodes.
    - Make C++ struct and union serialization/type metadata paths to honor
    nested specs when writing, reading, and building `FieldType` metadata,
    including configured integer encodings inside lists, maps, and
    optionals.
    - Updates the C++ IDL generator to emit inline `FORY_STRUCT` metadata
    instead of separate `FORY_FIELD_CONFIG` output and allows nested
    collections for the C++ target.
    - Updates C++ docs, README examples, benchmark structs, and
    polymorphism/xlang guides to use the inline field configuration style.
    - Adds compiler, C++ struct, C++ xlang, and Java CPP xlang coverage for
    nested annotated containers in schema-consistent and compatible modes.
    
    ## Related issues
    
    #1017
    #3630
    #3625
    #3630
    
    ## AI Contribution Checklist
    
    
    
    - [ ] Substantial AI assistance was used in this PR: `yes` / `no`
    - [ ] If `yes`, I included a completed [AI Contribution
    Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
    in this PR description and the required `AI Usage Disclosure`.
    - [ ] If `yes`, my PR description includes the required `ai_review`
    summary and screenshot evidence of the final clean AI review results
    from both fresh reviewers on the current PR diff or current HEAD after
    the latest code changes.
    
    
    
    ## Does this PR introduce any user-facing change?
    
    Yes. C++ users configure field metadata inline in `FORY_STRUCT`,
    including nested `fory::T` specs; `FORY_UNION` cases also require
    explicit `fory::F(id)` metadata. This does not intentionally change the
    binary protocol; it enables C++ to emit and read existing nested xlang
    field metadata.
    
    - [x] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 .github/workflows/ci.yml                           |    4 +-
 benchmarks/cpp/benchmark.cc                        |   58 +-
 benchmarks/cpp/benchmark_report.py                 |   30 +-
 compiler/fory_compiler/cli.py                      |    5 +-
 .../fory_compiler/frontend/proto/translator.py     |    1 +
 compiler/fory_compiler/generators/cpp.py           |  224 +--
 compiler/fory_compiler/ir/validator.py             |    6 +-
 .../fory_compiler/tests/test_collection_nesting.py |   11 +
 .../fory_compiler/tests/test_generated_code.py     |   22 +-
 cpp/fory/meta/BUILD                                |    1 +
 cpp/fory/meta/field.h                              | 1766 +-------------------
 cpp/fory/meta/field_info.h                         |  319 +++-
 cpp/fory/meta/field_test.cc                        |  431 +----
 cpp/fory/serialization/BUILD                       |    9 -
 cpp/fory/serialization/collection_serializer.h     |    3 +-
 cpp/fory/serialization/field_serializer_test.cc    | 1211 --------------
 cpp/fory/serialization/namespace_macro_test.cc     |   81 +-
 cpp/fory/serialization/serializer_traits.h         |   15 +-
 cpp/fory/serialization/skip.cc                     |   10 +-
 .../serialization/smart_ptr_serializer_test.cc     |   32 +-
 cpp/fory/serialization/struct_serializer.h         |  753 ++++++++-
 cpp/fory/serialization/struct_test.cc              |  248 ++-
 cpp/fory/serialization/type_resolver.cc            |  105 +-
 cpp/fory/serialization/type_resolver.h             |  565 +++++--
 cpp/fory/serialization/union_serializer.h          |  533 +++++-
 cpp/fory/serialization/xlang_test_main.cc          |  146 +-
 docs/benchmarks/cpp/README.md                      |   76 +-
 docs/benchmarks/cpp/mediacontent.png               |  Bin 52226 -> 52441 bytes
 docs/benchmarks/cpp/mediacontentlist.png           |  Bin 57111 -> 57760 bytes
 docs/benchmarks/cpp/sample.png                     |  Bin 55251 -> 54468 bytes
 docs/benchmarks/cpp/samplelist.png                 |  Bin 53909 -> 53635 bytes
 docs/benchmarks/cpp/struct.png                     |  Bin 51352 -> 51954 bytes
 docs/benchmarks/cpp/structlist.png                 |  Bin 53820 -> 52960 bytes
 docs/benchmarks/cpp/throughput.png                 |  Bin 78902 -> 80164 bytes
 docs/compiler/generated-code.md                    |    4 +-
 docs/compiler/schema-idl.md                        |    7 +-
 docs/guide/cpp/field-configuration.md              |  576 ++-----
 docs/guide/cpp/polymorphism.md                     |   46 +-
 docs/guide/xlang/field-nullability.md              |   27 +-
 docs/guide/xlang/field-reference-tracking.md       |   12 +-
 docs/guide/xlang/field-type-meta.md                |   28 +-
 .../java/org/apache/fory/xlang/CPPXlangTest.java   |   12 +
 42 files changed, 2950 insertions(+), 4427 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index adfd5b080..3a5b8db11 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -999,7 +999,7 @@ jobs:
   python:
     name: Python CI
     needs: changes
-    if: needs.changes.outputs.python == 'true'
+    if: needs.changes.outputs.python == 'true' || needs.changes.outputs.cpp == 'true'
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
@@ -1039,7 +1039,7 @@ jobs:
   python_windows:
     name: Python CI (${{ matrix.python-version }}, windows-2022)
     needs: changes
-    if: needs.changes.outputs.python == 'true'
+    if: needs.changes.outputs.python == 'true' || needs.changes.outputs.cpp == 'true'
     runs-on: windows-2022
     strategy:
       matrix:
diff --git a/benchmarks/cpp/benchmark.cc b/benchmarks/cpp/benchmark.cc
index f1d65bff4..b9ed84e5c 100644
--- a/benchmarks/cpp/benchmark.cc
+++ b/benchmarks/cpp/benchmark.cc
@@ -49,9 +49,9 @@ struct NumericStruct {
   }
   MSGPACK_DEFINE_MAP(f1, f2, f3, f4, f5, f6, f7, f8);
 };
-FORY_STRUCT(NumericStruct, f1, f2, f3, f4, f5, f6, f7, f8);
-FORY_FIELD_TAGS(NumericStruct, (f1, 1), (f2, 2), (f3, 3), (f4, 4), (f5, 5),
-                (f6, 6), (f7, 7), (f8, 8));
+FORY_STRUCT(NumericStruct, (f1, fory::F(1)), (f2, fory::F(2)), (f3, fory::F(3)),
+            (f4, fory::F(4)), (f5, fory::F(5)), (f6, fory::F(6)),
+            (f7, fory::F(7)), (f8, fory::F(8)));
 
 struct Sample {
   int32_t int_value;
@@ -103,20 +103,17 @@ struct Sample {
                      int_array, long_array, float_array, double_array,
                      short_array, char_array, boolean_array, string);
 };
-FORY_STRUCT(Sample, int_value, long_value, float_value, double_value,
-            short_value, char_value, boolean_value, int_value_boxed,
-            long_value_boxed, float_value_boxed, double_value_boxed,
-            short_value_boxed, char_value_boxed, boolean_value_boxed, int_array,
-            long_array, float_array, double_array, short_array, char_array,
-            boolean_array, string);
-FORY_FIELD_TAGS(Sample, (int_value, 1), (long_value, 2), (float_value, 3),
-                (double_value, 4), (short_value, 5), (char_value, 6),
-                (boolean_value, 7), (int_value_boxed, 8), (long_value_boxed, 9),
-                (float_value_boxed, 10), (double_value_boxed, 11),
-                (short_value_boxed, 12), (char_value_boxed, 13),
-                (boolean_value_boxed, 14), (int_array, 15), (long_array, 16),
-                (float_array, 17), (double_array, 18), (short_array, 19),
-                (char_array, 20), (boolean_array, 21), (string, 22));
+FORY_STRUCT(Sample, (int_value, fory::F(1)), (long_value, fory::F(2)),
+            (float_value, fory::F(3)), (double_value, fory::F(4)),
+            (short_value, fory::F(5)), (char_value, fory::F(6)),
+            (boolean_value, fory::F(7)), (int_value_boxed, fory::F(8)),
+            (long_value_boxed, fory::F(9)), (float_value_boxed, fory::F(10)),
+            (double_value_boxed, fory::F(11)), (short_value_boxed, fory::F(12)),
+            (char_value_boxed, fory::F(13)), (boolean_value_boxed, fory::F(14)),
+            (int_array, fory::F(15)), (long_array, fory::F(16)),
+            (float_array, fory::F(17)), (double_array, fory::F(18)),
+            (short_array, fory::F(19)), (char_array, fory::F(20)),
+            (boolean_array, fory::F(21)), (string, fory::F(22)));
 
 // Enums for MediaContent benchmark
 enum class Player : int32_t { JAVA = 0, FLASH = 1 };
@@ -150,11 +147,11 @@ struct Media {
   MSGPACK_DEFINE_MAP(uri, title, width, height, format, duration, size, bitrate,
                      has_bitrate, persons, player, copyright);
 };
-FORY_STRUCT(Media, uri, title, width, height, format, duration, size, bitrate,
-            has_bitrate, persons, player, copyright);
-FORY_FIELD_TAGS(Media, (uri, 1), (title, 2), (width, 3), (height, 4),
-                (format, 5), (duration, 6), (size, 7), (bitrate, 8),
-                (has_bitrate, 9), (persons, 10), (player, 11), (copyright, 12));
+FORY_STRUCT(Media, (uri, fory::F(1)), (title, fory::F(2)), (width, fory::F(3)),
+            (height, fory::F(4)), (format, fory::F(5)), (duration, fory::F(6)),
+            (size, fory::F(7)), (bitrate, fory::F(8)),
+            (has_bitrate, fory::F(9)), (persons, fory::F(10)),
+            (player, fory::F(11)), (copyright, fory::F(12)));
 
 struct Image {
   std::string uri;
@@ -169,9 +166,8 @@ struct Image {
   }
   MSGPACK_DEFINE_MAP(uri, title, width, height, size);
 };
-FORY_STRUCT(Image, uri, title, width, height, size);
-FORY_FIELD_TAGS(Image, (uri, 1), (title, 2), (width, 3), (height, 4),
-                (size, 5));
+FORY_STRUCT(Image, (uri, fory::F(1)), (title, fory::F(2)), (width, fory::F(3)),
+            (height, fory::F(4)), (size, fory::F(5)));
 
 struct MediaContent {
   Media media;
@@ -182,8 +178,7 @@ struct MediaContent {
   }
   MSGPACK_DEFINE_MAP(media, images);
 };
-FORY_STRUCT(MediaContent, media, images);
-FORY_FIELD_TAGS(MediaContent, (media, 1), (images, 2));
+FORY_STRUCT(MediaContent, (media, fory::F(1)), (images, fory::F(2)));
 
 struct StructList {
   std::vector<NumericStruct> struct_list;
@@ -193,8 +188,7 @@ struct StructList {
   }
   MSGPACK_DEFINE_MAP(struct_list);
 };
-FORY_STRUCT(StructList, struct_list);
-FORY_FIELD_TAGS(StructList, (struct_list, 1));
+FORY_STRUCT(StructList, (struct_list, fory::F(1)));
 
 struct SampleList {
   std::vector<Sample> sample_list;
@@ -204,8 +198,7 @@ struct SampleList {
   }
   MSGPACK_DEFINE_MAP(sample_list);
 };
-FORY_STRUCT(SampleList, sample_list);
-FORY_FIELD_TAGS(SampleList, (sample_list, 1));
+FORY_STRUCT(SampleList, (sample_list, fory::F(1)));
 
 struct MediaContentList {
   std::vector<MediaContent> media_content_list;
@@ -215,8 +208,7 @@ struct MediaContentList {
   }
   MSGPACK_DEFINE_MAP(media_content_list);
 };
-FORY_STRUCT(MediaContentList, media_content_list);
-FORY_FIELD_TAGS(MediaContentList, (media_content_list, 1));
+FORY_STRUCT(MediaContentList, (media_content_list, fory::F(1)));
 
 // ============================================================================
 // Test data creation
diff --git a/benchmarks/cpp/benchmark_report.py b/benchmarks/cpp/benchmark_report.py
index 1480818cf..fd3944d8e 100644
--- a/benchmarks/cpp/benchmark_report.py
+++ b/benchmarks/cpp/benchmark_report.py
@@ -45,6 +45,17 @@ SERIALIZER_LABELS = {
     "protobuf": "protobuf",
     "msgpack": "msgpack",
 }
+DATATYPE_ORDER = [
+    "struct",
+    "sample",
+    "mediacontent",
+    "structlist",
+    "samplelist",
+    "mediacontentlist",
+]
+DATATYPE_ORDER_INDEX = {
+    datatype: index for index, datatype in enumerate(DATATYPE_ORDER)
+}
 
 # === Parse arguments ===
 parser = argparse.ArgumentParser(
@@ -135,6 +146,16 @@ def format_datatype_table_label(datatype):
     return datatype.capitalize()
 
 
+def ordered_datatypes(datatypes):
+    return sorted(
+        datatypes,
+        key=lambda datatype: (
+            DATATYPE_ORDER_INDEX.get(datatype, len(DATATYPE_ORDER)),
+            datatype,
+        ),
+    )
+
+
 # === Read and parse benchmark JSON ===
 def load_benchmark_data(json_file):
     with open(json_file, "r", encoding="utf-8") as f:
@@ -243,7 +264,7 @@ def plot_datatype(ax, datatype, operation):
 
 # === Create plots ===
 plot_images = []
-datatypes = sorted(data.keys())
+datatypes = ordered_datatypes(data.keys())
 operations = ["serialize", "deserialize"]
 
 for datatype in datatypes:
@@ -343,7 +364,12 @@ for k, v in system_info.items():
 md_report.append("\n## Benchmark Plots\n")
 md_report.append("\nAll class-level plots below show throughput (ops/sec).\n")
 plot_images_sorted = sorted(
-    plot_images, key=lambda item: (0 if item[0] == "throughput" else 1, item[0])
+    plot_images,
+    key=lambda item: (
+        0 if item[0] == "throughput" else 1,
+        DATATYPE_ORDER_INDEX.get(item[0], len(DATATYPE_ORDER)),
+        item[0],
+    ),
 )
 for datatype, img in plot_images_sorted:
     img_filename = os.path.basename(img)
diff --git a/compiler/fory_compiler/cli.py b/compiler/fory_compiler/cli.py
index 541e100a1..c0620b854 100644
--- a/compiler/fory_compiler/cli.py
+++ b/compiler/fory_compiler/cli.py
@@ -520,7 +520,10 @@ def compile_file(
             print("======================")
 
     # Validate merged schema
-    validator = SchemaValidator(schema)
+    allow_nested_collections = set(lang_output_dirs) == {"cpp"}
+    validator = SchemaValidator(
+        schema, allow_nested_collections=allow_nested_collections
+    )
     if not validator.validate():
         for error in validator.errors:
             print(f"Error: {error}", file=sys.stderr)
diff --git a/compiler/fory_compiler/frontend/proto/translator.py b/compiler/fory_compiler/frontend/proto/translator.py
index 0bbebe8a0..f3f453721 100644
--- a/compiler/fory_compiler/frontend/proto/translator.py
+++ b/compiler/fory_compiler/frontend/proto/translator.py
@@ -297,6 +297,7 @@ class ProtoTranslator:
                 oneof_type_name, location=self._location(oneof.line, oneof.column)
             ),
             number=first_case.number,
+            tag_id=first_case.number,
             optional=True,
             ref=False,
             options={},
diff --git a/compiler/fory_compiler/generators/cpp.py b/compiler/fory_compiler/generators/cpp.py
index f1b6f4de6..66605d973 100644
--- a/compiler/fory_compiler/generators/cpp.py
+++ b/compiler/fory_compiler/generators/cpp.py
@@ -125,14 +125,6 @@ class CppGenerator(BaseGenerator):
             return f"{namespace}::{qualified_name}"
         return qualified_name
 
-    def get_field_config_type(
-        self,
-        type_name: str,
-        parent_stack: List[Message],
-    ) -> str:
-        """Get type name for FORY_FIELD_CONFIG."""
-        return self.get_namespaced_type_name(type_name, parent_stack)
-
     def is_imported_type(self, type_def: object) -> bool:
         """Return True if a type definition comes from an imported IDL file."""
         if not self.schema.source_file:
@@ -252,7 +244,6 @@ class CppGenerator(BaseGenerator):
         includes: Set[str] = set()
         enum_macros: List[str] = []
         union_macros: List[str] = []
-        field_config_macros: List[str] = []
         evolving_macros: List[str] = []
         definition_items = self.get_definition_order()
 
@@ -342,7 +333,6 @@ class CppGenerator(BaseGenerator):
                     [],
                     enum_macros,
                     union_macros,
-                    field_config_macros,
                     evolving_macros,
                     "",
                 )
@@ -353,10 +343,6 @@ class CppGenerator(BaseGenerator):
             lines.extend(union_macros)
             lines.append("")
 
-        if field_config_macros:
-            lines.extend(field_config_macros)
-            lines.append("")
-
         if enum_macros:
             lines.extend(enum_macros)
             lines.append("")
@@ -658,12 +644,14 @@ class CppGenerator(BaseGenerator):
             return f"std::unique_ptr<{type_name}>"
         return self.generate_type(
             field.field_type,
-            False
-            if (
-                self.is_message_type(field.field_type, parent_stack)
-                and (field.ref or weak_ref)
-            )
-            else field.optional,
+            (
+                False
+                if (
+                    self.is_message_type(field.field_type, parent_stack)
+                    and (field.ref or weak_ref)
+                )
+                else field.optional
+            ),
             field.ref,
             field.element_optional,
             field.element_ref,
@@ -878,7 +866,6 @@ class CppGenerator(BaseGenerator):
         parent_stack: List[Message],
         enum_macros: List[str],
         union_macros: List[str],
-        field_config_macros: List[str],
         evolving_macros: List[str],
         indent: str,
     ) -> List[str]:
@@ -908,7 +895,6 @@ class CppGenerator(BaseGenerator):
                     lineage,
                     enum_macros,
                     union_macros,
-                    field_config_macros,
                     evolving_macros,
                     body_indent,
                 )
@@ -959,13 +945,7 @@ class CppGenerator(BaseGenerator):
             lines.append("")
             lines.append(f"{body_indent}public:")
             field_members = ", ".join(
-                self.get_field_member_name(f) for f in message.fields
-            )
-            field_config_type_name = self.get_field_config_type(
-                message.name, parent_stack
-            )
-            field_config_macros.append(
-                self.generate_field_config_macro(message, field_config_type_name)
+                self.get_field_macro_entry(f) for f in message.fields
             )
             lines.append(
                 f"{body_indent}FORY_STRUCT({struct_type_name}, {field_members});"
@@ -1144,7 +1124,7 @@ class CppGenerator(BaseGenerator):
                 case_ctor = self.to_snake_case(field.name)
                 meta = self.get_union_field_meta(field)
                 suffix = "," if index + 1 < len(union.fields) else ""
-                lines.append(f"  ({case_type}, {case_ctor}, {meta}){suffix}")
+                lines.append(f"  ({case_ctor}, {case_type}, {meta}){suffix}")
             lines.append(");")
             return lines
 
@@ -1539,39 +1519,40 @@ class CppGenerator(BaseGenerator):
 
         return "void*"
 
-    def generate_field_config_macro(
-        self,
-        message: Message,
-        qualified_name: str,
-    ) -> str:
-        """Generate FORY_FIELD_CONFIG macro for a message."""
-        entries = []
-        for field in message.fields:
-            field_name = self.get_field_member_name(field)
-            meta = self.get_field_meta(field)
-            entries.append(f"({field_name}, {meta})")
-        joined = ", ".join(entries)
-        return f"FORY_FIELD_CONFIG({qualified_name}, {joined});"
+    def get_field_macro_entry(self, field: Field) -> str:
+        """Build one FORY_STRUCT field entry."""
+        return f"({self.get_field_member_name(field)}, {self.get_field_meta(field)})"
 
     def get_field_meta(self, field: Field) -> str:
         """Build FieldMeta expression for a field."""
-        meta = "fory::F()"
+        if field.tag_id is not None:
+            meta = f"fory::F({field.tag_id})"
+        else:
+            meta = "fory::F()"
         is_any = (
             isinstance(field.field_type, PrimitiveType)
             and field.field_type.kind == PrimitiveKind.ANY
         )
-        if field.tag_id is not None:
-            meta += f".id({field.tag_id})"
         if field.optional or is_any:
             meta += ".nullable()"
         if field.ref:
             meta += ".ref()"
-        encoding = self.get_encoding_config(field.field_type)
-        if encoding:
-            meta += encoding
-        array_type = self.get_array_type_config(field)
-        if array_type:
-            meta += array_type
+        spec = self.get_field_type_spec(field)
+        if spec:
+            if field.optional or field.ref:
+                meta += f".inner({spec})"
+            elif spec.startswith("fory::T::list("):
+                meta += f".list({spec[len('fory::T::list(') : -1]})"
+            elif spec.startswith("fory::T::set("):
+                meta += f".set({spec[len('fory::T::set(') : -1]})"
+            elif spec.startswith("fory::T::map("):
+                meta += f".map({spec[len('fory::T::map(') : -1]})"
+            elif spec.endswith(".fixed()"):
+                meta += ".fixed()"
+            elif spec.endswith(".varint()"):
+                meta += ".varint()"
+            elif spec.endswith(".tagged()"):
+                meta += ".tagged()"
         return meta
 
     def get_union_field_meta(self, field: Field) -> str:
@@ -1585,52 +1566,105 @@ class CppGenerator(BaseGenerator):
             meta += ".nullable()"
         if field.ref:
             meta += ".ref()"
-        encoding = self.get_encoding_config(field.field_type)
-        if encoding:
-            meta += encoding
-        array_type = self.get_array_type_config(field)
-        if array_type:
-            meta += array_type
+        spec = self.get_field_type_spec(field)
+        if spec:
+            if field.optional or field.ref:
+                meta += f".inner({spec})"
+            elif spec.startswith("fory::T::list("):
+                meta += f".list({spec[len('fory::T::list(') : -1]})"
+            elif spec.startswith("fory::T::set("):
+                meta += f".set({spec[len('fory::T::set(') : -1]})"
+            elif spec.startswith("fory::T::map("):
+                meta += f".map({spec[len('fory::T::map(') : -1]})"
+            elif spec.endswith(".fixed()"):
+                meta += ".fixed()"
+            elif spec.endswith(".varint()"):
+                meta += ".varint()"
+            elif spec.endswith(".tagged()"):
+                meta += ".tagged()"
         return meta
 
-    def get_encoding_config(self, field_type: FieldType) -> str:
-        """Return encoding config for primitive types."""
-        kind = None
-        if isinstance(field_type, PrimitiveType):
-            kind = field_type.kind
-        elif isinstance(field_type, ListType) and isinstance(
-            field_type.element_type, PrimitiveType
-        ):
-            kind = field_type.element_type.kind
-        if kind is None:
-            return ""
-        if kind in (
-            PrimitiveKind.INT32,
-            PrimitiveKind.INT64,
-            PrimitiveKind.UINT32,
-            PrimitiveKind.UINT64,
-        ):
-            return ".fixed()"
-        if kind in (PrimitiveKind.VAR_UINT32, PrimitiveKind.VAR_UINT64):
-            return ".varint()"
-        if kind in (PrimitiveKind.TAGGED_INT64, PrimitiveKind.TAGGED_UINT64):
-            return ".tagged()"
-        return ""
+    def get_field_type_spec(self, field: Field) -> str:
+        """Return the T-node spec for a field when generated metadata needs it."""
+        return self.get_type_spec(
+            field.field_type,
+            field.element_optional,
+            field.element_ref,
+        )
 
-    def get_array_type_config(self, field: Field) -> str:
-        """Return array type override for int8/uint8 arrays."""
-        if not isinstance(field.field_type, ListType):
-            return ""
-        if field.element_optional or field.element_ref:
-            return ""
-        element_type = field.field_type.element_type
-        if not isinstance(element_type, PrimitiveType):
-            return ""
-        if element_type.kind == PrimitiveKind.INT8:
-            return ".int8_array()"
-        if element_type.kind == PrimitiveKind.UINT8:
-            return ".uint8_array()"
-        return ""
+    def get_type_spec(
+        self,
+        field_type: FieldType,
+        optional: bool = False,
+        ref: bool = False,
+    ) -> str:
+        """Return a recursive fory::T spec for generated C++ metadata."""
+        if isinstance(field_type, PrimitiveType):
+            spec = self.get_primitive_type_spec(field_type.kind)
+        elif isinstance(field_type, ListType):
+            if (
+                isinstance(field_type.element_type, PrimitiveType)
+                and not field_type.element_optional
+                and not field_type.element_ref
+                and field_type.element_type.kind
+                not in (PrimitiveKind.INT8, PrimitiveKind.UINT8)
+            ):
+                spec = ""
+            else:
+                element = self.get_type_spec(
+                    field_type.element_type,
+                    field_type.element_optional,
+                    field_type.element_ref,
+                )
+                if not element:
+                    element = "fory::FieldNodeSpec{}"
+                spec = f"fory::T::list({element})"
+        elif isinstance(field_type, MapType):
+            key = self.get_type_spec(field_type.key_type)
+            element = self.get_type_spec(
+                field_type.value_type,
+                field_type.value_optional,
+                field_type.value_ref,
+            )
+            if key or element:
+                if not key:
+                    key = "fory::FieldNodeSpec{}"
+                if not element:
+                    element = "fory::FieldNodeSpec{}"
+                spec = f"fory::T::map({key}, {element})"
+            else:
+                spec = ""
+        else:
+            spec = ""
+
+        if (optional or ref) and spec:
+            return f"fory::T::inner({spec})"
+        return spec
+
+    def get_primitive_type_spec(self, kind: PrimitiveKind) -> str:
+        """Return a scalar T-node spec for primitive encoding metadata."""
+        typed = {
+            PrimitiveKind.BOOL: "fory::T::boolean()",
+            PrimitiveKind.INT8: "fory::T::int8()",
+            PrimitiveKind.INT16: "fory::T::int16()",
+            PrimitiveKind.INT32: "fory::T::int32().fixed()",
+            PrimitiveKind.VARINT32: "fory::T::int32().varint()",
+            PrimitiveKind.INT64: "fory::T::int64().fixed()",
+            PrimitiveKind.VARINT64: "fory::T::int64().varint()",
+            PrimitiveKind.TAGGED_INT64: "fory::T::int64().tagged()",
+            PrimitiveKind.UINT8: "fory::T::uint8()",
+            PrimitiveKind.UINT16: "fory::T::uint16()",
+            PrimitiveKind.UINT32: "fory::T::uint32().fixed()",
+            PrimitiveKind.VAR_UINT32: "fory::T::uint32().varint()",
+            PrimitiveKind.UINT64: "fory::T::uint64().fixed()",
+            PrimitiveKind.VAR_UINT64: "fory::T::uint64().varint()",
+            PrimitiveKind.TAGGED_UINT64: "fory::T::uint64().tagged()",
+            PrimitiveKind.FLOAT16: "fory::T::float16()",
+            PrimitiveKind.FLOAT32: "fory::T::float32()",
+            PrimitiveKind.FLOAT64: "fory::T::float64()",
+            PrimitiveKind.STRING: "fory::T::string()",
+        }
+        return typed.get(kind, "")
 
     def generate_type(
         self,
diff --git a/compiler/fory_compiler/ir/validator.py b/compiler/fory_compiler/ir/validator.py
index acd774177..0553c7524 100644
--- a/compiler/fory_compiler/ir/validator.py
+++ b/compiler/fory_compiler/ir/validator.py
@@ -54,8 +54,9 @@ class ValidationIssue:
 class SchemaValidator:
     """Validates a Fory IR schema."""
 
-    def __init__(self, schema: Schema):
+    def __init__(self, schema: Schema, allow_nested_collections: bool = False):
         self.schema = schema
+        self.allow_nested_collections = allow_nested_collections
         self.errors: List[ValidationIssue] = []
         self.warnings: List[ValidationIssue] = []
 
@@ -67,7 +68,8 @@ class SchemaValidator:
         self._check_messages()
         self._check_type_references()
         self._check_services()
-        self._check_collection_nesting()
+        if not self.allow_nested_collections:
+            self._check_collection_nesting()
         self._check_ref_rules()
         self._check_weak_refs()
         return not self.errors
diff --git a/compiler/fory_compiler/tests/test_collection_nesting.py b/compiler/fory_compiler/tests/test_collection_nesting.py
index 881451aeb..efb1ee622 100644
--- a/compiler/fory_compiler/tests/test_collection_nesting.py
+++ b/compiler/fory_compiler/tests/test_collection_nesting.py
@@ -79,3 +79,14 @@ def test_map_with_list_key_rejected():
     }
     """
     assert_nested_collections_rejected(source)
+
+
+def test_nested_collections_allowed_for_capable_targets():
+    source = """
+    message Foo {
+        map<string, list<int32>> values = 1;
+    }
+    """
+    schema = parse_schema(source)
+    validator = SchemaValidator(schema, allow_nested_collections=True)
+    assert validator.validate(), validator.errors
diff --git a/compiler/fory_compiler/tests/test_generated_code.py b/compiler/fory_compiler/tests/test_generated_code.py
index f35d24742..7b1510e88 100644
--- a/compiler/fory_compiler/tests/test_generated_code.py
+++ b/compiler/fory_compiler/tests/test_generated_code.py
@@ -596,6 +596,26 @@ def test_java_nested_integer_annotations_in_generic_containers():
     )
 
 
+def test_cpp_nested_integer_specs_in_generic_containers():
+    schema = parse_fdl(
+        dedent(
+            """
+            package gen;
+
+            message NestedIntegerSpecs {
+                map<fixed_uint32, list<optional tagged_uint64>> values = 1;
+            }
+            """
+        )
+    )
+    cpp_output = render_files(generate_files(schema, CppGenerator))
+    assert (
+        "FORY_STRUCT(NestedIntegerSpecs, "
+        "(values_, fory::F(1).map(fory::T::uint32().fixed(), "
+        "fory::T::list(fory::T::inner(fory::T::uint64().tagged())))));" in cpp_output
+    )
+
+
 def test_cpp_generator_supports_decimal_fields_and_unions():
     schema = parse_fdl(
         dedent(
@@ -618,7 +638,7 @@ def test_cpp_generator_supports_decimal_fields_and_unions():
     assert '#include "fory/serialization/decimal_serializers.h"' in cpp_output
     assert "const fory::serialization::Decimal& amount() const" in cpp_output
     assert "std::variant<fory::serialization::Decimal, Money> value_" in cpp_output
-    assert "(fory::serialization::Decimal, amount, fory::F(1))" in cpp_output
+    assert "(amount, fory::serialization::Decimal, fory::F(1))" in cpp_output
 
 
 def test_java_enum_generation_uses_fory_enum_ids():
diff --git a/cpp/fory/meta/BUILD b/cpp/fory/meta/BUILD
index d5f02247f..0b2b64a61 100644
--- a/cpp/fory/meta/BUILD
+++ b/cpp/fory/meta/BUILD
@@ -64,5 +64,6 @@ cc_test(
     deps = [
         ":fory_meta",
         "@googletest//:gtest",
+        "@googletest//:gtest_main",
     ],
 )
diff --git a/cpp/fory/meta/field.h b/cpp/fory/meta/field.h
index a7e23d9b2..faad1c2e7 100644
--- a/cpp/fory/meta/field.h
+++ b/cpp/fory/meta/field.h
@@ -20,16 +20,13 @@
 #pragma once
 
 #include "fory/meta/field_info.h"
-#include "fory/meta/preprocessor.h"
 #include "fory/type/type.h"
-#include <array>
 #include <cstdint>
 #include <memory>
 #include <optional>
 #include <string_view>
 #include <tuple>
 #include <type_traits>
-#include <utility>
 
 namespace fory {
 
@@ -37,372 +34,53 @@ namespace serialization {
 template <typename T> class SharedWeak;
 } // namespace serialization
 
-// ============================================================================
-// Field Option Tags
-// ============================================================================
-
-/// 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/SharedWeak/unique_ptr, non-nullable is already the
-/// default.
-struct not_null {};
-
-/// 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.
-/// - `dynamic<true>`: Force type info to be written (enable runtime subtype
-/// support)
-/// - `dynamic<false>`: skip type info (use declared type directly)
-///
-/// By default, Fory auto-detects polymorphism via `std::is_polymorphic<T>`.
-/// Use this tag to override the default behavior.
-///
-/// Example:
-///   fory::field<std::shared_ptr<Base>, 0, fory::dynamic<false>> ptr;
-template <bool V> struct dynamic : std::bool_constant<V> {};
-
 namespace detail {
 
-// ============================================================================
-// Type Traits for Smart Pointers and Optional
-// ============================================================================
-
 template <typename T>
 using FieldInfo = decltype(::fory::meta::fory_field_info(std::declval<T>()));
 
-inline constexpr size_t k_invalid_field_index = static_cast<size_t>(-1);
-
-template <typename T> constexpr size_t field_index(std::string_view name) {
-  constexpr auto names = FieldInfo<T>::Names;
-  for (size_t i = 0; i < names.size(); ++i) {
-    if (names[i] == name) {
-      return i;
-    }
-  }
-  return k_invalid_field_index;
-}
-
-template <typename T, size_t Index, typename Enable = void> struct FieldTypeAt;
-
-template <typename T, size_t Index>
-struct FieldTypeAt<T, Index, std::enable_if_t<Index != k_invalid_field_index>> {
-  using PtrsType = typename FieldInfo<T>::PtrsType;
-  using PtrT = std::tuple_element_t<Index, PtrsType>;
-  using type = ::fory::meta::RemoveMemberPointerCVRefT<PtrT>;
-};
-
-template <typename T, size_t Index>
-struct FieldTypeAt<T, Index, std::enable_if_t<Index == k_invalid_field_index>> {
-  static_assert(Index != k_invalid_field_index,
-                "Unknown field name in FORY_FIELD_TAGS");
-};
-
 template <typename T> struct is_shared_ptr : std::false_type {};
-
 template <typename T>
 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>
 struct is_unique_ptr<std::unique_ptr<T, D>> : std::true_type {};
-
 template <typename T>
 inline constexpr bool is_unique_ptr_v = is_unique_ptr<T>::value;
 
 template <typename T> struct is_optional : std::false_type {};
-
 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/SharedWeak or unique_ptr
 template <typename 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
-// ============================================================================
-
-/// Check if a specific tag type is present in the Options pack
-template <typename Tag, typename... Options>
-inline constexpr bool has_option_v = (std::is_same_v<Tag, Options> || ...);
-
-/// Check if a type is a dynamic<V> tag
-template <typename T> struct is_dynamic_tag : std::false_type {};
-template <bool V> struct is_dynamic_tag<dynamic<V>> : std::true_type {};
 template <typename T>
-inline constexpr bool is_dynamic_tag_v = is_dynamic_tag<T>::value;
-
-/// Check if any dynamic<V> tag is present in Options pack
-template <typename... Options>
-inline constexpr bool has_dynamic_option_v = (is_dynamic_tag_v<Options> || ...);
-
-/// Extract the dynamic value from Options pack (default = -1 for AUTO)
-/// Returns: 1 for dynamic<true>, 0 for dynamic<false>, -1 for AUTO (not
-/// specified)
-template <typename... Options> struct get_dynamic_value {
-  static constexpr int value = -1; // AUTO
-};
-template <bool V, typename... Rest>
-struct get_dynamic_value<dynamic<V>, Rest...> {
-  static constexpr int value = V ? 1 : 0;
-};
-template <typename First, typename... Rest>
-struct get_dynamic_value<First, Rest...> {
-  static constexpr int value = get_dynamic_value<Rest...>::value;
-};
-template <typename... Options>
-inline constexpr int get_dynamic_value_v = get_dynamic_value<Options...>::value;
-
-// ============================================================================
-// Field Tag Entry for FORY_FIELD_TAGS Macro
-// ============================================================================
-
-/// Compile-time field tag metadata entry
-/// Dynamic: -1 = AUTO (use std::is_polymorphic), 0 = FALSE (not dynamic), 1 =
-/// TRUE (dynamic)
-template <int16_t Id, bool Nullable, bool Ref, int Dynamic = -1>
-struct FieldTagEntry {
-  static constexpr int16_t id = Id;
-  static constexpr bool is_nullable = Nullable;
-  static constexpr bool track_ref = Ref;
-  static constexpr int dynamic_value = Dynamic;
-};
-
-struct FieldTagEntryWithName {
-  const char *name;
-  int16_t id;
-  bool is_nullable;
-  bool track_ref;
-  int dynamic_value;
-};
-
-template <typename Entry>
-constexpr FieldTagEntryWithName make_field_tag_entry(const char *name) {
-  return FieldTagEntryWithName{name, Entry::id, Entry::is_nullable,
-                               Entry::track_ref, Entry::dynamic_value};
-}
-
-/// Default: no field tags defined for type T (legacy specialization path)
-template <typename T> struct ForyFieldTagsImpl {
-  static constexpr bool has_tags = false;
-};
+using MemberFieldConfigDescriptor =
+    decltype(T::fory_field_config(std::declval<meta::Identity<T>>()));
 
 template <typename T>
-using AdlFieldTagsDescriptor =
-    decltype(fory_field_tags(std::declval<meta::Identity<T>>()));
+using AdlFieldConfigDescriptor =
+    decltype(fory_field_config(std::declval<meta::Identity<T>>()));
 
 template <typename T, typename = void>
-struct HasAdlFieldTags : std::false_type {};
+struct HasMemberFieldConfig : std::false_type {};
 
 template <typename T>
-struct HasAdlFieldTags<T, std::void_t<AdlFieldTagsDescriptor<T>>>
+struct HasMemberFieldConfig<T, std::void_t<MemberFieldConfigDescriptor<T>>>
     : std::true_type {};
 
-template <typename T, typename Enable = void> struct FieldTagsInfo {
-  static constexpr bool has_tags = false;
-  static constexpr size_t field_count = 0;
-  static inline constexpr auto entries = std::tuple<>{};
-  using Entries = std::decay_t<decltype(entries)>;
-  static constexpr bool use_index = true;
-};
-
-template <typename T>
-struct FieldTagsInfo<T, std::enable_if_t<HasAdlFieldTags<T>::value>> {
-  using Descriptor = AdlFieldTagsDescriptor<T>;
-  static constexpr bool has_tags = Descriptor::has_tags;
-  static inline constexpr auto entries = Descriptor::entries;
-  using Entries = std::decay_t<decltype(entries)>;
-  static constexpr size_t field_count = std::tuple_size_v<Entries>;
-  static constexpr bool use_index = false;
-};
-
-template <typename T>
-struct FieldTagsInfo<T, std::enable_if_t<!HasAdlFieldTags<T>::value &&
-                                         ForyFieldTagsImpl<T>::has_tags>> {
-  static constexpr bool has_tags = true;
-  static constexpr size_t field_count = ForyFieldTagsImpl<T>::field_count;
-  using Entries = typename ForyFieldTagsImpl<T>::Entries;
-  static inline constexpr auto entries = Entries{};
-  static constexpr bool use_index = true;
-};
-
-template <typename T>
-inline constexpr bool has_field_tags_v = FieldTagsInfo<T>::has_tags;
-
-} // namespace detail
-
-// ============================================================================
-// Field Encoding Types for Unsigned Integers
-// ============================================================================
-
-/// Encoding strategies for integer fields
-enum class Encoding {
-  Default = 0, // Use type's default encoding
-  Varint = 1,  // Variable-length encoding (smaller values use fewer bytes)
-  Fixed = 2,   // Fixed-size encoding (always uses full type width)
-  Tagged = 3   // Tagged encoding (uses tag byte + value)
-};
-
-// ============================================================================
-// FieldMeta - Compile-time Field Configuration with Builder Pattern
-// ============================================================================
-
-/// Compile-time field metadata with fluent builder API.
-/// Supports both:
-///   - Simple: F(0) - just field ID
-///   - Full:   F(0).nullable().varint().compress(false).dynamic(false)
-struct FieldMeta {
-  int16_t id_ = -1;
-  bool nullable_ = false;
-  bool ref_ = false;
-  int dynamic_ = -1; // -1 = AUTO, 0 = FALSE, 1 = TRUE
-  Encoding encoding_ = Encoding::Default;
-  bool compress_ = true;
-  int16_t type_id_override_ = -1; // -1 = unset
-
-  // Builder methods - each returns a modified copy
-  constexpr FieldMeta id(int16_t v) const {
-    auto c = *this;
-    c.id_ = v;
-    return c;
-  }
-  constexpr FieldMeta nullable(bool v = true) const {
-    auto c = *this;
-    c.nullable_ = v;
-    return c;
-  }
-  constexpr FieldMeta ref(bool v = true) const {
-    auto c = *this;
-    c.ref_ = v;
-    return c;
-  }
-  /// Set dynamic type dispatch: true = write type info, false = skip type info
-  constexpr FieldMeta dynamic(bool v) const {
-    auto c = *this;
-    c.dynamic_ = v ? 1 : 0;
-    return c;
-  }
-  constexpr FieldMeta encoding(Encoding v) const {
-    auto c = *this;
-    c.encoding_ = v;
-    return c;
-  }
-  constexpr FieldMeta compress(bool v) const {
-    auto c = *this;
-    c.compress_ = v;
-    return c;
-  }
-  constexpr FieldMeta type_id(TypeId v) const {
-    auto c = *this;
-    c.type_id_override_ = static_cast<int16_t>(v);
-    return c;
-  }
-  constexpr FieldMeta int8_array() const { return type_id(TypeId::INT8_ARRAY); }
-  constexpr FieldMeta uint8_array() const {
-    return type_id(TypeId::UINT8_ARRAY);
-  }
-
-  // Convenience shortcuts for common encodings
-  constexpr FieldMeta varint() const { return encoding(Encoding::Varint); }
-  constexpr FieldMeta fixed() const { return encoding(Encoding::Fixed); }
-  constexpr FieldMeta tagged() const { return encoding(Encoding::Tagged); }
-};
-
-/// Short factory functions for FieldMeta - use F() as a builder or F(id) for
-/// tag
-constexpr FieldMeta F() { return FieldMeta{}; }
-constexpr FieldMeta F(int16_t id) { return FieldMeta{}.id(id); }
-
-namespace detail {
-
-// ============================================================================
-// Config Normalization - Handle both integer IDs and FieldMeta
-// ============================================================================
-
-/// Normalize configuration: convert integer to FieldMeta, pass FieldMeta
-/// through
-template <typename T> constexpr auto normalize_config(T &&v) {
-  if constexpr (std::is_integral_v<std::decay_t<T>>) {
-    // Old syntax: just an integer ID
-    return FieldMeta{}.id(static_cast<int16_t>(v));
-  } else if constexpr (std::is_same_v<std::decay_t<T>, FieldMeta>) {
-    // New syntax: already a FieldMeta
-    return v;
-  } else {
-    static_assert(
-        std::is_integral_v<std::decay_t<T>> ||
-            std::is_same_v<std::decay_t<T>, FieldMeta>,
-        "Field config must be an integer ID or FieldMeta (use F(id)...)");
-    return FieldMeta{};
-  }
-}
-
-/// Apply tag to FieldMeta
-constexpr FieldMeta apply_tag(FieldMeta m, nullable) { return m.nullable(); }
-constexpr FieldMeta apply_tag(FieldMeta m, not_null) {
-  return m.nullable(false);
-}
-constexpr FieldMeta apply_tag(FieldMeta m, ref) { return m.ref(); }
-template <bool V> constexpr FieldMeta apply_tag(FieldMeta m, dynamic<V>) {
-  return m.dynamic(V);
-}
-
-/// Fold multiple tags onto a base config
-template <typename... Tags>
-constexpr FieldMeta apply_tags(FieldMeta base, Tags... tags) {
-  ((base = apply_tag(base, tags)), ...);
-  return base;
-}
-
-// ============================================================================
-// FieldEntry - Stores Field Configuration Metadata
-// ============================================================================
-
-/// Field entry that stores name and configuration metadata
-struct FieldEntry {
-  const char *name; // Field name for debugging
-  FieldMeta meta;   // Field configuration
-
-  constexpr FieldEntry(const char *n, FieldMeta m) : name(n), meta(m) {}
-};
-
-/// Create a FieldEntry
-constexpr auto make_field_entry(const char *name, FieldMeta meta) {
-  return FieldEntry{name, meta};
-}
-
-/// Default: no field config defined for type T
-template <typename T> struct ForyFieldConfigImpl {
-  static constexpr bool has_config = false;
-};
-
-template <typename T>
-using AdlFieldConfigDescriptor =
-    decltype(fory_field_config(std::declval<meta::Identity<T>>()));
-
 template <typename T, typename = void>
 struct HasAdlFieldConfig : std::false_type {};
 
@@ -417,35 +95,36 @@ template <typename T, typename Enable = void> struct FieldConfigInfo {
 };
 
 template <typename T>
-struct FieldConfigInfo<T, std::enable_if_t<HasAdlFieldConfig<T>::value>> {
-  using Descriptor = AdlFieldConfigDescriptor<T>;
+struct FieldConfigInfo<T, std::enable_if_t<HasMemberFieldConfig<T>::value>> {
+  using Descriptor = MemberFieldConfigDescriptor<T>;
   static constexpr bool has_config = Descriptor::has_config;
   static constexpr size_t field_count = Descriptor::field_count;
   static inline constexpr auto entries = Descriptor::entries;
 };
 
 template <typename T>
-struct FieldConfigInfo<T,
-                       std::enable_if_t<!HasAdlFieldConfig<T>::value &&
-                                        ForyFieldConfigImpl<T>::has_config>> {
-  static constexpr bool has_config = true;
-  static constexpr size_t field_count = ForyFieldConfigImpl<T>::field_count;
-  static inline constexpr auto entries = ForyFieldConfigImpl<T>::entries;
+struct FieldConfigInfo<T, std::enable_if_t<!HasMemberFieldConfig<T>::value &&
+                                           HasAdlFieldConfig<T>::value>> {
+  using Descriptor = AdlFieldConfigDescriptor<T>;
+  static constexpr bool has_config = Descriptor::has_config;
+  static constexpr size_t field_count = Descriptor::field_count;
+  static inline constexpr auto entries = Descriptor::entries;
 };
 
 template <typename T>
 inline constexpr bool has_field_config_v = FieldConfigInfo<T>::has_config;
 
-/// Helper to get field encoding from FieldConfigInfo
 template <typename T, size_t Index, typename = void>
 struct GetFieldConfigEntry {
   static constexpr Encoding encoding = Encoding::Default;
   static constexpr int16_t id = -1;
   static constexpr bool nullable = false;
   static constexpr bool ref = false;
-  static constexpr int dynamic_value = -1; // AUTO
+  static constexpr int dynamic_value = -1;
   static constexpr bool compress = true;
   static constexpr int16_t type_id_override = -1;
+  static constexpr bool has_id = false;
+  static constexpr FieldNodeSpec spec = FieldNodeSpec{};
   static constexpr bool has_entry = false;
 };
 
@@ -478,1450 +157,45 @@ public:
   static constexpr int dynamic_value = entry.meta.dynamic_;
   static constexpr bool compress = entry.meta.compress_;
   static constexpr int16_t type_id_override = entry.meta.type_id_override_;
+  static constexpr bool has_id = entry.meta.has_id_;
+  static constexpr FieldNodeSpec spec = entry.meta.spec_;
   static constexpr bool has_entry = entry.name[0] != '\0';
 };
 
 } // namespace detail
 
-// ============================================================================
-// fory::field<T, Id, Options...> Template
-// ============================================================================
-
-/// Field wrapper template that provides compile-time field metadata.
-///
-/// Usage:
-///   struct Person {
-///     fory::field<std::string, 0> name;                    // non-nullable
-///     fory::field<int32_t, 1> age;                         // non-nullable
-///     fory::field<std::optional<std::string>, 2> nickname; // inherently
-///     nullable fory::field<std::shared_ptr<Person>, 3> parent;        //
-///     non-nullable fory::field<std::shared_ptr<Person>, 4, fory::nullable>
-///     guardian; fory::field<std::shared_ptr<Node>, 5, fory::ref> node;
-///     fory::field<std::shared_ptr<Node>, 6, fory::nullable, fory::ref> link;
-///   };
-///
-/// Template Parameters:
-///   T       - The underlying field type
-///   Id      - The field tag ID (int16_t) for compact serialization
-///   Options - Optional tags: fory::nullable, fory::ref
-///
-/// Type Rules:
-///   - 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 {
-  // Validate: nullable and not_null are mutually exclusive
-  static_assert(!(detail::has_option_v<nullable, Options...> &&
-                  detail::has_option_v<not_null, Options...>),
-                "fory::nullable and fory::not_null are mutually exclusive.");
-
-  // 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/SharedWeak/"
-                "unique_ptr. "
-                "Use std::optional<T> for nullable primitives/strings.");
-
-  // Validate: not_null only for smart pointers (for now)
-  static_assert(!detail::has_option_v<not_null, Options...> ||
-                    detail::is_smart_ptr_v<T>,
-                "fory::not_null is only valid for pointer types.");
-
-  // Validate: ref only for shared_ptr/SharedWeak
-  static_assert(!detail::has_option_v<ref, Options...> ||
-                    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/SharedWeak/"
-                "unique_ptr.");
-
-  // Validate: no options for optional (inherently nullable)
-  static_assert(!detail::is_optional_v<T> || sizeof...(Options) == 0,
-                "std::optional<T> is inherently nullable. No options allowed.");
-
-  // 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/SharedWeak/unique_ptr "
-                "fields. "
-                "Use std::optional<T> for nullable primitives/strings.");
-
-public:
-  using value_type = T;
-  static constexpr int16_t tag_id = Id;
-
-  /// Field is nullable if:
-  /// - It's std::optional (inherently nullable), OR
-  /// - It's a smart pointer with fory::nullable option
-  static constexpr bool is_nullable =
-      detail::is_optional_v<T> ||
-      (detail::is_smart_ptr_v<T> && detail::has_option_v<nullable, Options...>);
-
-  /// Reference tracking is enabled if:
-  /// - 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
-  /// - 0 (FALSE): skip type info, use declared type directly
-  /// - 1 (TRUE): write type info, enable runtime subtype support
-  static constexpr int dynamic_value = detail::get_dynamic_value_v<Options...>;
-
-  T value{};
-
-  // Default constructor
-  field() = default;
-
-  // Value constructors
-  field(const T &v) : value(v) {}
-  field(T &&v) : value(std::move(v)) {}
-
-  // copy and move constructors
-  field(const field &) = default;
-  field(field &&) = default;
-
-  // copy and move assignment
-  field &operator=(const field &) = default;
-  field &operator=(field &&) = default;
-
-  // Value assignment
-  field &operator=(const T &v) {
-    value = v;
-    return *this;
-  }
-  field &operator=(T &&v) {
-    value = std::move(v);
-    return *this;
-  }
-
-  // Implicit conversions to underlying type
-  operator T &() { return value; }
-  operator const T &() const { return value; }
-
-  // Pointer-like access for smart pointers
-  T *operator->() { return &value; }
-  const T *operator->() const { return &value; }
-
-  // Dereference operators
-  T &operator*() { return value; }
-  const T &operator*() const { return value; }
-
-  // get underlying value
-  T &get() { return value; }
-  const T &get() const { return value; }
-};
-
-// ============================================================================
-// Type Traits for fory::field Detection
-// ============================================================================
-
-/// Check if a type is a fory::field wrapper
 template <typename T> struct is_fory_field : std::false_type {};
-
-template <typename T, int16_t Id, typename... Options>
-struct is_fory_field<field<T, Id, Options...>> : std::true_type {};
-
 template <typename T>
 inline constexpr bool is_fory_field_v = is_fory_field<T>::value;
 
-/// unwrap fory::field to get the underlying type
 template <typename T> struct unwrap_field {
   using type = T;
 };
-
-template <typename T, int16_t Id, typename... Options>
-struct unwrap_field<field<T, Id, Options...>> {
-  using type = T;
-};
-
 template <typename T> using unwrap_field_t = typename unwrap_field<T>::type;
 
-/// get tag ID from field type (returns -1 if not a fory::field)
 template <typename T> struct field_tag_id {
   static constexpr int16_t value = -1;
 };
-
-template <typename T, int16_t Id, typename... Options>
-struct field_tag_id<field<T, Id, Options...>> {
-  static constexpr int16_t value = Id;
-};
-
 template <typename T>
 inline constexpr int16_t field_tag_id_v = field_tag_id<T>::value;
 
-/// Determines whether a field is nullable and requires a RefFlag byte.
-///
-/// This mirrors Rust's `field_need_write_ref_into(type_id, nullable)` in
-/// rust/fory-core/src/serializer/util.rs and determines whether the writer
-/// emits a `RefFlag` byte before the field's value payload.
-///
-/// Per the xlang protocol:
-/// - Non-nullable types (nullable=false) skip the ref flag entirely
-/// - Nullable types (nullable=true) write a ref flag to indicate null vs
-///   non-null
-///
-/// For non-field types, std::optional is considered nullable.
-/// For fory::field types, uses the explicit nullable option if provided.
 template <typename T> struct field_is_nullable {
   static constexpr bool value = detail::is_optional_v<T>;
 };
-
-template <typename T, int16_t Id, typename... Options>
-struct field_is_nullable<field<T, Id, Options...>> {
-  static constexpr bool value = field<T, Id, Options...>::is_nullable;
-};
-
 template <typename T>
 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 =
       detail::is_shared_ptr_v<T> || detail::is_shared_weak_v<T>;
 };
-
-template <typename T, int16_t Id, typename... Options>
-struct field_track_ref<field<T, Id, Options...>> {
-  static constexpr bool value = field<T, Id, Options...>::track_ref;
-};
-
 template <typename T>
 inline constexpr bool field_track_ref_v = field_track_ref<T>::value;
 
-/// get dynamic_value from field type (-1 = AUTO, 0 = FALSE, 1 = TRUE)
 template <typename T> struct field_dynamic_value {
-  static constexpr int value = -1; // AUTO
+  static constexpr int value = -1;
 };
-
-template <typename T, int16_t Id, typename... Options>
-struct field_dynamic_value<field<T, Id, Options...>> {
-  static constexpr int value = field<T, Id, Options...>::dynamic_value;
-};
-
 template <typename T>
 inline constexpr int field_dynamic_value_v = field_dynamic_value<T>::value;
 
-// ============================================================================
-// FORY_FIELD_TAGS Macro Support
-// ============================================================================
-
-namespace detail {
-
-// Helper to parse field tag entry from macro arguments
-// Supports: (field, id), (field, id, nullable), (field, id, ref),
-//           (field, id, nullable, ref), (field, id, dynamic<false>), etc.
-template <typename FieldType, int16_t Id, typename... Options>
-struct ParseFieldTagEntry {
-  static constexpr bool is_nullable =
-      is_optional_v<FieldType> ||
-      (is_smart_ptr_v<FieldType> && has_option_v<nullable, 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/SharedWeak/"
-                "unique_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/SharedWeak/"
-                "unique_ptr");
-
-  using type = FieldTagEntry<Id, is_nullable, track_ref, dynamic_value>;
-};
-
-/// get field tag entry by index from FieldTagsInfo
-template <typename T, size_t Index, typename = void> struct GetFieldTagEntry {
-  static constexpr int16_t id = -1;
-  static constexpr bool is_nullable = false;
-  static constexpr bool track_ref = false;
-  static constexpr int dynamic_value = -1; // AUTO
-  static constexpr bool has_entry = false;
-};
-
-template <typename T, size_t Index>
-struct GetFieldTagEntry<
-    T, Index,
-    std::enable_if_t<FieldTagsInfo<T>::has_tags &&
-                     (Index < FieldTagsInfo<T>::field_count) &&
-                     FieldTagsInfo<T>::use_index>> {
-  using Entry = std::tuple_element_t<Index, typename FieldTagsInfo<T>::Entries>;
-  static constexpr int16_t id = Entry::id;
-  static constexpr bool is_nullable = Entry::is_nullable;
-  static constexpr bool track_ref = Entry::track_ref;
-  static constexpr int dynamic_value = Entry::dynamic_value;
-  static constexpr bool has_entry = true;
-};
-
-template <typename T, size_t Index>
-struct GetFieldTagEntry<T, Index,
-                        std::enable_if_t<FieldTagsInfo<T>::has_tags &&
-                                         !FieldTagsInfo<T>::use_index>> {
-private:
-  static constexpr std::string_view field_name = FieldInfo<T>::Names[Index];
-
-  template <size_t I = 0> static constexpr FieldTagEntryWithName find_entry() {
-    if constexpr (I >= std::tuple_size_v<typename FieldTagsInfo<T>::Entries>) {
-      return FieldTagEntryWithName{"", -1, false, false, -1};
-    } else {
-      constexpr auto entry = std::get<I>(FieldTagsInfo<T>::entries);
-      if (std::string_view{entry.name} == field_name) {
-        return entry;
-      }
-      return find_entry<I + 1>();
-    }
-  }
-
-  static constexpr FieldTagEntryWithName entry = find_entry<>();
-
-public:
-  static constexpr int16_t id = entry.id;
-  static constexpr bool is_nullable = entry.is_nullable;
-  static constexpr bool track_ref = entry.track_ref;
-  static constexpr int dynamic_value = entry.dynamic_value;
-  static constexpr bool has_entry = entry.name[0] != '\0';
-};
-
-} // namespace detail
-
 } // namespace fory
-
-// ============================================================================
-// FORY_FIELD_TAGS Macro Implementation
-// ============================================================================
-
-// Helper macros to extract parts from (field, id, ...) tuples
-#define FORY_FT_FIELD(tuple) FORY_FT_FIELD_IMPL tuple
-#define FORY_FT_FIELD_IMPL(field, ...) field
-
-// Stringify field name
-#define FORY_FT_STRINGIFY(x) FORY_FT_STRINGIFY_I(x)
-#define FORY_FT_STRINGIFY_I(x) #x
-
-#define FORY_FT_ID(tuple) FORY_FT_ID_IMPL tuple
-#define FORY_FT_ID_IMPL(field, id, ...) id
-
-// get options from tuple
-#define FORY_FT_GET_OPT1(tuple) FORY_FT_GET_OPT1_IMPL tuple
-#define FORY_FT_GET_OPT1_IMPL(f, i, o1, ...) o1
-#define FORY_FT_GET_OPT2(tuple) FORY_FT_GET_OPT2_IMPL tuple
-#define FORY_FT_GET_OPT2_IMPL(f, i, o1, o2, ...) o2
-#define FORY_FT_GET_OPT3(tuple) FORY_FT_GET_OPT3_IMPL tuple
-#define FORY_FT_GET_OPT3_IMPL(f, i, o1, o2, o3, ...) o3
-
-// Detect number of elements in tuple: 2, 3, 4, or 5
-#define FORY_FT_TUPLE_SIZE(tuple) FORY_FT_TUPLE_SIZE_IMPL tuple
-#define FORY_FT_TUPLE_SIZE_IMPL(...)                                           \
-  FORY_FT_TUPLE_SIZE_SELECT(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
-#define FORY_FT_TUPLE_SIZE_SELECT(_1, _2, _3, _4, _5, N, ...) N
-
-// Create FieldTagEntry based on tuple size using indirect call pattern
-// This pattern ensures the concatenated macro name is properly rescanned
-#define FORY_FT_MAKE_ENTRY(Type, tuple)                                        \
-  FORY_FT_MAKE_ENTRY_I(Type, tuple, FORY_FT_TUPLE_SIZE(tuple))
-#define FORY_FT_MAKE_ENTRY_I(Type, tuple, size)                                \
-  FORY_FT_MAKE_ENTRY_II(Type, tuple, size)
-#define FORY_FT_MAKE_ENTRY_II(Type, tuple, size)                               \
-  FORY_FT_MAKE_ENTRY_##size(Type, tuple)
-
-#define FORY_FT_FIELD_INDEX(Type, tuple)                                       \
-  ::fory::detail::field_index<Type>(                                           \
-      std::string_view{FORY_FT_STRINGIFY(FORY_FT_FIELD(tuple))})
-
-#define FORY_FT_FIELD_TYPE(Type, tuple)                                        \
-  typename ::fory::detail::FieldTypeAt<Type,                                   \
-                                       FORY_FT_FIELD_INDEX(Type, tuple)>::type
-
-#define FORY_FT_MAKE_ENTRY_2(Type, tuple)                                      \
-  ::fory::detail::make_field_tag_entry<                                        \
-      typename ::fory::detail::ParseFieldTagEntry<                             \
-          FORY_FT_FIELD_TYPE(Type, tuple), FORY_FT_ID(tuple)>::type>(          \
-      FORY_FT_STRINGIFY(FORY_FT_FIELD(tuple)))
-
-#define FORY_FT_MAKE_ENTRY_3(Type, tuple)                                      \
-  ::fory::detail::make_field_tag_entry<                                        \
-      typename ::fory::detail::ParseFieldTagEntry<                             \
-          FORY_FT_FIELD_TYPE(Type, tuple), FORY_FT_ID(tuple),                  \
-          ::fory::FORY_FT_GET_OPT1(tuple)>::type>(                             \
-      FORY_FT_STRINGIFY(FORY_FT_FIELD(tuple)))
-
-#define FORY_FT_MAKE_ENTRY_4(Type, tuple)                                      \
-  ::fory::detail::make_field_tag_entry<                                        \
-      typename ::fory::detail::ParseFieldTagEntry<                             \
-          FORY_FT_FIELD_TYPE(Type, tuple), FORY_FT_ID(tuple),                  \
-          ::fory::FORY_FT_GET_OPT1(tuple),                                     \
-          ::fory::FORY_FT_GET_OPT2(tuple)>::type>(                             \
-      FORY_FT_STRINGIFY(FORY_FT_FIELD(tuple)))
-
-#define FORY_FT_MAKE_ENTRY_5(Type, tuple)                                      \
-  ::fory::detail::make_field_tag_entry<                                        \
-      typename ::fory::detail::ParseFieldTagEntry<                             \
-          FORY_FT_FIELD_TYPE(Type, tuple), FORY_FT_ID(tuple),                  \
-          ::fory::FORY_FT_GET_OPT1(tuple), ::fory::FORY_FT_GET_OPT2(tuple),    \
-          ::fory::FORY_FT_GET_OPT3(tuple)>::type>(                             \
-      FORY_FT_STRINGIFY(FORY_FT_FIELD(tuple)))
-
-// Main macro: FORY_FIELD_TAGS(Type, (field1, id1), (field2, id2, nullable),...)
-#define FORY_FT_DESCRIPTOR_NAME(line)                                          \
-  FORY_PP_CONCAT(ForyFieldTagsDescriptor_, line)
-#define FORY_FIELD_TAGS(Type, ...)                                             \
-  FORY_FIELD_TAGS_IMPL(__LINE__, Type, __VA_ARGS__)
-#define FORY_FIELD_TAGS_IMPL(line, Type, ...)                                  \
-  struct FORY_FT_DESCRIPTOR_NAME(line) {                                       \
-    static constexpr bool has_tags = true;                                     \
-    static inline constexpr auto entries =                                     \
-        std::make_tuple(FORY_FT_ENTRIES(Type, __VA_ARGS__));                   \
-    using Entries = std::decay_t<decltype(entries)>;                           \
-    static constexpr size_t field_count = std::tuple_size_v<Entries>;          \
-  };                                                                           \
-  constexpr auto fory_field_tags(::fory::meta::Identity<Type>) {               \
-    return FORY_FT_DESCRIPTOR_NAME(line){};                                    \
-  }                                                                            \
-  static_assert(true)
-
-// Helper to generate entries tuple content using indirect expansion pattern
-// This ensures FORY_PP_NARG is fully expanded before concatenation
-#define FORY_FT_ENTRIES(Type, ...)                                             \
-  FORY_FT_ENTRIES_I(Type, FORY_PP_NARG(__VA_ARGS__), __VA_ARGS__)
-#define FORY_FT_ENTRIES_I(Type, N, ...) FORY_FT_ENTRIES_II(Type, N, __VA_ARGS__)
-#define FORY_FT_ENTRIES_II(Type, N, ...) FORY_FT_ENTRIES_##N(Type, __VA_ARGS__)
-
-// Generate entries for 1-64 fields
-#define FORY_FT_ENTRIES_1(T, _1) FORY_FT_MAKE_ENTRY(T, _1)
-#define FORY_FT_ENTRIES_2(T, _1, _2)                                           \
-  FORY_FT_MAKE_ENTRY(T, _1), FORY_FT_MAKE_ENTRY(T, _2)
-#define FORY_FT_ENTRIES_3(T, _1, _2, _3)                                       \
-  FORY_FT_ENTRIES_2(T, _1, _2), FORY_FT_MAKE_ENTRY(T, _3)
-#define FORY_FT_ENTRIES_4(T, _1, _2, _3, _4)                                   \
-  FORY_FT_ENTRIES_3(T, _1, _2, _3), FORY_FT_MAKE_ENTRY(T, _4)
-#define FORY_FT_ENTRIES_5(T, _1, _2, _3, _4, _5)                               \
-  FORY_FT_ENTRIES_4(T, _1, _2, _3, _4), FORY_FT_MAKE_ENTRY(T, _5)
-#define FORY_FT_ENTRIES_6(T, _1, _2, _3, _4, _5, _6)                           \
-  FORY_FT_ENTRIES_5(T, _1, _2, _3, _4, _5), FORY_FT_MAKE_ENTRY(T, _6)
-#define FORY_FT_ENTRIES_7(T, _1, _2, _3, _4, _5, _6, _7)                       \
-  FORY_FT_ENTRIES_6(T, _1, _2, _3, _4, _5, _6), FORY_FT_MAKE_ENTRY(T, _7)
-#define FORY_FT_ENTRIES_8(T, _1, _2, _3, _4, _5, _6, _7, _8)                   \
-  FORY_FT_ENTRIES_7(T, _1, _2, _3, _4, _5, _6, _7), FORY_FT_MAKE_ENTRY(T, _8)
-#define FORY_FT_ENTRIES_9(T, _1, _2, _3, _4, _5, _6, _7, _8, _9)               \
-  FORY_FT_ENTRIES_8(T, _1, _2, _3, _4, _5, _6, _7, _8),                        \
-      FORY_FT_MAKE_ENTRY(T, _9)
-#define FORY_FT_ENTRIES_10(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10)         \
-  FORY_FT_ENTRIES_9(T, _1, _2, _3, _4, _5, _6, _7, _8, _9),                    \
-      FORY_FT_MAKE_ENTRY(T, _10)
-#define FORY_FT_ENTRIES_11(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11)    \
-  FORY_FT_ENTRIES_10(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10),              \
-      FORY_FT_MAKE_ENTRY(T, _11)
-#define FORY_FT_ENTRIES_12(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12)                                                \
-  FORY_FT_ENTRIES_11(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11),         \
-      FORY_FT_MAKE_ENTRY(T, _12)
-#define FORY_FT_ENTRIES_13(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13)                                           \
-  FORY_FT_ENTRIES_12(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12),    \
-      FORY_FT_MAKE_ENTRY(T, _13)
-#define FORY_FT_ENTRIES_14(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14)                                      \
-  FORY_FT_ENTRIES_13(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13),                                                     \
-      FORY_FT_MAKE_ENTRY(T, _14)
-#define FORY_FT_ENTRIES_15(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15)                                 \
-  FORY_FT_ENTRIES_14(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14),                                                \
-      FORY_FT_MAKE_ENTRY(T, _15)
-#define FORY_FT_ENTRIES_16(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16)                            \
-  FORY_FT_ENTRIES_15(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15),                                           \
-      FORY_FT_MAKE_ENTRY(T, _16)
-#define FORY_FT_ENTRIES_17(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17)                       \
-  FORY_FT_ENTRIES_16(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16),                                      \
-      FORY_FT_MAKE_ENTRY(T, _17)
-#define FORY_FT_ENTRIES_18(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18)                  \
-  FORY_FT_ENTRIES_17(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17),                                 \
-      FORY_FT_MAKE_ENTRY(T, _18)
-#define FORY_FT_ENTRIES_19(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19)             \
-  FORY_FT_ENTRIES_18(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18),                            \
-      FORY_FT_MAKE_ENTRY(T, _19)
-#define FORY_FT_ENTRIES_20(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20)        \
-  FORY_FT_ENTRIES_19(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19),                       \
-      FORY_FT_MAKE_ENTRY(T, _20)
-#define FORY_FT_ENTRIES_21(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21)   \
-  FORY_FT_ENTRIES_20(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20),                  \
-      FORY_FT_MAKE_ENTRY(T, _21)
-#define FORY_FT_ENTRIES_22(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22)                                                \
-  FORY_FT_ENTRIES_21(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21),             \
-      FORY_FT_MAKE_ENTRY(T, _22)
-#define FORY_FT_ENTRIES_23(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23)                                           \
-  FORY_FT_ENTRIES_22(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22),        \
-      FORY_FT_MAKE_ENTRY(T, _23)
-#define FORY_FT_ENTRIES_24(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24)                                      \
-  FORY_FT_ENTRIES_23(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23),   \
-      FORY_FT_MAKE_ENTRY(T, _24)
-#define FORY_FT_ENTRIES_25(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25)                                 \
-  FORY_FT_ENTRIES_24(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24),                                                     \
-      FORY_FT_MAKE_ENTRY(T, _25)
-#define FORY_FT_ENTRIES_26(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26)                            \
-  FORY_FT_ENTRIES_25(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25),                                                \
-      FORY_FT_MAKE_ENTRY(T, _26)
-#define FORY_FT_ENTRIES_27(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27)                       \
-  FORY_FT_ENTRIES_26(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26),                                           \
-      FORY_FT_MAKE_ENTRY(T, _27)
-#define FORY_FT_ENTRIES_28(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28)                  \
-  FORY_FT_ENTRIES_27(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27),                                      \
-      FORY_FT_MAKE_ENTRY(T, _28)
-#define FORY_FT_ENTRIES_29(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29)             \
-  FORY_FT_ENTRIES_28(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28),                                 \
-      FORY_FT_MAKE_ENTRY(T, _29)
-#define FORY_FT_ENTRIES_30(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30)        \
-  FORY_FT_ENTRIES_29(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29),                            \
-      FORY_FT_MAKE_ENTRY(T, _30)
-#define FORY_FT_ENTRIES_31(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31)   \
-  FORY_FT_ENTRIES_30(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30),                       \
-      FORY_FT_MAKE_ENTRY(T, _31)
-#define FORY_FT_ENTRIES_32(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32)                                                \
-  FORY_FT_ENTRIES_31(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31),                  \
-      FORY_FT_MAKE_ENTRY(T, _32)
-#define FORY_FT_ENTRIES_33(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33)                                           \
-  FORY_FT_ENTRIES_32(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32),             \
-      FORY_FT_MAKE_ENTRY(T, _33)
-#define FORY_FT_ENTRIES_34(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34)                                      \
-  FORY_FT_ENTRIES_33(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33),        \
-      FORY_FT_MAKE_ENTRY(T, _34)
-#define FORY_FT_ENTRIES_35(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35)                                 \
-  FORY_FT_ENTRIES_34(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34),   \
-      FORY_FT_MAKE_ENTRY(T, _35)
-#define FORY_FT_ENTRIES_36(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36)                            \
-  FORY_FT_ENTRIES_35(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35),                                                     \
-      FORY_FT_MAKE_ENTRY(T, _36)
-#define FORY_FT_ENTRIES_37(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37)                       \
-  FORY_FT_ENTRIES_36(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36),                                                \
-      FORY_FT_MAKE_ENTRY(T, _37)
-#define FORY_FT_ENTRIES_38(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38)                  \
-  FORY_FT_ENTRIES_37(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37),                                           \
-      FORY_FT_MAKE_ENTRY(T, _38)
-#define FORY_FT_ENTRIES_39(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39)             \
-  FORY_FT_ENTRIES_38(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38),                                      \
-      FORY_FT_MAKE_ENTRY(T, _39)
-#define FORY_FT_ENTRIES_40(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40)        \
-  FORY_FT_ENTRIES_39(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39),                                 \
-      FORY_FT_MAKE_ENTRY(T, _40)
-#define FORY_FT_ENTRIES_41(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41)   \
-  FORY_FT_ENTRIES_40(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40),                            \
-      FORY_FT_MAKE_ENTRY(T, _41)
-#define FORY_FT_ENTRIES_42(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42)                     \
-  FORY_FT_ENTRIES_41(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41),                       \
-      FORY_FT_MAKE_ENTRY(T, _42)
-#define FORY_FT_ENTRIES_43(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43)                \
-  FORY_FT_ENTRIES_42(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42),                  \
-      FORY_FT_MAKE_ENTRY(T, _43)
-#define FORY_FT_ENTRIES_44(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44)           \
-  FORY_FT_ENTRIES_43(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43),             \
-      FORY_FT_MAKE_ENTRY(T, _44)
-#define FORY_FT_ENTRIES_45(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45)      \
-  FORY_FT_ENTRIES_44(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44),        \
-      FORY_FT_MAKE_ENTRY(T, _45)
-#define FORY_FT_ENTRIES_46(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46) \
-  FORY_FT_ENTRIES_45(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45),   \
-      FORY_FT_MAKE_ENTRY(T, _46)
-#define FORY_FT_ENTRIES_47(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41,   \
-                           _42, _43, _44, _45, _46, _47)                       \
-  FORY_FT_ENTRIES_46(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46),                                                     \
-      FORY_FT_MAKE_ENTRY(T, _47)
-#define FORY_FT_ENTRIES_48(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41,   \
-                           _42, _43, _44, _45, _46, _47, _48)                  \
-  FORY_FT_ENTRIES_47(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47),                                                \
-      FORY_FT_MAKE_ENTRY(T, _48)
-#define FORY_FT_ENTRIES_49(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41,   \
-                           _42, _43, _44, _45, _46, _47, _48, _49)             \
-  FORY_FT_ENTRIES_48(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48),                                           \
-      FORY_FT_MAKE_ENTRY(T, _49)
-#define FORY_FT_ENTRIES_50(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41,   \
-                           _42, _43, _44, _45, _46, _47, _48, _49, _50)        \
-  FORY_FT_ENTRIES_49(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49),                                      \
-      FORY_FT_MAKE_ENTRY(T, _50)
-#define FORY_FT_ENTRIES_51(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41,   \
-                           _42, _43, _44, _45, _46, _47, _48, _49, _50, _51)   \
-  FORY_FT_ENTRIES_50(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50),                                 \
-      FORY_FT_MAKE_ENTRY(T, _51)
-#define FORY_FT_ENTRIES_52(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52)                                              \
-  FORY_FT_ENTRIES_51(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51),                            \
-      FORY_FT_MAKE_ENTRY(T, _52)
-#define FORY_FT_ENTRIES_53(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53)                                         \
-  FORY_FT_ENTRIES_52(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52),                       \
-      FORY_FT_MAKE_ENTRY(T, _53)
-#define FORY_FT_ENTRIES_54(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54)                                    \
-  FORY_FT_ENTRIES_53(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53),                  \
-      FORY_FT_MAKE_ENTRY(T, _54)
-#define FORY_FT_ENTRIES_55(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55)                               \
-  FORY_FT_ENTRIES_54(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54),             \
-      FORY_FT_MAKE_ENTRY(T, _55)
-#define FORY_FT_ENTRIES_56(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56)                          \
-  FORY_FT_ENTRIES_55(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55),        \
-      FORY_FT_MAKE_ENTRY(T, _56)
-#define FORY_FT_ENTRIES_57(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57)                     \
-  FORY_FT_ENTRIES_56(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56),   \
-      FORY_FT_MAKE_ENTRY(T, _57)
-#define FORY_FT_ENTRIES_58(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58)                \
-  FORY_FT_ENTRIES_57(                                                          \
-      T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15,     \
-      _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29,    \
-      _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43,    \
-      _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57),   \
-      FORY_FT_MAKE_ENTRY(T, _58)
-#define FORY_FT_ENTRIES_59(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59)           \
-  FORY_FT_ENTRIES_58(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58),                                                \
-      FORY_FT_MAKE_ENTRY(T, _59)
-#define FORY_FT_ENTRIES_60(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60)      \
-  FORY_FT_ENTRIES_59(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58, _59),                                           \
-      FORY_FT_MAKE_ENTRY(T, _60)
-#define FORY_FT_ENTRIES_61(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61) \
-  FORY_FT_ENTRIES_60(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58, _59, _60),                                      \
-      FORY_FT_MAKE_ENTRY(T, _61)
-#define FORY_FT_ENTRIES_62(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, \
-    _62)                                                                       \
-  FORY_FT_ENTRIES_61(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58, _59, _60, _61),                                 \
-      FORY_FT_MAKE_ENTRY(T, _62)
-#define FORY_FT_ENTRIES_63(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, \
-    _62, _63)                                                                  \
-  FORY_FT_ENTRIES_62(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58, _59, _60, _61, _62),                            \
-      FORY_FT_MAKE_ENTRY(T, _63)
-#define FORY_FT_ENTRIES_64(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, \
-    _62, _63, _64)                                                             \
-  FORY_FT_ENTRIES_63(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58, _59, _60, _61, _62, _63),                       \
-      FORY_FT_MAKE_ENTRY(T, _64)
-
-// ============================================================================
-// FORY_FIELD_CONFIG Macro - New Syntax with Member Pointer Verification
-// ============================================================================
-//
-// Usage:
-//   FORY_FIELD_CONFIG(MyStruct,
-//       (field1, fory::F(0)),                  // Simple: just ID
-//       (field2, fory::F(1).nullable()),       // With nullable
-//       (field3, fory::F(2).varint()),         // With encoding
-//       (field4, fory::F(3).nullable().ref()), // Multiple options
-//       (field5, 4)                            // Backward compatible: integer
-//       ID
-//   );
-//
-// This macro:
-// 1. Verifies field names exist at compile time via member pointers
-// 2. Supports both integer IDs (old) and F(id).xxx() builder (new)
-// 3. Stores configuration in a constexpr tuple for efficient access
-
-// Helper to stringify field name
-#define FORY_FC_STRINGIFY(x) FORY_FC_STRINGIFY_I(x)
-#define FORY_FC_STRINGIFY_I(x) #x
-
-// Extract field name (first element of tuple)
-#define FORY_FC_NAME(tuple) FORY_FC_NAME_IMPL tuple
-#define FORY_FC_NAME_IMPL(name, ...) name
-
-// Extract config (second element of tuple)
-#define FORY_FC_CONFIG(tuple) FORY_FC_CONFIG_IMPL tuple
-#define FORY_FC_CONFIG_IMPL(name, config, ...) config
-
-// Create a FieldEntry with member pointer verification
-#define FORY_FC_MAKE_ENTRY(Type, tuple)                                        \
-  ::fory::detail::make_field_entry(                                            \
-      FORY_FC_STRINGIFY(FORY_FC_NAME(tuple)),                                  \
-      ::fory::detail::normalize_config(FORY_FC_CONFIG(tuple)))
-
-// Generate entries using indirect expansion
-#define FORY_FC_ENTRIES(Type, ...)                                             \
-  FORY_FC_ENTRIES_I(Type, FORY_PP_NARG(__VA_ARGS__), __VA_ARGS__)
-#define FORY_FC_ENTRIES_I(Type, N, ...) FORY_FC_ENTRIES_II(Type, N, __VA_ARGS__)
-#define FORY_FC_ENTRIES_II(Type, N, ...) FORY_FC_ENTRIES_##N(Type, __VA_ARGS__)
-
-// Generate entries for 1-64 fields
-#define FORY_FC_ENTRIES_1(T, _1) FORY_FC_MAKE_ENTRY(T, _1)
-#define FORY_FC_ENTRIES_2(T, _1, _2)                                           \
-  FORY_FC_MAKE_ENTRY(T, _1), FORY_FC_MAKE_ENTRY(T, _2)
-#define FORY_FC_ENTRIES_3(T, _1, _2, _3)                                       \
-  FORY_FC_ENTRIES_2(T, _1, _2), FORY_FC_MAKE_ENTRY(T, _3)
-#define FORY_FC_ENTRIES_4(T, _1, _2, _3, _4)                                   \
-  FORY_FC_ENTRIES_3(T, _1, _2, _3), FORY_FC_MAKE_ENTRY(T, _4)
-#define FORY_FC_ENTRIES_5(T, _1, _2, _3, _4, _5)                               \
-  FORY_FC_ENTRIES_4(T, _1, _2, _3, _4), FORY_FC_MAKE_ENTRY(T, _5)
-#define FORY_FC_ENTRIES_6(T, _1, _2, _3, _4, _5, _6)                           \
-  FORY_FC_ENTRIES_5(T, _1, _2, _3, _4, _5), FORY_FC_MAKE_ENTRY(T, _6)
-#define FORY_FC_ENTRIES_7(T, _1, _2, _3, _4, _5, _6, _7)                       \
-  FORY_FC_ENTRIES_6(T, _1, _2, _3, _4, _5, _6), FORY_FC_MAKE_ENTRY(T, _7)
-#define FORY_FC_ENTRIES_8(T, _1, _2, _3, _4, _5, _6, _7, _8)                   \
-  FORY_FC_ENTRIES_7(T, _1, _2, _3, _4, _5, _6, _7), FORY_FC_MAKE_ENTRY(T, _8)
-#define FORY_FC_ENTRIES_9(T, _1, _2, _3, _4, _5, _6, _7, _8, _9)               \
-  FORY_FC_ENTRIES_8(T, _1, _2, _3, _4, _5, _6, _7, _8),                        \
-      FORY_FC_MAKE_ENTRY(T, _9)
-#define FORY_FC_ENTRIES_10(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10)         \
-  FORY_FC_ENTRIES_9(T, _1, _2, _3, _4, _5, _6, _7, _8, _9),                    \
-      FORY_FC_MAKE_ENTRY(T, _10)
-#define FORY_FC_ENTRIES_11(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11)    \
-  FORY_FC_ENTRIES_10(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10),              \
-      FORY_FC_MAKE_ENTRY(T, _11)
-#define FORY_FC_ENTRIES_12(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12)                                                \
-  FORY_FC_ENTRIES_11(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11),         \
-      FORY_FC_MAKE_ENTRY(T, _12)
-#define FORY_FC_ENTRIES_13(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13)                                           \
-  FORY_FC_ENTRIES_12(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12),    \
-      FORY_FC_MAKE_ENTRY(T, _13)
-#define FORY_FC_ENTRIES_14(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14)                                      \
-  FORY_FC_ENTRIES_13(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13),                                                     \
-      FORY_FC_MAKE_ENTRY(T, _14)
-#define FORY_FC_ENTRIES_15(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15)                                 \
-  FORY_FC_ENTRIES_14(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14),                                                \
-      FORY_FC_MAKE_ENTRY(T, _15)
-#define FORY_FC_ENTRIES_16(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16)                            \
-  FORY_FC_ENTRIES_15(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15),                                           \
-      FORY_FC_MAKE_ENTRY(T, _16)
-#define FORY_FC_ENTRIES_17(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17)                       \
-  FORY_FC_ENTRIES_16(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16),                                      \
-      FORY_FC_MAKE_ENTRY(T, _17)
-#define FORY_FC_ENTRIES_18(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18)                  \
-  FORY_FC_ENTRIES_17(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17),                                 \
-      FORY_FC_MAKE_ENTRY(T, _18)
-#define FORY_FC_ENTRIES_19(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19)             \
-  FORY_FC_ENTRIES_18(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18),                            \
-      FORY_FC_MAKE_ENTRY(T, _19)
-#define FORY_FC_ENTRIES_20(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20)        \
-  FORY_FC_ENTRIES_19(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19),                       \
-      FORY_FC_MAKE_ENTRY(T, _20)
-#define FORY_FC_ENTRIES_21(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21)   \
-  FORY_FC_ENTRIES_20(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20),                  \
-      FORY_FC_MAKE_ENTRY(T, _21)
-#define FORY_FC_ENTRIES_22(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22)                                                \
-  FORY_FC_ENTRIES_21(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21),             \
-      FORY_FC_MAKE_ENTRY(T, _22)
-#define FORY_FC_ENTRIES_23(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23)                                           \
-  FORY_FC_ENTRIES_22(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22),        \
-      FORY_FC_MAKE_ENTRY(T, _23)
-#define FORY_FC_ENTRIES_24(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24)                                      \
-  FORY_FC_ENTRIES_23(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23),   \
-      FORY_FC_MAKE_ENTRY(T, _24)
-#define FORY_FC_ENTRIES_25(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25)                                 \
-  FORY_FC_ENTRIES_24(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24),                                                     \
-      FORY_FC_MAKE_ENTRY(T, _25)
-#define FORY_FC_ENTRIES_26(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26)                            \
-  FORY_FC_ENTRIES_25(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25),                                                \
-      FORY_FC_MAKE_ENTRY(T, _26)
-#define FORY_FC_ENTRIES_27(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27)                       \
-  FORY_FC_ENTRIES_26(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26),                                           \
-      FORY_FC_MAKE_ENTRY(T, _27)
-#define FORY_FC_ENTRIES_28(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28)                  \
-  FORY_FC_ENTRIES_27(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27),                                      \
-      FORY_FC_MAKE_ENTRY(T, _28)
-#define FORY_FC_ENTRIES_29(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29)             \
-  FORY_FC_ENTRIES_28(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28),                                 \
-      FORY_FC_MAKE_ENTRY(T, _29)
-#define FORY_FC_ENTRIES_30(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30)        \
-  FORY_FC_ENTRIES_29(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29),                            \
-      FORY_FC_MAKE_ENTRY(T, _30)
-#define FORY_FC_ENTRIES_31(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31)   \
-  FORY_FC_ENTRIES_30(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30),                       \
-      FORY_FC_MAKE_ENTRY(T, _31)
-#define FORY_FC_ENTRIES_32(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32)                                                \
-  FORY_FC_ENTRIES_31(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31),                  \
-      FORY_FC_MAKE_ENTRY(T, _32)
-#define FORY_FC_ENTRIES_33(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33)                                           \
-  FORY_FC_ENTRIES_32(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32),             \
-      FORY_FC_MAKE_ENTRY(T, _33)
-#define FORY_FC_ENTRIES_34(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34)                                      \
-  FORY_FC_ENTRIES_33(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33),        \
-      FORY_FC_MAKE_ENTRY(T, _34)
-#define FORY_FC_ENTRIES_35(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35)                                 \
-  FORY_FC_ENTRIES_34(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34),   \
-      FORY_FC_MAKE_ENTRY(T, _35)
-#define FORY_FC_ENTRIES_36(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36)                            \
-  FORY_FC_ENTRIES_35(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35),                                                     \
-      FORY_FC_MAKE_ENTRY(T, _36)
-#define FORY_FC_ENTRIES_37(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37)                       \
-  FORY_FC_ENTRIES_36(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36),                                                \
-      FORY_FC_MAKE_ENTRY(T, _37)
-#define FORY_FC_ENTRIES_38(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38)                  \
-  FORY_FC_ENTRIES_37(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37),                                           \
-      FORY_FC_MAKE_ENTRY(T, _38)
-#define FORY_FC_ENTRIES_39(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39)             \
-  FORY_FC_ENTRIES_38(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38),                                      \
-      FORY_FC_MAKE_ENTRY(T, _39)
-#define FORY_FC_ENTRIES_40(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40)        \
-  FORY_FC_ENTRIES_39(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39),                                 \
-      FORY_FC_MAKE_ENTRY(T, _40)
-#define FORY_FC_ENTRIES_41(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41)   \
-  FORY_FC_ENTRIES_40(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40),                            \
-      FORY_FC_MAKE_ENTRY(T, _41)
-#define FORY_FC_ENTRIES_42(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42)                     \
-  FORY_FC_ENTRIES_41(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41),                       \
-      FORY_FC_MAKE_ENTRY(T, _42)
-#define FORY_FC_ENTRIES_43(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43)                \
-  FORY_FC_ENTRIES_42(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42),                  \
-      FORY_FC_MAKE_ENTRY(T, _43)
-#define FORY_FC_ENTRIES_44(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44)           \
-  FORY_FC_ENTRIES_43(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43),             \
-      FORY_FC_MAKE_ENTRY(T, _44)
-#define FORY_FC_ENTRIES_45(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45)      \
-  FORY_FC_ENTRIES_44(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44),        \
-      FORY_FC_MAKE_ENTRY(T, _45)
-#define FORY_FC_ENTRIES_46(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46) \
-  FORY_FC_ENTRIES_45(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45),   \
-      FORY_FC_MAKE_ENTRY(T, _46)
-#define FORY_FC_ENTRIES_47(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41,   \
-                           _42, _43, _44, _45, _46, _47)                       \
-  FORY_FC_ENTRIES_46(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46),                                                     \
-      FORY_FC_MAKE_ENTRY(T, _47)
-#define FORY_FC_ENTRIES_48(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41,   \
-                           _42, _43, _44, _45, _46, _47, _48)                  \
-  FORY_FC_ENTRIES_47(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47),                                                \
-      FORY_FC_MAKE_ENTRY(T, _48)
-#define FORY_FC_ENTRIES_49(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41,   \
-                           _42, _43, _44, _45, _46, _47, _48, _49)             \
-  FORY_FC_ENTRIES_48(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48),                                           \
-      FORY_FC_MAKE_ENTRY(T, _49)
-#define FORY_FC_ENTRIES_50(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41,   \
-                           _42, _43, _44, _45, _46, _47, _48, _49, _50)        \
-  FORY_FC_ENTRIES_49(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49),                                      \
-      FORY_FC_MAKE_ENTRY(T, _50)
-#define FORY_FC_ENTRIES_51(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11,    \
-                           _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,   \
-                           _22, _23, _24, _25, _26, _27, _28, _29, _30, _31,   \
-                           _32, _33, _34, _35, _36, _37, _38, _39, _40, _41,   \
-                           _42, _43, _44, _45, _46, _47, _48, _49, _50, _51)   \
-  FORY_FC_ENTRIES_50(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50),                                 \
-      FORY_FC_MAKE_ENTRY(T, _51)
-#define FORY_FC_ENTRIES_52(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52)                                              \
-  FORY_FC_ENTRIES_51(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51),                            \
-      FORY_FC_MAKE_ENTRY(T, _52)
-#define FORY_FC_ENTRIES_53(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53)                                         \
-  FORY_FC_ENTRIES_52(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52),                       \
-      FORY_FC_MAKE_ENTRY(T, _53)
-#define FORY_FC_ENTRIES_54(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54)                                    \
-  FORY_FC_ENTRIES_53(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53),                  \
-      FORY_FC_MAKE_ENTRY(T, _54)
-#define FORY_FC_ENTRIES_55(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55)                               \
-  FORY_FC_ENTRIES_54(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54),             \
-      FORY_FC_MAKE_ENTRY(T, _55)
-#define FORY_FC_ENTRIES_56(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56)                          \
-  FORY_FC_ENTRIES_55(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55),        \
-      FORY_FC_MAKE_ENTRY(T, _56)
-#define FORY_FC_ENTRIES_57(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57)                     \
-  FORY_FC_ENTRIES_56(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56),   \
-      FORY_FC_MAKE_ENTRY(T, _57)
-#define FORY_FC_ENTRIES_58(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58)                \
-  FORY_FC_ENTRIES_57(                                                          \
-      T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15,     \
-      _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29,    \
-      _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43,    \
-      _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57),   \
-      FORY_FC_MAKE_ENTRY(T, _58)
-#define FORY_FC_ENTRIES_59(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59)           \
-  FORY_FC_ENTRIES_58(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58),                                                \
-      FORY_FC_MAKE_ENTRY(T, _59)
-#define FORY_FC_ENTRIES_60(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60)      \
-  FORY_FC_ENTRIES_59(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58, _59),                                           \
-      FORY_FC_MAKE_ENTRY(T, _60)
-#define FORY_FC_ENTRIES_61(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61) \
-  FORY_FC_ENTRIES_60(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58, _59, _60),                                      \
-      FORY_FC_MAKE_ENTRY(T, _61)
-#define FORY_FC_ENTRIES_62(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, \
-    _62)                                                                       \
-  FORY_FC_ENTRIES_61(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58, _59, _60, _61),                                 \
-      FORY_FC_MAKE_ENTRY(T, _62)
-#define FORY_FC_ENTRIES_63(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, \
-    _62, _63)                                                                  \
-  FORY_FC_ENTRIES_62(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58, _59, _60, _61, _62),                            \
-      FORY_FC_MAKE_ENTRY(T, _63)
-#define FORY_FC_ENTRIES_64(                                                    \
-    T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16,  \
-    _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
-    _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
-    _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, \
-    _62, _63, _64)                                                             \
-  FORY_FC_ENTRIES_63(T, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12,     \
-                     _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23,    \
-                     _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,    \
-                     _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45,    \
-                     _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56,    \
-                     _57, _58, _59, _60, _61, _62, _63),                       \
-      FORY_FC_MAKE_ENTRY(T, _64)
-
-// Main FORY_FIELD_CONFIG macro
-// Creates a constexpr tuple of FieldEntry objects with member pointer
-// verification. Descriptor name uses a unique line-based token.
-#define FORY_FC_DESCRIPTOR_NAME(line)                                          \
-  FORY_PP_CONCAT(ForyFieldConfigDescriptor_, line)
-#define FORY_FIELD_CONFIG(Type, ...)                                           \
-  FORY_FIELD_CONFIG_IMPL(__LINE__, Type, __VA_ARGS__)
-#define FORY_FIELD_CONFIG_IMPL(line, Type, ...)                                \
-  struct FORY_FC_DESCRIPTOR_NAME(line) {                                       \
-    static constexpr bool has_config = true;                                   \
-    static inline constexpr auto entries =                                     \
-        std::make_tuple(FORY_FC_ENTRIES(Type, __VA_ARGS__));                   \
-    static constexpr size_t field_count =                                      \
-        std::tuple_size_v<std::decay_t<decltype(entries)>>;                    \
-  };                                                                           \
-  constexpr auto fory_field_config(::fory::meta::Identity<Type>) {             \
-    return FORY_FC_DESCRIPTOR_NAME(line){};                                    \
-  }                                                                            \
-  static_assert(true)
diff --git a/cpp/fory/meta/field_info.h b/cpp/fory/meta/field_info.h
index f49cfddfe..b5476b559 100644
--- a/cpp/fory/meta/field_info.h
+++ b/cpp/fory/meta/field_info.h
@@ -22,6 +22,7 @@
 #include "fory/meta/preprocessor.h"
 #include "fory/meta/type_traits.h"
 #include <array>
+#include <cstdint>
 #include <string_view>
 #include <tuple>
 #include <type_traits>
@@ -29,6 +30,277 @@
 
 namespace fory {
 
+/// Encoding strategies for integer field nodes.
+enum class Encoding { Default = 0, Varint = 1, Fixed = 2, Tagged = 3 };
+
+enum class FieldNodeKind {
+  Default = 0,
+  Scalar = 1,
+  List = 2,
+  Set = 3,
+  Map = 4,
+  Inner = 5
+};
+
+enum class FieldScalarKind {
+  Inferred = 0,
+  Bool,
+  Int8,
+  Int16,
+  Int32,
+  Int64,
+  UInt8,
+  UInt16,
+  UInt32,
+  UInt64,
+  Float16,
+  BFloat16,
+  Float32,
+  Float64,
+  String
+};
+
+struct FieldNodeSpec {
+  static constexpr uint8_t kMaxNodes = 16;
+
+  FieldNodeKind kind_[kMaxNodes]{};
+  Encoding encoding_[kMaxNodes]{};
+  FieldScalarKind scalar_[kMaxNodes]{};
+  int8_t child0_[kMaxNodes]{};
+  int8_t child1_[kMaxNodes]{};
+  uint8_t size_ = 1;
+
+  constexpr FieldNodeSpec() {
+    for (uint8_t i = 0; i < kMaxNodes; ++i) {
+      kind_[i] = FieldNodeKind::Default;
+      encoding_[i] = Encoding::Default;
+      scalar_[i] = FieldScalarKind::Inferred;
+      child0_[i] = -1;
+      child1_[i] = -1;
+    }
+  }
+
+  static constexpr FieldNodeSpec
+  scalar(FieldScalarKind scalar_kind = FieldScalarKind::Inferred) {
+    FieldNodeSpec spec;
+    spec.kind_[0] = FieldNodeKind::Scalar;
+    spec.scalar_[0] = scalar_kind;
+    return spec;
+  }
+
+  constexpr int8_t append_tree(const FieldNodeSpec &child,
+                               int8_t child_index = 0) {
+    const int8_t dest = static_cast<int8_t>(size_++);
+    kind_[dest] = child.kind_[child_index];
+    encoding_[dest] = child.encoding_[child_index];
+    scalar_[dest] = child.scalar_[child_index];
+    if (child.child0_[child_index] >= 0) {
+      child0_[dest] = append_tree(child, child.child0_[child_index]);
+    }
+    if (child.child1_[child_index] >= 0) {
+      child1_[dest] = append_tree(child, child.child1_[child_index]);
+    }
+    return dest;
+  }
+
+  constexpr FieldNodeSpec with_encoding(Encoding encoding) const {
+    auto copy = *this;
+    copy.encoding_[0] = encoding;
+    if (copy.kind_[0] == FieldNodeKind::Default) {
+      copy.kind_[0] = FieldNodeKind::Scalar;
+    }
+    return copy;
+  }
+
+  constexpr FieldNodeSpec fixed() const {
+    return with_encoding(Encoding::Fixed);
+  }
+  constexpr FieldNodeSpec varint() const {
+    return with_encoding(Encoding::Varint);
+  }
+  constexpr FieldNodeSpec tagged() const {
+    return with_encoding(Encoding::Tagged);
+  }
+
+  constexpr FieldNodeSpec list(FieldNodeSpec elem) const {
+    auto copy = *this;
+    copy.size_ = 1;
+    copy.kind_[0] = FieldNodeKind::List;
+    copy.child0_[0] = copy.append_tree(elem);
+    copy.child1_[0] = -1;
+    copy.encoding_[0] = Encoding::Default;
+    return copy;
+  }
+
+  constexpr FieldNodeSpec set(FieldNodeSpec elem) const {
+    auto copy = *this;
+    copy.size_ = 1;
+    copy.kind_[0] = FieldNodeKind::Set;
+    copy.child0_[0] = copy.append_tree(elem);
+    copy.child1_[0] = -1;
+    copy.encoding_[0] = Encoding::Default;
+    return copy;
+  }
+
+  constexpr FieldNodeSpec map() const {
+    auto copy = *this;
+    copy.size_ = 1;
+    copy.kind_[0] = FieldNodeKind::Map;
+    copy.child0_[0] = -1;
+    copy.child1_[0] = -1;
+    copy.encoding_[0] = Encoding::Default;
+    return copy;
+  }
+
+  constexpr FieldNodeSpec map(FieldNodeSpec key, FieldNodeSpec value) const {
+    return map().key(key).value(value);
+  }
+
+  constexpr FieldNodeSpec key(FieldNodeSpec key_spec) const {
+    auto copy = *this;
+    copy.kind_[0] = FieldNodeKind::Map;
+    copy.child0_[0] = copy.append_tree(key_spec);
+    return copy;
+  }
+
+  constexpr FieldNodeSpec value(FieldNodeSpec value_spec) const {
+    auto copy = *this;
+    copy.kind_[0] = FieldNodeKind::Map;
+    copy.child1_[0] = copy.append_tree(value_spec);
+    return copy;
+  }
+
+  constexpr FieldNodeSpec inner(FieldNodeSpec child) const {
+    auto copy = *this;
+    copy.size_ = 1;
+    copy.kind_[0] = FieldNodeKind::Inner;
+    copy.child0_[0] = copy.append_tree(child);
+    copy.child1_[0] = -1;
+    copy.encoding_[0] = Encoding::Default;
+    return copy;
+  }
+};
+
+struct FieldMeta {
+  int16_t id_ = -1;
+  bool has_id_ = false;
+  bool nullable_ = false;
+  bool ref_ = false;
+  int dynamic_ = -1;
+  FieldNodeSpec spec_{};
+  Encoding encoding_ = Encoding::Default;
+  bool compress_ = true;
+  int16_t type_id_override_ = -1;
+
+  constexpr FieldMeta id(int16_t v) const {
+    auto copy = *this;
+    copy.id_ = v;
+    copy.has_id_ = true;
+    return copy;
+  }
+  constexpr FieldMeta nullable(bool v = true) const {
+    auto copy = *this;
+    copy.nullable_ = v;
+    return copy;
+  }
+  constexpr FieldMeta ref(bool v = true) const {
+    auto copy = *this;
+    copy.ref_ = v;
+    return copy;
+  }
+  constexpr FieldMeta dynamic(bool v) const {
+    auto copy = *this;
+    copy.dynamic_ = v ? 1 : 0;
+    return copy;
+  }
+  constexpr FieldMeta with_spec(FieldNodeSpec spec) const {
+    auto copy = *this;
+    copy.spec_ = spec;
+    copy.encoding_ = spec.encoding_[0];
+    return copy;
+  }
+  constexpr FieldMeta fixed() const { return with_spec(spec_.fixed()); }
+  constexpr FieldMeta varint() const { return with_spec(spec_.varint()); }
+  constexpr FieldMeta tagged() const { return with_spec(spec_.tagged()); }
+  constexpr FieldMeta list(FieldNodeSpec elem) const {
+    return with_spec(spec_.list(elem));
+  }
+  constexpr FieldMeta set(FieldNodeSpec elem) const {
+    return with_spec(spec_.set(elem));
+  }
+  constexpr FieldMeta map() const { return with_spec(spec_.map()); }
+  constexpr FieldMeta map(FieldNodeSpec key, FieldNodeSpec value) const {
+    return with_spec(spec_.map(key, value));
+  }
+  constexpr FieldMeta key(FieldNodeSpec key_spec) const {
+    return with_spec(spec_.key(key_spec));
+  }
+  constexpr FieldMeta value(FieldNodeSpec value_spec) const {
+    return with_spec(spec_.value(value_spec));
+  }
+  constexpr FieldMeta inner(FieldNodeSpec child) const {
+    return with_spec(spec_.inner(child));
+  }
+};
+
+constexpr FieldMeta F() { return FieldMeta{}; }
+constexpr FieldMeta F(int16_t id) { return FieldMeta{}.id(id); }
+
+namespace T {
+
+constexpr FieldNodeSpec
+scalar(FieldScalarKind kind = FieldScalarKind::Inferred) {
+  return FieldNodeSpec::scalar(kind);
+}
+constexpr FieldNodeSpec fixed() { return scalar().fixed(); }
+constexpr FieldNodeSpec varint() { return scalar().varint(); }
+constexpr FieldNodeSpec tagged() { return scalar().tagged(); }
+constexpr FieldNodeSpec list(FieldNodeSpec elem) {
+  return FieldNodeSpec{}.list(elem);
+}
+constexpr FieldNodeSpec set(FieldNodeSpec elem) {
+  return FieldNodeSpec{}.set(elem);
+}
+constexpr FieldNodeSpec map() { return FieldNodeSpec{}.map(); }
+constexpr FieldNodeSpec map(FieldNodeSpec key, FieldNodeSpec value) {
+  return FieldNodeSpec{}.map(key, value);
+}
+constexpr FieldNodeSpec inner(FieldNodeSpec child) {
+  return FieldNodeSpec{}.inner(child);
+}
+
+constexpr FieldNodeSpec boolean() { return scalar(FieldScalarKind::Bool); }
+constexpr FieldNodeSpec int8() { return scalar(FieldScalarKind::Int8); }
+constexpr FieldNodeSpec int16() { return scalar(FieldScalarKind::Int16); }
+constexpr FieldNodeSpec int32() { return scalar(FieldScalarKind::Int32); }
+constexpr FieldNodeSpec int64() { return scalar(FieldScalarKind::Int64); }
+constexpr FieldNodeSpec uint8() { return scalar(FieldScalarKind::UInt8); }
+constexpr FieldNodeSpec uint16() { return scalar(FieldScalarKind::UInt16); }
+constexpr FieldNodeSpec uint32() { return scalar(FieldScalarKind::UInt32); }
+constexpr FieldNodeSpec uint64() { return scalar(FieldScalarKind::UInt64); }
+constexpr FieldNodeSpec float16() { return scalar(FieldScalarKind::Float16); }
+constexpr FieldNodeSpec bfloat16() { return scalar(FieldScalarKind::BFloat16); }
+constexpr FieldNodeSpec float32() { return scalar(FieldScalarKind::Float32); }
+constexpr FieldNodeSpec float64() { return scalar(FieldScalarKind::Float64); }
+constexpr FieldNodeSpec string() { return scalar(FieldScalarKind::String); }
+
+} // namespace T
+
+namespace detail {
+
+struct FieldEntry {
+  const char *name;
+  FieldMeta meta;
+
+  constexpr FieldEntry(const char *n, FieldMeta m) : name(n), meta(m) {}
+};
+
+constexpr auto make_field_entry(const char *name, FieldMeta meta) {
+  return FieldEntry{name, meta};
+}
+
+} // namespace detail
+
 namespace meta {
 
 template <typename T> struct Identity {
@@ -214,15 +486,40 @@ constexpr auto concat_tuples_from_tuple(const Tuple &tuple) {
 
 #define FORY_BASE_TYPE(arg) FORY_PP_TUPLE_SECOND(arg)
 
+#define FORY_PP_IS_CONFIG(arg)                                                 \
+  FORY_PP_IS_CONFIG_IMPL(FORY_PP_IS_PAREN(arg), arg)
+#define FORY_PP_IS_CONFIG_IMPL(is_paren, arg)                                  \
+  FORY_PP_CONCAT(FORY_PP_IS_CONFIG_IMPL_, is_paren)(arg)
+#define FORY_PP_IS_CONFIG_IMPL_0(arg) 0
+#define FORY_PP_IS_CONFIG_IMPL_1(arg) FORY_PP_NOT(FORY_PP_IS_BASE(arg))
+
+#define FORY_FIELD_ARG_NAME(arg)                                               \
+  FORY_PP_IF(FORY_PP_IS_CONFIG(arg))                                           \
+  (FORY_PP_TUPLE_FIRST(arg), arg)
+
+#define FORY_FIELD_ARG_META(arg)                                               \
+  FORY_PP_IF(FORY_PP_IS_CONFIG(arg))                                           \
+  (FORY_PP_TUPLE_SECOND(arg), ::fory::F())
+
 #define FORY_FIELD_INFO_NAMES_FIELD(field) #field,
+#define FORY_FIELD_INFO_NAMES_ARG(arg) FORY_FIELD_INFO_NAMES_FIELD(arg)
 #define FORY_FIELD_INFO_NAMES_FUNC(arg)                                        \
   FORY_PP_IF(FORY_PP_IS_BASE(arg))                                             \
-  (FORY_PP_EMPTY(), FORY_FIELD_INFO_NAMES_FIELD(arg))
+  (FORY_PP_EMPTY(), FORY_FIELD_INFO_NAMES_ARG(FORY_FIELD_ARG_NAME(arg)))
 
 #define FORY_FIELD_INFO_PTRS_FIELD(type, field) &type::field,
+#define FORY_FIELD_INFO_PTRS_ARG(type, field)                                  \
+  FORY_FIELD_INFO_PTRS_FIELD(type, field)
 #define FORY_FIELD_INFO_PTRS_FUNC(type, arg)                                   \
   FORY_PP_IF(FORY_PP_IS_BASE(arg))                                             \
-  (FORY_PP_EMPTY(), FORY_FIELD_INFO_PTRS_FIELD(type, arg))
+  (FORY_PP_EMPTY(), FORY_FIELD_INFO_PTRS_ARG(type, FORY_FIELD_ARG_NAME(arg)))
+
+#define FORY_FIELD_INFO_CONFIG_ENTRY(arg)                                      \
+  ::fory::detail::make_field_entry(                                            \
+      FORY_PP_STRINGIFY(FORY_FIELD_ARG_NAME(arg)), FORY_FIELD_ARG_META(arg)),
+#define FORY_FIELD_INFO_CONFIG_FUNC(arg)                                       \
+  FORY_PP_IF(FORY_PP_IS_BASE(arg))                                             \
+  (FORY_PP_EMPTY(), FORY_FIELD_INFO_CONFIG_ENTRY(arg))
 
 #define FORY_BASE_NAMES_ARG(arg)                                               \
   FORY_PP_IF(FORY_PP_IS_BASE(arg))                                             \
@@ -268,6 +565,13 @@ constexpr auto concat_tuples_from_tuple(const Tuple &tuple) {
     static inline constexpr std::array<std::string_view, FieldSize>            \
         FieldNames = {                                                         \
             FORY_PP_FOREACH(FORY_FIELD_INFO_NAMES_FUNC, __VA_ARGS__)};         \
+    static inline constexpr auto FieldConfigEntries =                          \
+        std::tuple{FORY_PP_FOREACH(FORY_FIELD_INFO_CONFIG_FUNC, __VA_ARGS__)}; \
+    static constexpr bool has_config = true;                                   \
+    static inline constexpr auto entries = FieldConfigEntries;                 \
+    using FieldConfigEntriesType = std::decay_t<decltype(entries)>;            \
+    [[maybe_unused]] static constexpr size_t field_count =                     \
+        std::tuple_size_v<FieldConfigEntriesType>;                             \
     static inline constexpr auto Names =                                       \
         fory::meta::concat_arrays(BaseNames, FieldNames);                      \
     using BasePtrsType = decltype(fory::meta::concat_tuples_from_tuple(        \
@@ -303,6 +607,10 @@ constexpr auto concat_tuples_from_tuple(const Tuple &tuple) {
       const ::fory::meta::Identity<type> &) noexcept {                         \
     return FORY_PP_CONCAT(ForyFieldInfoDescriptor_, unique_id){};              \
   }                                                                            \
+  [[maybe_unused]] inline static constexpr auto fory_field_config(             \
+      const ::fory::meta::Identity<type> &) noexcept {                         \
+    return FORY_PP_CONCAT(ForyFieldInfoDescriptor_, unique_id){};              \
+  }                                                                            \
   [[maybe_unused]] inline static constexpr std::true_type fory_struct_marker(  \
       const ::fory::meta::Identity<type> &) noexcept {                         \
     return {};                                                                 \
@@ -314,6 +622,9 @@ constexpr auto concat_tuples_from_tuple(const Tuple &tuple) {
     static inline constexpr size_t Size = 0;                                   \
     static inline constexpr std::string_view Name = #type;                     \
     static inline constexpr std::array<std::string_view, Size> Names = {};     \
+    static constexpr bool has_config = false;                                  \
+    static inline constexpr auto entries = std::tuple{};                       \
+    [[maybe_unused]] static constexpr size_t field_count = 0;                  \
     using PtrsType = decltype(std::tuple{});                                   \
     static constexpr PtrsType ptrs() { return {}; }                            \
     static const PtrsType &ptrs_ref() {                                        \
@@ -332,6 +643,10 @@ constexpr auto concat_tuples_from_tuple(const Tuple &tuple) {
       const ::fory::meta::Identity<type> &) noexcept {                         \
     return FORY_PP_CONCAT(ForyFieldInfoDescriptor_, unique_id){};              \
   }                                                                            \
+  [[maybe_unused]] inline static constexpr auto fory_field_config(             \
+      const ::fory::meta::Identity<type> &) noexcept {                         \
+    return FORY_PP_CONCAT(ForyFieldInfoDescriptor_, unique_id){};              \
+  }                                                                            \
   [[maybe_unused]] inline static constexpr std::true_type fory_struct_marker(  \
       const ::fory::meta::Identity<type> &) noexcept {                         \
     return {};                                                                 \
diff --git a/cpp/fory/meta/field_test.cc b/cpp/fory/meta/field_test.cc
index f8c92af56..24285a133 100644
--- a/cpp/fory/meta/field_test.cc
+++ b/cpp/fory/meta/field_test.cc
@@ -21,396 +21,121 @@
 
 #include "fory/meta/field.h"
 #include "fory/meta/field_info.h"
+#include <map>
 #include <memory>
 #include <optional>
 #include <string>
+#include <vector>
 
-namespace fory {
+namespace fory::test {
 
-namespace test {
+namespace T = fory::T;
 
-// ============================================================================
-// Type Traits Tests
-// ============================================================================
-
-TEST(FieldTraits, IsSharedPtr) {
+TEST(FieldTraits, CarrierTraits) {
   static_assert(!detail::is_shared_ptr_v<int>);
-  static_assert(!detail::is_shared_ptr_v<std::string>);
-  static_assert(!detail::is_shared_ptr_v<std::unique_ptr<int>>);
   static_assert(detail::is_shared_ptr_v<std::shared_ptr<int>>);
-  static_assert(detail::is_shared_ptr_v<std::shared_ptr<std::string>>);
-}
-
-TEST(FieldTraits, IsUniquePtr) {
   static_assert(!detail::is_unique_ptr_v<int>);
-  static_assert(!detail::is_unique_ptr_v<std::string>);
-  static_assert(!detail::is_unique_ptr_v<std::shared_ptr<int>>);
   static_assert(detail::is_unique_ptr_v<std::unique_ptr<int>>);
-  static_assert(detail::is_unique_ptr_v<std::unique_ptr<std::string>>);
-}
-
-TEST(FieldTraits, IsOptional) {
   static_assert(!detail::is_optional_v<int>);
-  static_assert(!detail::is_optional_v<std::string>);
-  static_assert(!detail::is_optional_v<std::shared_ptr<int>>);
   static_assert(detail::is_optional_v<std::optional<int>>);
-  static_assert(detail::is_optional_v<std::optional<std::string>>);
-}
-
-TEST(FieldTraits, IsSmartPtr) {
-  static_assert(!detail::is_smart_ptr_v<int>);
-  static_assert(!detail::is_smart_ptr_v<std::string>);
   static_assert(!detail::is_smart_ptr_v<std::optional<int>>);
   static_assert(detail::is_smart_ptr_v<std::shared_ptr<int>>);
   static_assert(detail::is_smart_ptr_v<std::unique_ptr<int>>);
 }
 
-// ============================================================================
-// fory::field<> Basic Tests
-// ============================================================================
-
-TEST(Field, BasicPrimitive) {
-  using FieldType = field<int32_t, 0>;
-  static_assert(FieldType::tag_id == 0);
-  static_assert(FieldType::is_nullable == false);
-  static_assert(FieldType::track_ref == false);
+TEST(FieldBuilder, ScalarEncodingSpec) {
+  constexpr auto fixed = fory::F(7).fixed();
+  static_assert(fixed.has_id_);
+  static_assert(fixed.id_ == 7);
+  static_assert(fixed.encoding_ == Encoding::Fixed);
+  static_assert(fixed.spec_.encoding_[0] == Encoding::Fixed);
 
-  FieldType f;
-  f = 42;
-  EXPECT_EQ(f.value, 42);
-
-  // Implicit conversion
-  int32_t val = f;
-  EXPECT_EQ(val, 42);
+  constexpr auto tagged = T::uint64().tagged();
+  static_assert(tagged.scalar_[0] == FieldScalarKind::UInt64);
+  static_assert(tagged.encoding_[0] == Encoding::Tagged);
 }
 
-TEST(Field, BasicString) {
-  using FieldType = field<std::string, 1>;
-  static_assert(FieldType::tag_id == 1);
-  static_assert(FieldType::is_nullable == false);
-  static_assert(FieldType::track_ref == false);
-
-  FieldType f;
-  f = "hello";
-  EXPECT_EQ(f.value, "hello");
+TEST(FieldBuilder, NestedSpecComposition) {
+  constexpr auto spec = fory::F().map(T::varint(), T::list(T::tagged()));
+  static_assert(!spec.has_id_);
+  static_assert(spec.spec_.kind_[0] == FieldNodeKind::Map);
+  static_assert(spec.spec_.kind_[1] == FieldNodeKind::Scalar);
+  static_assert(spec.spec_.encoding_[1] == Encoding::Varint);
+  static_assert(spec.spec_.kind_[2] == FieldNodeKind::List);
+  static_assert(spec.spec_.encoding_[3] == Encoding::Tagged);
 }
 
-TEST(Field, OptionalField) {
-  // std::optional is inherently nullable
-  using FieldType = field<std::optional<int32_t>, 2>;
-  static_assert(FieldType::tag_id == 2);
-  static_assert(FieldType::is_nullable == true);
-  static_assert(FieldType::track_ref == false);
+TEST(FieldBuilder, ChainMapPartialOverrides) {
+  constexpr auto key_only = fory::F().map().key(T::varint());
+  static_assert(key_only.spec_.kind_[0] == FieldNodeKind::Map);
+  static_assert(key_only.spec_.child0_[0] == 1);
+  static_assert(key_only.spec_.child1_[0] == -1);
+  static_assert(key_only.spec_.encoding_[1] == Encoding::Varint);
 
-  FieldType f;
-  f = std::optional<int32_t>(123);
-  EXPECT_TRUE(f.value.has_value());
-  EXPECT_EQ(*f.value, 123);
+  constexpr auto value_only = fory::F().map().value(T::list(T::tagged()));
+  static_assert(value_only.spec_.child0_[0] == -1);
+  static_assert(value_only.spec_.child1_[0] == 1);
+  static_assert(value_only.spec_.kind_[1] == FieldNodeKind::List);
+  static_assert(value_only.spec_.encoding_[2] == Encoding::Tagged);
 }
 
-TEST(Field, SharedPtrNonNullable) {
-  // shared_ptr is non-nullable by default
-  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 == true);
-
-  FieldType f;
-  f = std::make_shared<int32_t>(99);
-  EXPECT_NE(f.value, nullptr);
-  EXPECT_EQ(*f.value, 99);
-}
-
-TEST(Field, SharedPtrNullable) {
-  // shared_ptr with nullable option
-  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 == true);
-
-  FieldType f;
-  EXPECT_EQ(f.value, nullptr); // Default is null
-}
-
-TEST(Field, SharedPtrWithRef) {
-  // shared_ptr with ref tracking
-  using FieldType = field<std::shared_ptr<int32_t>, 5, ref>;
-  static_assert(FieldType::tag_id == 5);
-  static_assert(FieldType::is_nullable == false);
-  static_assert(FieldType::track_ref == true);
-}
-
-TEST(Field, SharedPtrNullableWithRef) {
-  // shared_ptr with both nullable and ref
-  using FieldType = field<std::shared_ptr<int32_t>, 6, nullable, ref>;
-  static_assert(FieldType::tag_id == 6);
-  static_assert(FieldType::is_nullable == true);
-  static_assert(FieldType::track_ref == true);
-}
+struct NameModeStruct {
+  uint32_t id;
+  std::optional<std::vector<uint64_t>> values;
 
-TEST(Field, UniquePtrNonNullable) {
-  // unique_ptr is non-nullable by default
-  using FieldType = field<std::unique_ptr<int32_t>, 7>;
-  static_assert(FieldType::tag_id == 7);
-  static_assert(FieldType::is_nullable == false);
-  static_assert(FieldType::track_ref == false); // ref not valid for unique_ptr
-}
-
-TEST(Field, UniquePtrNullable) {
-  // unique_ptr with nullable option
-  using FieldType = field<std::unique_ptr<int32_t>, 8, nullable>;
-  static_assert(FieldType::tag_id == 8);
-  static_assert(FieldType::is_nullable == true);
-  static_assert(FieldType::track_ref == false);
-}
-
-TEST(Field, SharedPtrNotNull) {
-  // shared_ptr with not_null option (explicit non-nullable)
-  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 == true);
-}
-
-TEST(Field, SharedPtrNotNullWithRef) {
-  // shared_ptr with not_null and ref options
-  using FieldType = field<std::shared_ptr<int32_t>, 10, not_null, ref>;
-  static_assert(FieldType::tag_id == 10);
-  static_assert(FieldType::is_nullable == false);
-  static_assert(FieldType::track_ref == true);
-}
-
-// ============================================================================
-// fory::field<> Type Traits Tests
-// ============================================================================
-
-TEST(FieldTraits, IsForyField) {
-  static_assert(!is_fory_field_v<int>);
-  static_assert(!is_fory_field_v<std::string>);
-  static_assert(!is_fory_field_v<std::shared_ptr<int>>);
-  static_assert(is_fory_field_v<field<int, 0>>);
-  static_assert(is_fory_field_v<field<std::string, 1>>);
-  static_assert(is_fory_field_v<field<std::shared_ptr<int>, 2, nullable>>);
-}
-
-TEST(FieldTraits, UnwrapField) {
-  static_assert(std::is_same_v<unwrap_field_t<int>, int>);
-  static_assert(std::is_same_v<unwrap_field_t<std::string>, std::string>);
-  static_assert(std::is_same_v<unwrap_field_t<field<int, 0>>, int>);
-  static_assert(
-      std::is_same_v<unwrap_field_t<field<std::string, 1>>, std::string>);
-  static_assert(
-      std::is_same_v<unwrap_field_t<field<std::shared_ptr<int>, 2, nullable>>,
-                     std::shared_ptr<int>>);
-}
-
-TEST(FieldTraits, FieldTagId) {
-  static_assert(field_tag_id_v<int> == -1);
-  static_assert(field_tag_id_v<field<int, 0>> == 0);
-  static_assert(field_tag_id_v<field<std::string, 42>> == 42);
-}
-
-TEST(FieldTraits, FieldIsNullable) {
-  static_assert(field_is_nullable_v<int> == false);
-  static_assert(field_is_nullable_v<std::optional<int>> == true);
-  static_assert(field_is_nullable_v<field<int, 0>> == false);
-  static_assert(field_is_nullable_v<field<std::optional<int>, 1>> == true);
-  static_assert(field_is_nullable_v<field<std::shared_ptr<int>, 2>> == false);
-  static_assert(field_is_nullable_v<field<std::shared_ptr<int>, 3, nullable>> ==
-                true);
-}
-
-TEST(FieldTraits, FieldTrackRef) {
-  static_assert(field_track_ref_v<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>> == 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);
-}
-
-// ============================================================================
-// Struct with fory::field<> members
-// ============================================================================
-
-struct Person {
-  field<std::string, 0> name;
-  field<int32_t, 1> age;
-  field<std::optional<std::string>, 2> nickname;
-  field<std::shared_ptr<Person>, 3, ref> parent;
-  field<std::shared_ptr<Person>, 4, nullable> guardian;
-  FORY_STRUCT(Person, name, age, nickname, parent, guardian);
+  FORY_STRUCT(NameModeStruct, id,
+              (values, fory::F().inner(T::list(T::tagged()))));
 };
 
-TEST(FieldStruct, BasicUsage) {
-  Person p;
-  p.name = "Alice";
-  p.age = 30;
-  p.nickname = std::optional<std::string>("Ali");
-  p.parent = nullptr;
-  p.guardian = nullptr;
-
-  EXPECT_EQ(p.name.value, "Alice");
-  EXPECT_EQ(p.age.value, 30);
-  EXPECT_TRUE(p.nickname.value.has_value());
-  EXPECT_EQ(*p.nickname.value, "Ali");
-  EXPECT_EQ(p.parent.value, nullptr);
-  EXPECT_EQ(p.guardian.value, nullptr);
-}
-
-TEST(FieldStruct, FieldInfo) {
-  Person p;
-  constexpr auto info = meta::fory_field_info(p);
-
-  static_assert(info.Size == 5);
-  static_assert(info.Name == "Person");
-  static_assert(info.Names[0] == "name");
-  static_assert(info.Names[1] == "age");
-  static_assert(info.Names[2] == "nickname");
-  static_assert(info.Names[3] == "parent");
-  static_assert(info.Names[4] == "guardian");
-}
-
-} // namespace test
-
-} // namespace fory
-
-// ============================================================================
-// FORY_FIELD_TAGS Macro Tests
-// ============================================================================
-
-namespace field_tags_test {
+struct IdModeStruct {
+  uint32_t id;
+  std::map<uint32_t, std::vector<int64_t>> values;
 
-// Test struct with pure C++ types (no fory::field wrappers)
-struct Document {
-  std::string title;
-  int32_t version;
-  std::optional<std::string> description;
-  std::shared_ptr<Document> author;
-  std::shared_ptr<Document> reviewer;
-  std::shared_ptr<Document> parent;
-  std::unique_ptr<std::string> metadata;
-  FORY_STRUCT(Document, title, version, description, author, reviewer, parent,
-              metadata);
+  FORY_STRUCT(IdModeStruct, (id, fory::F(0).varint()),
+              (values, fory::F(1).map(T::varint(), T::list(T::tagged()))));
 };
 
-// Test struct with nullable + ref combined
-struct Node {
-  std::string name;
-  std::shared_ptr<Node> left;
-  std::shared_ptr<Node> right;
-  FORY_STRUCT(Node, name, left, right);
+struct NamespaceConfigStruct {
+  uint32_t id;
+  uint64_t timestamp;
 };
 
-// Test with single field
-struct SingleField {
-  int32_t value;
-  FORY_STRUCT(SingleField, value);
-};
-
-// Define field tags in the same namespace as the types.
-FORY_FIELD_TAGS(Document, (title, 0),     // string: non-nullable
-                (version, 1),             // int: 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, ref
-                (metadata, 6, nullable)); // unique_ptr: nullable
-
-FORY_FIELD_TAGS(Node, (name, 0), (left, 1, nullable, ref),
-                (right, 2, nullable, ref));
-
-FORY_FIELD_TAGS(SingleField, (value, 0));
+FORY_STRUCT(NamespaceConfigStruct, (id, fory::F(3).varint()),
+            (timestamp, fory::F(4).tagged()));
 
-} // namespace field_tags_test
-
-namespace fory {
-namespace test {
-
-using field_tags_test::Document;
-using field_tags_test::Node;
-using field_tags_test::SingleField;
-
-TEST(FieldTags, HasTags) {
-  static_assert(detail::has_field_tags_v<Document> == true);
-  static_assert(detail::has_field_tags_v<Person> == false); // Uses fory::field
-  static_assert(detail::has_field_tags_v<int> == false);
-}
-
-TEST(FieldTags, FieldCount) {
-  static_assert(detail::FieldTagsInfo<Document>::field_count == 7);
-}
-
-TEST(FieldTags, TagIds) {
-  // Check tag IDs
-  static_assert(detail::GetFieldTagEntry<Document, 0>::id == 0);
-  static_assert(detail::GetFieldTagEntry<Document, 1>::id == 1);
-  static_assert(detail::GetFieldTagEntry<Document, 2>::id == 2);
-  static_assert(detail::GetFieldTagEntry<Document, 3>::id == 3);
-  static_assert(detail::GetFieldTagEntry<Document, 4>::id == 4);
-  static_assert(detail::GetFieldTagEntry<Document, 5>::id == 5);
-  static_assert(detail::GetFieldTagEntry<Document, 6>::id == 6);
-}
-
-TEST(FieldTags, Nullability) {
-  // title (string): non-nullable
-  static_assert(detail::GetFieldTagEntry<Document, 0>::is_nullable == false);
-  // version (int): non-nullable
-  static_assert(detail::GetFieldTagEntry<Document, 1>::is_nullable == false);
-  // description (optional): inherently nullable
-  static_assert(detail::GetFieldTagEntry<Document, 2>::is_nullable == true);
-  // author (shared_ptr): non-nullable (default)
-  static_assert(detail::GetFieldTagEntry<Document, 3>::is_nullable == false);
-  // reviewer (shared_ptr, nullable): nullable
-  static_assert(detail::GetFieldTagEntry<Document, 4>::is_nullable == true);
-  // parent (shared_ptr, ref): non-nullable
-  static_assert(detail::GetFieldTagEntry<Document, 5>::is_nullable == false);
-  // metadata (unique_ptr, nullable): nullable
-  static_assert(detail::GetFieldTagEntry<Document, 6>::is_nullable == true);
+TEST(FieldConfig, NameModeEntries) {
+  static_assert(detail::has_field_config_v<NameModeStruct>);
+  static_assert(!detail::GetFieldConfigEntry<NameModeStruct, 0>::has_id);
+  static_assert(!detail::GetFieldConfigEntry<NameModeStruct, 1>::has_id);
+  static_assert(detail::GetFieldConfigEntry<NameModeStruct, 1>::spec.kind_[0] ==
+                FieldNodeKind::Inner);
 }
 
-TEST(FieldTags, RefTracking) {
-  // 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 == 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);
+TEST(FieldConfig, IdModeEntries) {
+  static_assert(detail::has_field_config_v<IdModeStruct>);
+  static_assert(detail::GetFieldConfigEntry<IdModeStruct, 0>::has_id);
+  static_assert(detail::GetFieldConfigEntry<IdModeStruct, 0>::id == 0);
+  static_assert(detail::GetFieldConfigEntry<IdModeStruct, 0>::encoding ==
+                Encoding::Varint);
+  static_assert(detail::GetFieldConfigEntry<IdModeStruct, 1>::has_id);
+  static_assert(detail::GetFieldConfigEntry<IdModeStruct, 1>::id == 1);
+  static_assert(detail::GetFieldConfigEntry<IdModeStruct, 1>::spec.kind_[0] ==
+                FieldNodeKind::Map);
 }
 
-TEST(FieldTags, NullableWithRef) {
-  // name: non-nullable, no ref
-  static_assert(detail::GetFieldTagEntry<Node, 0>::id == 0);
-  static_assert(detail::GetFieldTagEntry<Node, 0>::is_nullable == false);
-  static_assert(detail::GetFieldTagEntry<Node, 0>::track_ref == false);
-
-  // left: nullable + ref
-  static_assert(detail::GetFieldTagEntry<Node, 1>::id == 1);
-  static_assert(detail::GetFieldTagEntry<Node, 1>::is_nullable == true);
-  static_assert(detail::GetFieldTagEntry<Node, 1>::track_ref == true);
-
-  // right: nullable + ref
-  static_assert(detail::GetFieldTagEntry<Node, 2>::id == 2);
-  static_assert(detail::GetFieldTagEntry<Node, 2>::is_nullable == true);
-  static_assert(detail::GetFieldTagEntry<Node, 2>::track_ref == true);
-}
-
-TEST(FieldTags, SingleField) {
-  static_assert(detail::has_field_tags_v<SingleField> == true);
-  static_assert(detail::FieldTagsInfo<SingleField>::field_count == 1);
-  static_assert(detail::GetFieldTagEntry<SingleField, 0>::id == 0);
-  static_assert(detail::GetFieldTagEntry<SingleField, 0>::is_nullable == false);
-  static_assert(detail::GetFieldTagEntry<SingleField, 0>::track_ref == false);
+TEST(FieldConfig, NamespaceScopeEntries) {
+  static_assert(detail::has_field_config_v<NamespaceConfigStruct>);
+  static_assert(
+      detail::GetFieldConfigEntry<NamespaceConfigStruct, 0>::has_entry);
+  static_assert(detail::GetFieldConfigEntry<NamespaceConfigStruct, 0>::has_id);
+  static_assert(detail::GetFieldConfigEntry<NamespaceConfigStruct, 0>::id == 3);
+  static_assert(
+      detail::GetFieldConfigEntry<NamespaceConfigStruct, 0>::encoding ==
+      Encoding::Varint);
+  static_assert(detail::GetFieldConfigEntry<NamespaceConfigStruct, 1>::has_id);
+  static_assert(detail::GetFieldConfigEntry<NamespaceConfigStruct, 1>::id == 4);
+  static_assert(detail::GetFieldConfigEntry<NamespaceConfigStruct, 1>::spec
+                    .encoding_[0] == Encoding::Tagged);
 }
 
-} // namespace test
-
-} // namespace fory
-
-int main(int argc, char **argv) {
-  ::testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
+} // namespace fory::test
diff --git a/cpp/fory/serialization/BUILD b/cpp/fory/serialization/BUILD
index 2308a59bf..87b3ede02 100644
--- a/cpp/fory/serialization/BUILD
+++ b/cpp/fory/serialization/BUILD
@@ -146,15 +146,6 @@ cc_test(
     ],
 )
 
-cc_test(
-    name = "field_serializer_test",
-    srcs = ["field_serializer_test.cc"],
-    deps = [
-        ":fory_serialization",
-        "@googletest//:gtest",
-    ],
-)
-
 cc_test(
     name = "namespace_macro_test",
     srcs = ["namespace_macro_test.cc"],
diff --git a/cpp/fory/serialization/collection_serializer.h b/cpp/fory/serialization/collection_serializer.h
index 0dadca10f..dfbd74478 100644
--- a/cpp/fory/serialization/collection_serializer.h
+++ b/cpp/fory/serialization/collection_serializer.h
@@ -631,7 +631,8 @@ struct Serializer<
 
     if (total_bytes_u32 % sizeof(T) != 0) {
       ctx.set_error(Error::invalid_data(
-          "Vector byte size not aligned with element size"));
+          "Vector byte size " + std::to_string(total_bytes_u32) +
+          " not aligned with element size " + std::to_string(sizeof(T))));
       return std::vector<T, Alloc>();
     }
     std::vector<T, Alloc> result(elem_count);
diff --git a/cpp/fory/serialization/field_serializer_test.cc b/cpp/fory/serialization/field_serializer_test.cc
deleted file mode 100644
index 72ca3ff68..000000000
--- a/cpp/fory/serialization/field_serializer_test.cc
+++ /dev/null
@@ -1,1211 +0,0 @@
-/*
- * 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.
- */
-
-/**
- * Serialization tests for fory::field<> template.
- *
- * Tests struct serialization with fory::field<> members including:
- * - Primitive fields with tag IDs
- * - String fields with tag IDs
- * - Optional fields (inherently nullable)
- * - Smart pointer fields with nullable/ref options
- * - Nested structs with field metadata
- * - Reference tracking with fory::ref
- */
-
-#include "fory/meta/field.h"
-#include "fory/serialization/fory.h"
-#include "gtest/gtest.h"
-#include <cstdint>
-#include <memory>
-#include <optional>
-#include <string>
-#include <vector>
-
-// ============================================================================
-// Struct definitions with fory::field<> members
-// ============================================================================
-
-// Basic struct with primitive fields
-struct FieldPerson {
-  fory::field<std::string, 0> name;
-  fory::field<int32_t, 1> age;
-  fory::field<double, 2> score;
-  fory::field<bool, 3> active;
-
-  bool operator==(const FieldPerson &other) const {
-    return name.value == other.name.value && age.value == other.age.value &&
-           score.value == other.score.value &&
-           active.value == other.active.value;
-  }
-  FORY_STRUCT(FieldPerson, name, age, score, active);
-};
-
-// Struct with optional fields
-struct FieldOptionalData {
-  fory::field<std::string, 0> required_name;
-  fory::field<std::optional<int32_t>, 1> optional_age;
-  fory::field<std::optional<std::string>, 2> optional_email;
-
-  bool operator==(const FieldOptionalData &other) const {
-    return required_name.value == other.required_name.value &&
-           optional_age.value == other.optional_age.value &&
-           optional_email.value == other.optional_email.value;
-  }
-  FORY_STRUCT(FieldOptionalData, required_name, optional_age, optional_email);
-};
-
-// Struct with shared_ptr fields (non-nullable by default)
-struct FieldSharedPtrHolder {
-  fory::field<std::shared_ptr<int32_t>, 0> value;
-  fory::field<std::shared_ptr<std::string>, 1> text;
-
-  bool operator==(const FieldSharedPtrHolder &other) const {
-    if (static_cast<bool>(value.value) != static_cast<bool>(other.value.value))
-      return false;
-    if (static_cast<bool>(text.value) != static_cast<bool>(other.text.value))
-      return false;
-    if (value.value && *value.value != *other.value.value)
-      return false;
-    if (text.value && *text.value != *other.text.value)
-      return false;
-    return true;
-  }
-  FORY_STRUCT(FieldSharedPtrHolder, value, text);
-};
-
-// Struct with nullable shared_ptr fields
-struct FieldNullableSharedPtr {
-  fory::field<std::shared_ptr<int32_t>, 0, fory::nullable> nullable_value;
-  fory::field<std::shared_ptr<std::string>, 1, fory::nullable> nullable_text;
-
-  bool operator==(const FieldNullableSharedPtr &other) const {
-    if (static_cast<bool>(nullable_value.value) !=
-        static_cast<bool>(other.nullable_value.value))
-      return false;
-    if (static_cast<bool>(nullable_text.value) !=
-        static_cast<bool>(other.nullable_text.value))
-      return false;
-    if (nullable_value.value &&
-        *nullable_value.value != *other.nullable_value.value)
-      return false;
-    if (nullable_text.value &&
-        *nullable_text.value != *other.nullable_text.value)
-      return false;
-    return true;
-  }
-  FORY_STRUCT(FieldNullableSharedPtr, nullable_value, nullable_text);
-};
-
-// Struct with unique_ptr fields
-struct FieldUniquePtrHolder {
-  fory::field<std::unique_ptr<int32_t>, 0> value;
-  fory::field<std::unique_ptr<int32_t>, 1, fory::nullable> nullable_value;
-  FORY_STRUCT(FieldUniquePtrHolder, value, nullable_value);
-};
-
-// Nested struct for reference tracking tests
-struct FieldNode {
-  fory::field<int32_t, 0> id;
-  fory::field<std::string, 1> name;
-
-  bool operator==(const FieldNode &other) const {
-    return id.value == other.id.value && name.value == other.name.value;
-  }
-  FORY_STRUCT(FieldNode, id, name);
-};
-
-// Struct with ref tracking for shared_ptr
-struct FieldRefTrackingHolder {
-  fory::field<std::shared_ptr<FieldNode>, 0, fory::ref> first;
-  fory::field<std::shared_ptr<FieldNode>, 1, fory::ref> second;
-  FORY_STRUCT(FieldRefTrackingHolder, first, second);
-};
-
-// Struct with nullable + ref
-struct FieldNullableRefHolder {
-  fory::field<std::shared_ptr<FieldNode>, 0, fory::nullable, fory::ref> node;
-  FORY_STRUCT(FieldNullableRefHolder, node);
-};
-
-// Struct with not_null + ref
-struct FieldNotNullRefHolder {
-  fory::field<std::shared_ptr<FieldNode>, 0, fory::not_null, fory::ref> node;
-  FORY_STRUCT(FieldNotNullRefHolder, node);
-};
-
-// Struct with vector of field-wrapped structs
-struct FieldVectorHolder {
-  fory::field<std::vector<FieldNode>, 0> nodes;
-
-  bool operator==(const FieldVectorHolder &other) const {
-    return nodes.value == other.nodes.value;
-  }
-  FORY_STRUCT(FieldVectorHolder, nodes);
-};
-
-// Mixed struct: some fields with fory::field, some without
-struct MixedFieldStruct {
-  fory::field<std::string, 0> field_name;
-  int32_t plain_age; // Not wrapped
-  fory::field<double, 2> field_score;
-
-  bool operator==(const MixedFieldStruct &other) const {
-    return field_name.value == other.field_name.value &&
-           plain_age == other.plain_age &&
-           field_score.value == other.field_score.value;
-  }
-  FORY_STRUCT(MixedFieldStruct, field_name, plain_age, field_score);
-};
-
-// ============================================================================
-// Test Implementation
-// ============================================================================
-
-namespace fory {
-namespace serialization {
-namespace test {
-
-inline void register_field_test_types(Fory &fory) {
-  uint32_t type_id = 500; // Start from 500 to avoid conflicts
-
-  fory.register_struct<FieldPerson>(type_id++);
-  fory.register_struct<FieldOptionalData>(type_id++);
-  fory.register_struct<FieldSharedPtrHolder>(type_id++);
-  fory.register_struct<FieldNullableSharedPtr>(type_id++);
-  fory.register_struct<FieldUniquePtrHolder>(type_id++);
-  fory.register_struct<FieldNode>(type_id++);
-  fory.register_struct<FieldRefTrackingHolder>(type_id++);
-  fory.register_struct<FieldNullableRefHolder>(type_id++);
-  fory.register_struct<FieldNotNullRefHolder>(type_id++);
-  fory.register_struct<FieldVectorHolder>(type_id++);
-  fory.register_struct<MixedFieldStruct>(type_id++);
-}
-
-Fory create_fory(bool track_ref = true) {
-  return Fory::builder().xlang(true).track_ref(track_ref).build();
-}
-
-// ============================================================================
-// Primitive Field Tests
-// ============================================================================
-
-TEST(FieldSerializerTest, PrimitiveFieldsRoundTrip) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldPerson original;
-  original.name = "Alice";
-  original.age = 30;
-  original.score = 95.5;
-  original.active = true;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result =
-      fory.deserialize<FieldPerson>(bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-TEST(FieldSerializerTest, PrimitiveFieldsEdgeCases) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldPerson original;
-  original.name = "";
-  original.age = -1;
-  original.score = 0.0;
-  original.active = false;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result =
-      fory.deserialize<FieldPerson>(bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-// ============================================================================
-// Optional Field Tests
-// ============================================================================
-
-TEST(FieldSerializerTest, OptionalFieldsAllSet) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldOptionalData original;
-  original.required_name = "Bob";
-  original.optional_age = 25;
-  original.optional_email = "bob@example.com";
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldOptionalData>(bytes_result->data(),
-                                                          bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-TEST(FieldSerializerTest, OptionalFieldsAllEmpty) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldOptionalData original;
-  original.required_name = "Charlie";
-  original.optional_age = std::nullopt;
-  original.optional_email = std::nullopt;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldOptionalData>(bytes_result->data(),
-                                                          bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-TEST(FieldSerializerTest, OptionalFieldsMixed) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldOptionalData original;
-  original.required_name = "Diana";
-  original.optional_age = 35;
-  original.optional_email = std::nullopt;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldOptionalData>(bytes_result->data(),
-                                                          bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-// ============================================================================
-// Shared Pointer Field Tests
-// ============================================================================
-
-TEST(FieldSerializerTest, SharedPtrFieldsNonNullable) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldSharedPtrHolder original;
-  original.value = std::make_shared<int32_t>(42);
-  original.text = std::make_shared<std::string>("hello");
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldSharedPtrHolder>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-TEST(FieldSerializerTest, NullableSharedPtrWithValues) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldNullableSharedPtr original;
-  original.nullable_value = std::make_shared<int32_t>(99);
-  original.nullable_text = std::make_shared<std::string>("world");
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldNullableSharedPtr>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-TEST(FieldSerializerTest, NullableSharedPtrWithNulls) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldNullableSharedPtr original;
-  original.nullable_value = nullptr;
-  original.nullable_text = nullptr;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldNullableSharedPtr>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-TEST(FieldSerializerTest, NullableSharedPtrMixed) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldNullableSharedPtr original;
-  original.nullable_value = std::make_shared<int32_t>(123);
-  original.nullable_text = nullptr;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldNullableSharedPtr>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-// ============================================================================
-// Unique Pointer Field Tests
-// ============================================================================
-
-TEST(FieldSerializerTest, UniquePtrFieldWithValue) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldUniquePtrHolder original;
-  original.value = std::make_unique<int32_t>(2025);
-  original.nullable_value = std::make_unique<int32_t>(1234);
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldUniquePtrHolder>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  ASSERT_TRUE(deser_result.value().value.value);
-  EXPECT_EQ(*deser_result.value().value.value, 2025);
-  ASSERT_TRUE(deser_result.value().nullable_value.value);
-  EXPECT_EQ(*deser_result.value().nullable_value.value, 1234);
-}
-
-TEST(FieldSerializerTest, UniquePtrNullableFieldNull) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldUniquePtrHolder original;
-  original.value = std::make_unique<int32_t>(999);
-  original.nullable_value = nullptr;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldUniquePtrHolder>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  ASSERT_TRUE(deser_result.value().value.value);
-  EXPECT_EQ(*deser_result.value().value.value, 999);
-  EXPECT_EQ(deser_result.value().nullable_value.value, nullptr);
-}
-
-// ============================================================================
-// Reference Tracking Tests
-// ============================================================================
-
-TEST(FieldSerializerTest, RefTrackingSameObject) {
-  auto fory = create_fory(true);
-  register_field_test_types(fory);
-
-  auto shared_node = std::make_shared<FieldNode>();
-  shared_node->id = 42;
-  shared_node->name = "shared";
-
-  FieldRefTrackingHolder original;
-  original.first = shared_node;
-  original.second = shared_node; // Same object
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldRefTrackingHolder>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  auto &result = deser_result.value();
-  ASSERT_TRUE(result.first.value);
-  ASSERT_TRUE(result.second.value);
-  EXPECT_EQ(result.first.value->id.value, 42);
-  EXPECT_EQ(result.first.value->name.value, "shared");
-  EXPECT_EQ(result.second.value->id.value, 42);
-  EXPECT_EQ(result.second.value->name.value, "shared");
-
-  // Reference tracking should preserve shared_ptr aliasing
-  EXPECT_EQ(result.first.value, result.second.value)
-      << "Reference tracking should preserve shared_ptr aliasing";
-}
-
-TEST(FieldSerializerTest, RefTrackingDifferentObjects) {
-  auto fory = create_fory(true);
-  register_field_test_types(fory);
-
-  FieldRefTrackingHolder original;
-  original.first = std::make_shared<FieldNode>();
-  original.first.value->id = 1;
-  original.first.value->name = "first";
-  original.second = std::make_shared<FieldNode>();
-  original.second.value->id = 2;
-  original.second.value->name = "second";
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldRefTrackingHolder>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  auto &result = deser_result.value();
-  ASSERT_TRUE(result.first.value);
-  ASSERT_TRUE(result.second.value);
-  EXPECT_EQ(result.first.value->id.value, 1);
-  EXPECT_EQ(result.first.value->name.value, "first");
-  EXPECT_EQ(result.second.value->id.value, 2);
-  EXPECT_EQ(result.second.value->name.value, "second");
-
-  // Different objects should not share
-  EXPECT_NE(result.first.value, result.second.value);
-}
-
-TEST(FieldSerializerTest, NullableRefWithValue) {
-  auto fory = create_fory(true);
-  register_field_test_types(fory);
-
-  FieldNullableRefHolder original;
-  original.node = std::make_shared<FieldNode>();
-  original.node.value->id = 100;
-  original.node.value->name = "nullable_ref";
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldNullableRefHolder>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  auto &result = deser_result.value();
-  ASSERT_TRUE(result.node.value);
-  EXPECT_EQ(result.node.value->id.value, 100);
-  EXPECT_EQ(result.node.value->name.value, "nullable_ref");
-}
-
-TEST(FieldSerializerTest, NullableRefWithNull) {
-  auto fory = create_fory(true);
-  register_field_test_types(fory);
-
-  FieldNullableRefHolder original;
-  original.node = nullptr;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldNullableRefHolder>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(deser_result.value().node.value, nullptr);
-}
-
-TEST(FieldSerializerTest, NotNullRefWithValue) {
-  auto fory = create_fory(true);
-  register_field_test_types(fory);
-
-  FieldNotNullRefHolder original;
-  original.node = std::make_shared<FieldNode>();
-  original.node.value->id = 200;
-  original.node.value->name = "not_null_ref";
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldNotNullRefHolder>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  auto &result = deser_result.value();
-  ASSERT_TRUE(result.node.value);
-  EXPECT_EQ(result.node.value->id.value, 200);
-  EXPECT_EQ(result.node.value->name.value, "not_null_ref");
-}
-
-// ============================================================================
-// Container Field Tests
-// ============================================================================
-
-TEST(FieldSerializerTest, VectorOfFieldStructs) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldVectorHolder original;
-  for (int i = 0; i < 5; ++i) {
-    FieldNode node;
-    node.id = i;
-    node.name = "node_" + std::to_string(i);
-    original.nodes.value.push_back(node);
-  }
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldVectorHolder>(bytes_result->data(),
-                                                          bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-TEST(FieldSerializerTest, EmptyVectorField) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  FieldVectorHolder original;
-  // Empty vector
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<FieldVectorHolder>(bytes_result->data(),
-                                                          bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-// ============================================================================
-// Mixed Field Tests
-// ============================================================================
-
-TEST(FieldSerializerTest, MixedFieldStruct) {
-  auto fory = create_fory();
-  register_field_test_types(fory);
-
-  MixedFieldStruct original;
-  original.field_name = "mixed";
-  original.plain_age = 42;
-  original.field_score = 88.5;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<MixedFieldStruct>(bytes_result->data(),
-                                                         bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-// ============================================================================
-// Field Metadata Compile-time Tests
-// ============================================================================
-
-TEST(FieldSerializerTest, FieldMetadataCompileTime) {
-  // Verify compile-time field metadata extraction
-  using PersonType = FieldPerson;
-
-  // Check that field types are correctly detected
-  static_assert(is_fory_field_v<decltype(PersonType::name)>);
-  static_assert(is_fory_field_v<decltype(PersonType::age)>);
-  static_assert(is_fory_field_v<decltype(PersonType::score)>);
-  static_assert(is_fory_field_v<decltype(PersonType::active)>);
-
-  // Check tag IDs
-  static_assert(decltype(PersonType::name)::tag_id == 0);
-  static_assert(decltype(PersonType::age)::tag_id == 1);
-  static_assert(decltype(PersonType::score)::tag_id == 2);
-  static_assert(decltype(PersonType::active)::tag_id == 3);
-
-  // Check nullability
-  static_assert(!decltype(PersonType::name)::is_nullable);
-  static_assert(!decltype(PersonType::age)::is_nullable);
-
-  // Optional fields are inherently nullable
-  static_assert(decltype(FieldOptionalData::optional_age)::is_nullable);
-  static_assert(decltype(FieldOptionalData::optional_email)::is_nullable);
-
-  // Nullable shared_ptr
-  static_assert(decltype(FieldNullableSharedPtr::nullable_value)::is_nullable);
-  static_assert(!decltype(FieldSharedPtrHolder::value)::is_nullable);
-
-  // Ref tracking
-  static_assert(decltype(FieldRefTrackingHolder::first)::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);
-  static_assert(decltype(FieldNotNullRefHolder::node)::track_ref);
-}
-
-} // namespace test
-} // namespace serialization
-} // namespace fory
-
-// ============================================================================
-// FORY_FIELD_TAGS Serialization Tests
-// FORY_FIELD_TAGS remains namespace-scope, FORY_STRUCT is declared in-class
-// ============================================================================
-
-// Simple helper struct for testing FORY_FIELD_TAGS
-struct TagsTestData {
-  std::string content;
-  int32_t value;
-
-  bool operator==(const TagsTestData &other) const {
-    return content == other.content && value == other.value;
-  }
-  FORY_STRUCT(TagsTestData, content, value);
-};
-
-FORY_FIELD_TAGS(TagsTestData, (content, 0), (value, 1));
-
-// Pure C++ struct with FORY_FIELD_TAGS metadata (non-invasive)
-struct TagsTestDocument {
-  std::string title;
-  int32_t version;
-  std::optional<std::string> description;
-  std::shared_ptr<TagsTestData> data;
-  std::shared_ptr<TagsTestData> optional_data;
-
-  bool operator==(const TagsTestDocument &other) const {
-    bool data_eq = static_cast<bool>(data) == static_cast<bool>(other.data);
-    if (data_eq && data && other.data) {
-      data_eq = (*data == *other.data);
-    }
-    bool opt_data_eq = static_cast<bool>(optional_data) ==
-                       static_cast<bool>(other.optional_data);
-    if (opt_data_eq && optional_data && other.optional_data) {
-      opt_data_eq = (*optional_data == *other.optional_data);
-    }
-    return title == other.title && version == other.version &&
-           description == other.description && data_eq && opt_data_eq;
-  }
-  FORY_STRUCT(TagsTestDocument, title, version, description, data,
-              optional_data);
-};
-
-FORY_FIELD_TAGS(TagsTestDocument, (title, 0), // string: non-nullable
-                (version, 1),                 // int: non-nullable
-                (description, 2),             // optional: inherently nullable
-                (data, 3), // shared_ptr: non-nullable (default)
-                (optional_data, 4, nullable)); // shared_ptr: nullable
-
-// Struct for testing FORY_FIELD_TAGS with ref tracking
-struct TagsRefNode {
-  std::string name;
-  int32_t id;
-
-  bool operator==(const TagsRefNode &other) const {
-    return name == other.name && id == other.id;
-  }
-  FORY_STRUCT(TagsRefNode, name, id);
-};
-
-FORY_FIELD_TAGS(TagsRefNode, (name, 0), (id, 1));
-
-// Struct with ref tracking via FORY_FIELD_TAGS
-struct TagsRefHolder {
-  std::shared_ptr<TagsRefNode> first;
-  std::shared_ptr<TagsRefNode> second;
-  FORY_STRUCT(TagsRefHolder, first, second);
-};
-
-FORY_FIELD_TAGS(TagsRefHolder, (first, 0, ref), (second, 1, ref));
-
-// Struct with nullable + ref via FORY_FIELD_TAGS
-struct TagsNullableRefHolder {
-  std::shared_ptr<TagsRefNode> required_node;
-  std::shared_ptr<TagsRefNode> optional_node;
-  FORY_STRUCT(TagsNullableRefHolder, required_node, optional_node);
-};
-
-FORY_FIELD_TAGS(TagsNullableRefHolder, (required_node, 0, ref),
-                (optional_node, 1, nullable, ref));
-
-// Tree-like struct with self-referential nullable ref pointers
-struct TagsTreeNode {
-  std::string value;
-  std::shared_ptr<TagsTreeNode> left;
-  std::shared_ptr<TagsTreeNode> right;
-  FORY_STRUCT(TagsTreeNode, value, left, right);
-};
-
-FORY_FIELD_TAGS(TagsTreeNode, (value, 0), (left, 1, nullable, ref),
-                (right, 2, nullable, ref));
-
-namespace fory {
-namespace serialization {
-namespace test {
-
-TEST(FieldTagsSerializerTest, BasicTagsDocument) {
-  auto fory =
-      Fory::builder().xlang(true).track_ref(false).compatible(false).build();
-  fory.register_struct<TagsTestData>(200);
-  fory.register_struct<TagsTestDocument>(201);
-
-  TagsTestDocument original;
-  original.title = "My Document";
-  original.version = 1;
-  original.description = "A test document";
-  original.data = std::make_shared<TagsTestData>();
-  original.data->content = "data content";
-  original.data->value = 42;
-  original.optional_data = nullptr; // nullable
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<TagsTestDocument>(bytes_result->data(),
-                                                         bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-TEST(FieldTagsSerializerTest, TagsDocumentWithNullableSet) {
-  auto fory =
-      Fory::builder().xlang(true).track_ref(false).compatible(false).build();
-  fory.register_struct<TagsTestData>(200);
-  fory.register_struct<TagsTestDocument>(201);
-
-  TagsTestDocument original;
-  original.title = "Doc with optional";
-  original.version = 2;
-  original.description = std::nullopt;
-  original.data = std::make_shared<TagsTestData>();
-  original.data->content = "main data";
-  original.data->value = 100;
-  original.optional_data = std::make_shared<TagsTestData>();
-  original.optional_data->content = "optional data";
-  original.optional_data->value = 999;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<TagsTestDocument>(bytes_result->data(),
-                                                         bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  EXPECT_EQ(original, deser_result.value());
-}
-
-TEST(FieldTagsSerializerTest, TagsMetadataCompileTime) {
-  // Verify that FORY_FIELD_TAGS metadata is correctly accessed
-  using DocHelpers = detail::CompileTimeFieldHelpers<TagsTestDocument>;
-  using DataHelpers = detail::CompileTimeFieldHelpers<TagsTestData>;
-
-  // Check tag IDs via GetFieldTagEntry for TagsTestData
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTestData, 0>::id == 0);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTestData, 1>::id == 1);
-
-  // Check tag IDs via GetFieldTagEntry for TagsTestDocument
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTestDocument, 0>::id == 0);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTestDocument, 1>::id == 1);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTestDocument, 2>::id == 2);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTestDocument, 3>::id == 3);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTestDocument, 4>::id == 4);
-
-  // Check nullability via GetFieldTagEntry
-  // title (string): non-nullable
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsTestDocument, 0>::is_nullable ==
-      false);
-  // version (int): non-nullable
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsTestDocument, 1>::is_nullable ==
-      false);
-  // description (optional): inherently nullable
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsTestDocument, 2>::is_nullable ==
-      true);
-  // data (shared_ptr): non-nullable (default)
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsTestDocument, 3>::is_nullable ==
-      false);
-  // optional_data (shared_ptr, nullable): nullable
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsTestDocument, 4>::is_nullable ==
-      true);
-
-  // Verify CompileTimeFieldHelpers uses the tags correctly
-  static_assert(DocHelpers::template field_nullable<0>() == false);
-  static_assert(DocHelpers::template field_nullable<1>() == false);
-  static_assert(DocHelpers::template field_nullable<2>() == true);
-  static_assert(DocHelpers::template field_nullable<3>() == false);
-  static_assert(DocHelpers::template field_nullable<4>() == true);
-
-  // Check tag IDs via CompileTimeFieldHelpers
-  static_assert(DocHelpers::template field_tag_id<0>() == 0);
-  static_assert(DocHelpers::template field_tag_id<1>() == 1);
-  static_assert(DocHelpers::template field_tag_id<2>() == 2);
-  static_assert(DocHelpers::template field_tag_id<3>() == 3);
-  static_assert(DocHelpers::template field_tag_id<4>() == 4);
-
-  // Verify TagsTestData helpers
-  static_assert(DataHelpers::template field_nullable<0>() == false);
-  static_assert(DataHelpers::template field_nullable<1>() == false);
-  static_assert(DataHelpers::template field_tag_id<0>() == 0);
-  static_assert(DataHelpers::template field_tag_id<1>() == 1);
-}
-
-// ============================================================================
-// FORY_FIELD_TAGS Reference Tracking Tests
-// ============================================================================
-
-TEST(FieldTagsSerializerTest, TagsRefTrackingSameObject) {
-  auto fory =
-      Fory::builder().xlang(true).track_ref(true).compatible(false).build();
-  fory.register_struct<TagsRefNode>(300);
-  fory.register_struct<TagsRefHolder>(301);
-
-  // Create a shared node that will be referenced by both fields
-  auto shared_node = std::make_shared<TagsRefNode>();
-  shared_node->name = "shared";
-  shared_node->id = 42;
-
-  TagsRefHolder original;
-  original.first = shared_node;
-  original.second = shared_node; // Same object
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<TagsRefHolder>(bytes_result->data(),
-                                                      bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  auto &result = deser_result.value();
-  ASSERT_TRUE(result.first);
-  ASSERT_TRUE(result.second);
-  EXPECT_EQ(result.first->name, "shared");
-  EXPECT_EQ(result.first->id, 42);
-  EXPECT_EQ(result.second->name, "shared");
-  EXPECT_EQ(result.second->id, 42);
-
-  // Reference tracking should preserve shared_ptr aliasing
-  EXPECT_EQ(result.first, result.second)
-      << "FORY_FIELD_TAGS with ref should preserve shared_ptr aliasing";
-}
-
-TEST(FieldTagsSerializerTest, TagsRefTrackingDifferentObjects) {
-  auto fory =
-      Fory::builder().xlang(true).track_ref(true).compatible(false).build();
-  fory.register_struct<TagsRefNode>(300);
-  fory.register_struct<TagsRefHolder>(301);
-
-  TagsRefHolder original;
-  original.first = std::make_shared<TagsRefNode>();
-  original.first->name = "first";
-  original.first->id = 1;
-  original.second = std::make_shared<TagsRefNode>();
-  original.second->name = "second";
-  original.second->id = 2;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<TagsRefHolder>(bytes_result->data(),
-                                                      bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  auto &result = deser_result.value();
-  ASSERT_TRUE(result.first);
-  ASSERT_TRUE(result.second);
-  EXPECT_EQ(result.first->name, "first");
-  EXPECT_EQ(result.first->id, 1);
-  EXPECT_EQ(result.second->name, "second");
-  EXPECT_EQ(result.second->id, 2);
-
-  // Different objects should not share
-  EXPECT_NE(result.first, result.second);
-}
-
-TEST(FieldTagsSerializerTest, TagsNullableRefWithValue) {
-  auto fory =
-      Fory::builder().xlang(true).track_ref(true).compatible(false).build();
-  fory.register_struct<TagsRefNode>(300);
-  fory.register_struct<TagsNullableRefHolder>(302);
-
-  TagsNullableRefHolder original;
-  original.required_node = std::make_shared<TagsRefNode>();
-  original.required_node->name = "required";
-  original.required_node->id = 100;
-  original.optional_node = std::make_shared<TagsRefNode>();
-  original.optional_node->name = "optional";
-  original.optional_node->id = 200;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<TagsNullableRefHolder>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  auto &result = deser_result.value();
-  ASSERT_TRUE(result.required_node);
-  ASSERT_TRUE(result.optional_node);
-  EXPECT_EQ(result.required_node->name, "required");
-  EXPECT_EQ(result.required_node->id, 100);
-  EXPECT_EQ(result.optional_node->name, "optional");
-  EXPECT_EQ(result.optional_node->id, 200);
-}
-
-TEST(FieldTagsSerializerTest, TagsNullableRefWithNull) {
-  auto fory =
-      Fory::builder().xlang(true).track_ref(true).compatible(false).build();
-  fory.register_struct<TagsRefNode>(300);
-  fory.register_struct<TagsNullableRefHolder>(302);
-
-  TagsNullableRefHolder original;
-  original.required_node = std::make_shared<TagsRefNode>();
-  original.required_node->name = "required";
-  original.required_node->id = 100;
-  original.optional_node = nullptr; // nullable field set to null
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<TagsNullableRefHolder>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  auto &result = deser_result.value();
-  ASSERT_TRUE(result.required_node);
-  EXPECT_EQ(result.required_node->name, "required");
-  EXPECT_EQ(result.required_node->id, 100);
-  EXPECT_EQ(result.optional_node, nullptr);
-}
-
-TEST(FieldTagsSerializerTest, TagsNullableRefSharedObject) {
-  auto fory =
-      Fory::builder().xlang(true).track_ref(true).compatible(false).build();
-  fory.register_struct<TagsRefNode>(300);
-  fory.register_struct<TagsNullableRefHolder>(302);
-
-  // Both fields point to the same object
-  auto shared_node = std::make_shared<TagsRefNode>();
-  shared_node->name = "shared_nullable";
-  shared_node->id = 999;
-
-  TagsNullableRefHolder original;
-  original.required_node = shared_node;
-  original.optional_node = shared_node;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<TagsNullableRefHolder>(
-      bytes_result->data(), bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  auto &result = deser_result.value();
-  ASSERT_TRUE(result.required_node);
-  ASSERT_TRUE(result.optional_node);
-  EXPECT_EQ(result.required_node->name, "shared_nullable");
-  EXPECT_EQ(result.required_node->id, 999);
-
-  // Both should point to the same deserialized object
-  EXPECT_EQ(result.required_node, result.optional_node)
-      << "Nullable ref fields should also preserve shared_ptr aliasing";
-}
-
-TEST(FieldTagsSerializerTest, TagsTreeNodeSerialization) {
-  auto fory =
-      Fory::builder().xlang(true).track_ref(true).compatible(false).build();
-  fory.register_struct<TagsTreeNode>(303);
-
-  // Build a simple tree:
-  //        root
-  //       /    \
-  //    left   right
-  //   /    \
-  // ll     lr
-  TagsTreeNode original;
-  original.value = "root";
-  original.left = std::make_shared<TagsTreeNode>();
-  original.left->value = "left";
-  original.left->left = std::make_shared<TagsTreeNode>();
-  original.left->left->value = "ll";
-  original.left->left->left = nullptr;
-  original.left->left->right = nullptr;
-  original.left->right = std::make_shared<TagsTreeNode>();
-  original.left->right->value = "lr";
-  original.left->right->left = nullptr;
-  original.left->right->right = nullptr;
-  original.right = std::make_shared<TagsTreeNode>();
-  original.right->value = "right";
-  original.right->left = nullptr;
-  original.right->right = nullptr;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<TagsTreeNode>(bytes_result->data(),
-                                                     bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  auto &result = deser_result.value();
-  EXPECT_EQ(result.value, "root");
-  ASSERT_TRUE(result.left);
-  EXPECT_EQ(result.left->value, "left");
-  ASSERT_TRUE(result.left->left);
-  EXPECT_EQ(result.left->left->value, "ll");
-  EXPECT_EQ(result.left->left->left, nullptr);
-  EXPECT_EQ(result.left->left->right, nullptr);
-  ASSERT_TRUE(result.left->right);
-  EXPECT_EQ(result.left->right->value, "lr");
-  EXPECT_EQ(result.left->right->left, nullptr);
-  EXPECT_EQ(result.left->right->right, nullptr);
-  ASSERT_TRUE(result.right);
-  EXPECT_EQ(result.right->value, "right");
-  EXPECT_EQ(result.right->left, nullptr);
-  EXPECT_EQ(result.right->right, nullptr);
-}
-
-TEST(FieldTagsSerializerTest, TagsTreeNodeWithSharedSubtree) {
-  auto fory =
-      Fory::builder().xlang(true).track_ref(true).compatible(false).build();
-  fory.register_struct<TagsTreeNode>(303);
-
-  // Build a DAG (tree with shared subtree):
-  //        root
-  //       /    \
-  //    left   right
-  //       \   /
-  //       shared
-  auto shared = std::make_shared<TagsTreeNode>();
-  shared->value = "shared_subtree";
-  shared->left = nullptr;
-  shared->right = nullptr;
-
-  TagsTreeNode original;
-  original.value = "root";
-  original.left = std::make_shared<TagsTreeNode>();
-  original.left->value = "left";
-  original.left->left = nullptr;
-  original.left->right = shared; // left's right points to shared
-  original.right = std::make_shared<TagsTreeNode>();
-  original.right->value = "right";
-  original.right->left = shared; // right's left points to same shared
-  original.right->right = nullptr;
-
-  auto bytes_result = fory.serialize(original);
-  ASSERT_TRUE(bytes_result.ok()) << bytes_result.error().to_string();
-
-  auto deser_result = fory.deserialize<TagsTreeNode>(bytes_result->data(),
-                                                     bytes_result->size());
-  ASSERT_TRUE(deser_result.ok()) << deser_result.error().to_string();
-
-  auto &result = deser_result.value();
-  EXPECT_EQ(result.value, "root");
-  ASSERT_TRUE(result.left);
-  ASSERT_TRUE(result.right);
-  ASSERT_TRUE(result.left->right);
-  ASSERT_TRUE(result.right->left);
-
-  // The shared subtree should still be the same object after deserialization
-  EXPECT_EQ(result.left->right, result.right->left)
-      << "Tree nodes with shared subtrees should preserve sharing";
-  EXPECT_EQ(result.left->right->value, "shared_subtree");
-}
-
-TEST(FieldTagsSerializerTest, TagsRefMetadataCompileTime) {
-  // Verify that FORY_FIELD_TAGS with ref option is correctly parsed
-  using RefHolderHelpers = detail::CompileTimeFieldHelpers<TagsRefHolder>;
-  using NullableRefHelpers =
-      detail::CompileTimeFieldHelpers<TagsNullableRefHolder>;
-  using TreeHelpers = detail::CompileTimeFieldHelpers<TagsTreeNode>;
-
-  // TagsRefHolder: (first, 0, ref), (second, 1, ref)
-  static_assert(::fory::detail::GetFieldTagEntry<TagsRefHolder, 0>::id == 0);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsRefHolder, 1>::id == 1);
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsRefHolder, 0>::is_nullable == false);
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsRefHolder, 1>::is_nullable == false);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsRefHolder, 0>::track_ref ==
-                true);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsRefHolder, 1>::track_ref ==
-                true);
-
-  // TagsNullableRefHolder: (required_node, 0, ref), (optional_node, 1,
-  // nullable, ref)
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsNullableRefHolder, 0>::id == 0);
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsNullableRefHolder, 1>::id == 1);
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsNullableRefHolder, 0>::is_nullable ==
-      false);
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsNullableRefHolder, 1>::is_nullable ==
-      true);
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsNullableRefHolder, 0>::track_ref ==
-      true);
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsNullableRefHolder, 1>::track_ref ==
-      true);
-
-  // TagsTreeNode: (value, 0), (left, 1, nullable, ref), (right, 2, nullable,
-  // ref)
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTreeNode, 0>::id == 0);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTreeNode, 1>::id == 1);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTreeNode, 2>::id == 2);
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsTreeNode, 0>::is_nullable == false);
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsTreeNode, 1>::is_nullable == true);
-  static_assert(
-      ::fory::detail::GetFieldTagEntry<TagsTreeNode, 2>::is_nullable == true);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTreeNode, 0>::track_ref ==
-                false);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTreeNode, 1>::track_ref ==
-                true);
-  static_assert(::fory::detail::GetFieldTagEntry<TagsTreeNode, 2>::track_ref ==
-                true);
-
-  // Verify CompileTimeFieldHelpers uses the tags correctly
-  static_assert(RefHolderHelpers::template field_track_ref<0>() == true);
-  static_assert(RefHolderHelpers::template field_track_ref<1>() == true);
-  static_assert(NullableRefHelpers::template field_track_ref<0>() == true);
-  static_assert(NullableRefHelpers::template field_track_ref<1>() == true);
-  static_assert(TreeHelpers::template field_track_ref<0>() == false);
-  static_assert(TreeHelpers::template field_track_ref<1>() == true);
-  static_assert(TreeHelpers::template field_track_ref<2>() == true);
-}
-
-} // namespace test
-} // namespace serialization
-} // namespace fory
-
-int main(int argc, char **argv) {
-  ::testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
diff --git a/cpp/fory/serialization/namespace_macro_test.cc b/cpp/fory/serialization/namespace_macro_test.cc
index a508f57c6..155e951a6 100644
--- a/cpp/fory/serialization/namespace_macro_test.cc
+++ b/cpp/fory/serialization/namespace_macro_test.cc
@@ -25,10 +25,12 @@
 #include "gtest/gtest.h"
 
 #include <cstdint>
+#include <map>
 #include <optional>
 #include <string>
 #include <utility>
 #include <variant>
+#include <vector>
 
 namespace macro_test {
 
@@ -47,7 +49,7 @@ private:
   int32_t id_ = 0;
 
 public:
-  FORY_STRUCT(Configured, id_);
+  FORY_STRUCT(Configured, (id_, fory::F(1).varint()));
 };
 
 class OptionalHolder final {
@@ -64,7 +66,7 @@ private:
   std::optional<std::string> name_;
 
 public:
-  FORY_STRUCT(OptionalHolder, name_);
+  FORY_STRUCT(OptionalHolder, (name_, fory::F(1)));
 };
 
 class Choice final {
@@ -103,6 +105,32 @@ private:
       : value_(tag, std::forward<Args>(args)...) {}
 };
 
+class NestedChoice final {
+public:
+  using Values = std::map<uint32_t, std::vector<uint64_t>>;
+
+  NestedChoice() = default;
+
+  static NestedChoice values(Values value) {
+    return NestedChoice(std::move(value));
+  }
+
+  uint32_t fory_case_id() const noexcept { return 1; }
+
+  template <class Visitor> decltype(auto) visit(Visitor &&vis) const {
+    return std::forward<Visitor>(vis)(values_);
+  }
+
+  bool operator==(const NestedChoice &other) const {
+    return values_ == other.values_;
+  }
+
+private:
+  Values values_;
+
+  explicit NestedChoice(Values value) : values_(std::move(value)) {}
+};
+
 class Partial final {
 public:
   Partial() = default;
@@ -115,7 +143,8 @@ private:
   int64_t count_ = 0;
 
 public:
-  FORY_STRUCT(Partial, id_, name_, count_);
+  FORY_STRUCT(Partial, (id_, fory::F(5)), (name_, fory::F(6)),
+              (count_, fory::F(7).varint()));
 };
 
 class EnumContainer final {
@@ -125,13 +154,12 @@ public:
 
 FORY_ENUM(EnumContainer::Kind, Alpha, Beta);
 
-FORY_FIELD_CONFIG(Configured, (id_, fory::F().id(1).varint()));
-FORY_FIELD_TAGS(OptionalHolder, (name_, 1));
-FORY_FIELD_CONFIG(Partial, (count_, fory::F().id(7).varint()));
-FORY_FIELD_TAGS(Partial, (id_, 5));
-
-FORY_UNION(Choice, (std::string, text, fory::F(1)),
-           (int32_t, number, fory::F(2).varint()));
+FORY_UNION(Choice, (text, std::string, fory::F(1)),
+           (number, int32_t, fory::F(2).varint()));
+FORY_UNION(NestedChoice,
+           (values, NestedChoice::Values,
+            fory::F(1).map(fory::T::uint32().fixed(),
+                           fory::T::list(fory::T::uint64().tagged()))));
 
 } // namespace macro_test
 
@@ -139,7 +167,7 @@ namespace fory {
 namespace serialization {
 namespace test {
 
-TEST(NamespaceMacros, FieldConfigAndTagsInNamespace) {
+TEST(NamespaceMacros, FieldConfigInNamespace) {
   static_assert(::fory::detail::has_field_config_v<macro_test::Configured>);
   static_assert(
       ::fory::detail::GetFieldConfigEntry<macro_test::Configured, 0>::id == 1);
@@ -147,20 +175,20 @@ TEST(NamespaceMacros, FieldConfigAndTagsInNamespace) {
                                                     0>::encoding ==
                 ::fory::Encoding::Varint);
 
-  static_assert(::fory::detail::has_field_tags_v<macro_test::OptionalHolder>);
-  static_assert(::fory::detail::GetFieldTagEntry<macro_test::OptionalHolder,
-                                                 0>::is_nullable);
+  static_assert(
+      ::fory::detail::GetFieldConfigEntry<macro_test::OptionalHolder, 0>::id ==
+      1);
+  static_assert(::fory::serialization::detail::CompileTimeFieldHelpers<
+                macro_test::OptionalHolder>::field_nullable<0>());
 
-  static_assert(::fory::detail::GetFieldTagEntry<macro_test::Partial, 0>::id ==
-                5);
   static_assert(
-      !::fory::detail::GetFieldTagEntry<macro_test::Partial, 1>::has_entry);
+      ::fory::detail::GetFieldConfigEntry<macro_test::Partial, 0>::id == 5);
+  static_assert(
+      ::fory::detail::GetFieldConfigEntry<macro_test::Partial, 1>::id == 6);
   static_assert(::fory::serialization::detail::CompileTimeFieldHelpers<
                 macro_test::Partial>::field_nullable<1>());
   static_assert(
       ::fory::detail::GetFieldConfigEntry<macro_test::Partial, 2>::id == 7);
-  static_assert(
-      ::fory::detail::GetFieldConfigEntry<macro_test::Partial, 0>::id == -1);
 }
 
 TEST(NamespaceMacros, UnionInNamespace) {
@@ -176,6 +204,21 @@ TEST(NamespaceMacros, UnionInNamespace) {
   EXPECT_EQ(macro_test::Choice::text("hello"), decoded.value());
 }
 
+TEST(NamespaceMacros, NestedUnionPayloadSpec) {
+  auto fory = Fory::builder().xlang(true).track_ref(false).build();
+  ASSERT_TRUE(fory.register_union<macro_test::NestedChoice>(1002).ok());
+
+  macro_test::NestedChoice::Values values = {{4000000000u, {7u, 1000000000u}},
+                                             {3u, {42u}}};
+  auto bytes = fory.serialize(macro_test::NestedChoice::values(values));
+  ASSERT_TRUE(bytes.ok()) << bytes.error().to_string();
+
+  auto decoded =
+      fory.deserialize<macro_test::NestedChoice>(bytes->data(), bytes->size());
+  ASSERT_TRUE(decoded.ok()) << decoded.error().to_string();
+  EXPECT_EQ(macro_test::NestedChoice::values(values), decoded.value());
+}
+
 TEST(NamespaceMacros, EnumInAndOutOfClass) {
   static_assert(::fory::meta::EnumInfo<macro_test::LocalEnum>::defined);
   static_assert(::fory::meta::EnumInfo<macro_test::LocalEnum>::size == 2);
diff --git a/cpp/fory/serialization/serializer_traits.h b/cpp/fory/serialization/serializer_traits.h
index 98c476f7a..ad07c4aa6 100644
--- a/cpp/fory/serialization/serializer_traits.h
+++ b/cpp/fory/serialization/serializer_traits.h
@@ -373,8 +373,19 @@ inline constexpr bool is_shared_ref_v = is_shared_ref<T>::value;
 // Element Type Extraction
 // ============================================================================
 
-/// get element type for containers (reuse meta::GetValueType)
-template <typename T> using element_type_t = typename meta::GetValueType<T>;
+/// get element type for containers.
+template <typename T, typename = void> struct element_type_impl {
+  using type = typename meta::GetValueType<T>;
+};
+
+template <typename T>
+struct element_type_impl<T, std::void_t<typename T::value_type>> {
+  using type = typename T::value_type;
+};
+
+template <typename T>
+using element_type_t = typename element_type_impl<
+    std::remove_cv_t<std::remove_reference_t<T>>>::type;
 
 /// get key type for map-like containers
 template <typename T, typename = void> struct key_type_impl {};
diff --git a/cpp/fory/serialization/skip.cc b/cpp/fory/serialization/skip.cc
index 9835e0b45..d146c9395 100644
--- a/cpp/fory/serialization/skip.cc
+++ b/cpp/fory/serialization/skip.cc
@@ -82,7 +82,7 @@ void skip_list(ReadContext &ctx, const FieldType &field_type) {
     elem_type = field_type.generics[0];
   } else {
     // Unknown element type, need to read type info for each element
-    elem_type.type_id = 0; // Unknown
+    elem_type.set_type_id(0); // Unknown
     elem_type.nullable = false;
   }
 
@@ -135,8 +135,8 @@ void skip_map(ReadContext &ctx, const FieldType &field_type) {
     value_type = field_type.generics[1];
   } else {
     // Unknown types
-    key_type.type_id = 0;
-    value_type.type_id = 0;
+    key_type.set_type_id(0);
+    value_type.set_type_id(0);
   }
 
   uint64_t read_count = 0;
@@ -423,7 +423,7 @@ void skip_unknown(ReadContext &ctx) {
     // For non-struct types (primitives, arrays, maps, etc.),
     // recursively call skip_field_value with the actual type
     FieldType actual_field_type;
-    actual_field_type.type_id = type_info->type_id;
+    actual_field_type.set_type_id(type_info->type_id);
     actual_field_type.nullable = false;
     skip_field_value(ctx, actual_field_type, RefMode::None);
     return;
@@ -469,7 +469,7 @@ void skip_union(ReadContext &ctx) {
 
   // skip the alternative's value
   FieldType alt_field_type;
-  alt_field_type.type_id = type_info->type_id;
+  alt_field_type.set_type_id(type_info->type_id);
   alt_field_type.nullable = false;
   skip_field_value(ctx, alt_field_type, RefMode::None);
 }
diff --git a/cpp/fory/serialization/smart_ptr_serializer_test.cc b/cpp/fory/serialization/smart_ptr_serializer_test.cc
index 1c8b105f6..971ea2966 100644
--- a/cpp/fory/serialization/smart_ptr_serializer_test.cc
+++ b/cpp/fory/serialization/smart_ptr_serializer_test.cc
@@ -437,7 +437,7 @@ TEST(SmartPtrSerializerTest, MaxDynDepthDefault) {
 } // namespace
 
 // ============================================================================
-// Monomorphic field tests (fory::field<> style)
+// Monomorphic field tests
 // ============================================================================
 namespace {
 
@@ -450,21 +450,17 @@ struct PolymorphicBaseForMono {
   FORY_STRUCT(PolymorphicBaseForMono, value, data);
 };
 
-// Holder with non-dynamic field using fory::field<>
 struct NonDynamicFieldHolder {
-  // Field marked as dynamic<false> - no dynamic type dispatch, always
-  // PolymorphicBaseForMono
-  fory::field<std::shared_ptr<PolymorphicBaseForMono>, 0, fory::nullable,
-              fory::dynamic<false>>
-      ptr;
-  FORY_STRUCT(NonDynamicFieldHolder, ptr);
+  std::shared_ptr<PolymorphicBaseForMono> ptr;
+  FORY_STRUCT(NonDynamicFieldHolder,
+              (ptr, fory::F().nullable().dynamic(false)));
 };
 
-TEST(SmartPtrSerializerTest, NonDynamicFieldWithForyField) {
+TEST(SmartPtrSerializerTest, NonDynamicFieldConfig) {
   NonDynamicFieldHolder original;
-  original.ptr.value = std::make_shared<PolymorphicBaseForMono>();
-  original.ptr.value->value = 42;
-  original.ptr.value->data = "test data";
+  original.ptr = std::make_shared<PolymorphicBaseForMono>();
+  original.ptr->value = 42;
+  original.ptr->data = "test data";
 
   auto fory = Fory::builder().track_ref(false).build();
   fory.register_struct<NonDynamicFieldHolder>(400);
@@ -479,15 +475,15 @@ TEST(SmartPtrSerializerTest, NonDynamicFieldWithForyField) {
       << deserialize_result.error().to_string();
 
   auto deserialized = std::move(deserialize_result).value();
-  ASSERT_TRUE(deserialized.ptr.value);
-  EXPECT_EQ(deserialized.ptr.value->value, 42);
-  EXPECT_EQ(deserialized.ptr.value->data, "test data");
-  EXPECT_EQ(deserialized.ptr.value->name(), "PolymorphicBaseForMono");
+  ASSERT_TRUE(deserialized.ptr);
+  EXPECT_EQ(deserialized.ptr->value, 42);
+  EXPECT_EQ(deserialized.ptr->data, "test data");
+  EXPECT_EQ(deserialized.ptr->name(), "PolymorphicBaseForMono");
 }
 
 TEST(SmartPtrSerializerTest, NonDynamicFieldNullValue) {
   NonDynamicFieldHolder original;
-  original.ptr.value = nullptr;
+  original.ptr = nullptr;
 
   auto fory = Fory::builder().track_ref(false).build();
   fory.register_struct<NonDynamicFieldHolder>(404);
@@ -502,7 +498,7 @@ TEST(SmartPtrSerializerTest, NonDynamicFieldNullValue) {
       << deserialize_result.error().to_string();
 
   auto deserialized = std::move(deserialize_result).value();
-  EXPECT_FALSE(deserialized.ptr.value);
+  EXPECT_FALSE(deserialized.ptr);
 }
 
 } // namespace
diff --git a/cpp/fory/serialization/struct_serializer.h b/cpp/fory/serialization/struct_serializer.h
index cdd4a5f40..88a6de6d2 100644
--- a/cpp/fory/serialization/struct_serializer.h
+++ b/cpp/fory/serialization/struct_serializer.h
@@ -24,6 +24,8 @@
 #include "fory/meta/field_info.h"
 #include "fory/meta/preprocessor.h"
 #include "fory/meta/type_traits.h"
+#include "fory/serialization/collection_serializer.h"
+#include "fory/serialization/map_serializer.h"
 #include "fory/serialization/serializer.h"
 #include "fory/serialization/serializer_traits.h"
 #include "fory/serialization/skip.h"
@@ -34,6 +36,7 @@
 #include <limits>
 #include <memory>
 #include <numeric>
+#include <string>
 #include <string_view>
 #include <tuple>
 #include <type_traits>
@@ -476,6 +479,587 @@ void for_each_index(std::index_sequence<Indices...>, Func &&func) {
   (func(std::integral_constant<size_t, Indices>{}), ...);
 }
 
+template <typename StructT, size_t Index, int8_t NodeIndex = 0>
+constexpr FieldNodeKind configured_node_kind() {
+  constexpr auto spec =
+      ::fory::detail::GetFieldConfigEntry<StructT, Index>::spec;
+  return NodeIndex >= 0 ? spec.kind_[NodeIndex] : FieldNodeKind::Default;
+}
+
+template <typename StructT, size_t Index, int8_t NodeIndex = 0>
+constexpr Encoding configured_node_encoding() {
+  constexpr auto spec =
+      ::fory::detail::GetFieldConfigEntry<StructT, Index>::spec;
+  return NodeIndex >= 0 ? spec.encoding_[NodeIndex] : Encoding::Default;
+}
+
+template <typename StructT, size_t Index, int8_t NodeIndex = 0>
+constexpr bool configured_node_has_override() {
+  constexpr auto spec =
+      ::fory::detail::GetFieldConfigEntry<StructT, Index>::spec;
+  return NodeIndex >= 0 &&
+         (spec.kind_[NodeIndex] != FieldNodeKind::Default ||
+          spec.encoding_[NodeIndex] != Encoding::Default ||
+          spec.scalar_[NodeIndex] != FieldScalarKind::Inferred);
+}
+
+template <typename StructT, size_t Index, int8_t NodeIndex = 0>
+constexpr FieldScalarKind configured_node_scalar() {
+  constexpr auto spec =
+      ::fory::detail::GetFieldConfigEntry<StructT, Index>::spec;
+  return NodeIndex >= 0 ? spec.scalar_[NodeIndex] : FieldScalarKind::Inferred;
+}
+
+template <typename FieldType, FieldScalarKind ScalarKind>
+constexpr bool configured_scalar_kind_matches() {
+  using Decayed = decay_t<FieldType>;
+  if constexpr (ScalarKind == FieldScalarKind::Inferred) {
+    return true;
+  } else if constexpr (ScalarKind == FieldScalarKind::Bool) {
+    return std::is_same_v<Decayed, bool>;
+  } else if constexpr (ScalarKind == FieldScalarKind::Int8) {
+    return std::is_same_v<Decayed, int8_t>;
+  } else if constexpr (ScalarKind == FieldScalarKind::Int16) {
+    return std::is_same_v<Decayed, int16_t>;
+  } else if constexpr (ScalarKind == FieldScalarKind::Int32) {
+    return std::is_same_v<Decayed, int32_t> || std::is_same_v<Decayed, int>;
+  } else if constexpr (ScalarKind == FieldScalarKind::Int64) {
+    return std::is_same_v<Decayed, int64_t> ||
+           std::is_same_v<Decayed, long long>;
+  } else if constexpr (ScalarKind == FieldScalarKind::UInt8) {
+    return std::is_same_v<Decayed, uint8_t>;
+  } else if constexpr (ScalarKind == FieldScalarKind::UInt16) {
+    return std::is_same_v<Decayed, uint16_t>;
+  } else if constexpr (ScalarKind == FieldScalarKind::UInt32) {
+    return std::is_same_v<Decayed, uint32_t> ||
+           std::is_same_v<Decayed, unsigned int>;
+  } else if constexpr (ScalarKind == FieldScalarKind::UInt64) {
+    return std::is_same_v<Decayed, uint64_t> ||
+           std::is_same_v<Decayed, unsigned long long>;
+  } else if constexpr (ScalarKind == FieldScalarKind::Float16) {
+    return std::is_same_v<Decayed, float16_t>;
+  } else if constexpr (ScalarKind == FieldScalarKind::BFloat16) {
+    return std::is_same_v<Decayed, bfloat16_t>;
+  } else if constexpr (ScalarKind == FieldScalarKind::Float32) {
+    return std::is_same_v<Decayed, float>;
+  } else if constexpr (ScalarKind == FieldScalarKind::Float64) {
+    return std::is_same_v<Decayed, double>;
+  } else if constexpr (ScalarKind == FieldScalarKind::String) {
+    return std::is_same_v<Decayed, std::string>;
+  } else {
+    return false;
+  }
+}
+
+template <typename StructT, size_t Index, int8_t NodeIndex, int ChildSlot>
+constexpr int8_t configured_node_child() {
+  constexpr auto spec =
+      ::fory::detail::GetFieldConfigEntry<StructT, Index>::spec;
+  if constexpr (ChildSlot == 0) {
+    return NodeIndex >= 0 ? spec.child0_[NodeIndex] : -1;
+  } else {
+    return NodeIndex >= 0 ? spec.child1_[NodeIndex] : -1;
+  }
+}
+
+template <typename ValueType, typename StructT, size_t Index, int8_t NodeIndex>
+constexpr bool configured_vector_primitive_array_spec() {
+  if constexpr (!is_vector_v<ValueType>) {
+    return false;
+  } else {
+    using Element = element_type_t<ValueType>;
+    if constexpr (!std::is_same_v<decay_t<Element>, int8_t> &&
+                  !std::is_same_v<decay_t<Element>, uint8_t>) {
+      return false;
+    } else {
+      constexpr FieldNodeKind kind =
+          configured_node_kind<StructT, Index, NodeIndex>();
+      constexpr int8_t child =
+          configured_node_child<StructT, Index, NodeIndex, 0>();
+      if constexpr (kind != FieldNodeKind::List || child < 0) {
+        return false;
+      } else if constexpr (configured_node_kind<StructT, Index, child>() !=
+                               FieldNodeKind::Scalar ||
+                           configured_node_encoding<StructT, Index, child>() !=
+                               Encoding::Default) {
+        return false;
+      } else if constexpr (std::is_same_v<decay_t<Element>, int8_t>) {
+        return configured_node_scalar<StructT, Index, child>() ==
+               FieldScalarKind::Int8;
+      } else {
+        return configured_node_scalar<StructT, Index, child>() ==
+               FieldScalarKind::UInt8;
+      }
+    }
+  }
+}
+
+template <typename ValueType, typename StructT, size_t Index, int8_t NodeIndex>
+constexpr uint32_t configured_vector_primitive_array_type_id() {
+  if constexpr (!configured_vector_primitive_array_spec<ValueType, StructT,
+                                                        Index, NodeIndex>()) {
+    return 0;
+  } else {
+    using Element = element_type_t<ValueType>;
+    if constexpr (std::is_same_v<decay_t<Element>, int8_t>) {
+      return static_cast<uint32_t>(TypeId::INT8_ARRAY);
+    } else {
+      return static_cast<uint32_t>(TypeId::UINT8_ARRAY);
+    }
+  }
+}
+
+template <typename FieldType, typename StructT, size_t Index, int8_t NodeIndex>
+constexpr uint32_t configured_scalar_type_id() {
+  constexpr Encoding enc =
+      configured_node_encoding<StructT, Index, NodeIndex>();
+  using Decayed = decay_t<FieldType>;
+  if constexpr (std::is_same_v<Decayed, bool>) {
+    return static_cast<uint32_t>(TypeId::BOOL);
+  } else if constexpr (std::is_same_v<Decayed, int8_t>) {
+    return static_cast<uint32_t>(TypeId::INT8);
+  } else if constexpr (std::is_same_v<Decayed, uint8_t>) {
+    return static_cast<uint32_t>(TypeId::UINT8);
+  } else if constexpr (std::is_same_v<Decayed, int16_t>) {
+    return static_cast<uint32_t>(TypeId::INT16);
+  } else if constexpr (std::is_same_v<Decayed, uint16_t>) {
+    return static_cast<uint32_t>(TypeId::UINT16);
+  } else if constexpr (std::is_same_v<Decayed, int32_t> ||
+                       std::is_same_v<Decayed, int>) {
+    if constexpr (enc == Encoding::Fixed) {
+      return static_cast<uint32_t>(TypeId::INT32);
+    }
+    return static_cast<uint32_t>(TypeId::VARINT32);
+  } else if constexpr (std::is_same_v<Decayed, uint32_t> ||
+                       std::is_same_v<Decayed, unsigned int>) {
+    if constexpr (enc == Encoding::Varint) {
+      return static_cast<uint32_t>(TypeId::VAR_UINT32);
+    }
+    return static_cast<uint32_t>(TypeId::UINT32);
+  } else if constexpr (std::is_same_v<Decayed, int64_t> ||
+                       std::is_same_v<Decayed, long long>) {
+    if constexpr (enc == Encoding::Fixed) {
+      return static_cast<uint32_t>(TypeId::INT64);
+    } else if constexpr (enc == Encoding::Tagged) {
+      return static_cast<uint32_t>(TypeId::TAGGED_INT64);
+    }
+    return static_cast<uint32_t>(TypeId::VARINT64);
+  } else if constexpr (std::is_same_v<Decayed, uint64_t> ||
+                       std::is_same_v<Decayed, unsigned long long>) {
+    if constexpr (enc == Encoding::Varint) {
+      return static_cast<uint32_t>(TypeId::VAR_UINT64);
+    } else if constexpr (enc == Encoding::Tagged) {
+      return static_cast<uint32_t>(TypeId::TAGGED_UINT64);
+    }
+    return static_cast<uint32_t>(TypeId::UINT64);
+  } else if constexpr (std::is_same_v<Decayed, float16_t>) {
+    return static_cast<uint32_t>(TypeId::FLOAT16);
+  } else if constexpr (std::is_same_v<Decayed, bfloat16_t>) {
+    return static_cast<uint32_t>(TypeId::BFLOAT16);
+  } else if constexpr (std::is_same_v<Decayed, float>) {
+    return static_cast<uint32_t>(TypeId::FLOAT32);
+  } else if constexpr (std::is_same_v<Decayed, double>) {
+    return static_cast<uint32_t>(TypeId::FLOAT64);
+  } else if constexpr (std::is_same_v<Decayed, std::string>) {
+    return static_cast<uint32_t>(TypeId::STRING);
+  }
+  return 0;
+}
+
+template <typename FieldType, typename StructT, size_t Index, int8_t NodeIndex>
+constexpr uint32_t configured_effective_type_id() {
+  constexpr FieldNodeKind kind =
+      configured_node_kind<StructT, Index, NodeIndex>();
+  if constexpr (is_optional_v<FieldType>) {
+    using Inner = typename decay_t<FieldType>::value_type;
+    constexpr int8_t child =
+        kind == FieldNodeKind::Inner
+            ? configured_node_child<StructT, Index, NodeIndex, 0>()
+            : NodeIndex;
+    return configured_effective_type_id<Inner, StructT, Index, child>();
+  } else {
+    constexpr uint32_t vector_array_tid =
+        configured_vector_primitive_array_type_id<FieldType, StructT, Index,
+                                                  NodeIndex>();
+    if constexpr (vector_array_tid != 0) {
+      return vector_array_tid;
+    } else if constexpr (kind == FieldNodeKind::List) {
+      return static_cast<uint32_t>(TypeId::LIST);
+    } else if constexpr (kind == FieldNodeKind::Set) {
+      return static_cast<uint32_t>(TypeId::SET);
+    } else if constexpr (kind == FieldNodeKind::Map) {
+      return static_cast<uint32_t>(TypeId::MAP);
+    } else if constexpr (kind == FieldNodeKind::Scalar ||
+                         configured_node_encoding<StructT, Index,
+                                                  NodeIndex>() !=
+                             Encoding::Default) {
+      return configured_scalar_type_id<FieldType, StructT, Index, NodeIndex>();
+    }
+    return 0;
+  }
+}
+
+template <typename FieldType, typename StructT, size_t Index, int8_t NodeIndex>
+FORY_ALWAYS_INLINE void write_configured_scalar(const FieldType &value,
+                                                WriteContext &ctx) {
+  constexpr Encoding enc =
+      configured_node_encoding<StructT, Index, NodeIndex>();
+  if constexpr (is_configurable_int_v<FieldType>) {
+    if constexpr (std::is_same_v<FieldType, uint32_t>) {
+      if constexpr (enc == Encoding::Varint) {
+        ctx.write_var_uint32(value);
+      } else {
+        ctx.buffer().write_int32(static_cast<int32_t>(value));
+      }
+    } else if constexpr (std::is_same_v<FieldType, uint64_t>) {
+      if constexpr (enc == Encoding::Varint) {
+        ctx.write_var_uint64(value);
+      } else if constexpr (enc == Encoding::Tagged) {
+        ctx.write_tagged_uint64(value);
+      } else {
+        ctx.buffer().write_int64(static_cast<int64_t>(value));
+      }
+    } else if constexpr (std::is_same_v<FieldType, int32_t> ||
+                         std::is_same_v<FieldType, int>) {
+      if constexpr (enc == Encoding::Fixed) {
+        ctx.buffer().write_int32(static_cast<int32_t>(value));
+      } else {
+        ctx.write_var_int32(static_cast<int32_t>(value));
+      }
+    } else if constexpr (std::is_same_v<FieldType, int64_t> ||
+                         std::is_same_v<FieldType, long long>) {
+      if constexpr (enc == Encoding::Fixed) {
+        ctx.buffer().write_int64(static_cast<int64_t>(value));
+      } else if constexpr (enc == Encoding::Tagged) {
+        ctx.write_tagged_int64(static_cast<int64_t>(value));
+      } else {
+        ctx.write_var_int64(static_cast<int64_t>(value));
+      }
+    } else {
+      Serializer<FieldType>::write_data(value, ctx);
+    }
+  } else {
+    Serializer<FieldType>::write_data(value, ctx);
+  }
+}
+
+template <typename FieldType, typename StructT, size_t Index, int8_t NodeIndex>
+FORY_ALWAYS_INLINE FieldType read_configured_scalar(ReadContext &ctx) {
+  if constexpr (is_configurable_int_v<FieldType>) {
+    constexpr Encoding enc =
+        configured_node_encoding<StructT, Index, NodeIndex>();
+    if constexpr (std::is_same_v<FieldType, uint32_t>) {
+      if constexpr (enc == Encoding::Varint) {
+        return static_cast<FieldType>(ctx.read_var_uint32(ctx.error()));
+      }
+      return static_cast<FieldType>(ctx.read_int32(ctx.error()));
+    } else if constexpr (std::is_same_v<FieldType, uint64_t>) {
+      if constexpr (enc == Encoding::Varint) {
+        return static_cast<FieldType>(ctx.read_var_uint64(ctx.error()));
+      } else if constexpr (enc == Encoding::Tagged) {
+        return static_cast<FieldType>(ctx.read_tagged_uint64(ctx.error()));
+      }
+      return static_cast<FieldType>(ctx.read_uint64(ctx.error()));
+    } else if constexpr (std::is_same_v<FieldType, int32_t> ||
+                         std::is_same_v<FieldType, int>) {
+      if constexpr (enc == Encoding::Fixed) {
+        return static_cast<FieldType>(ctx.read_int32(ctx.error()));
+      }
+      return static_cast<FieldType>(ctx.read_var_int32(ctx.error()));
+    } else if constexpr (std::is_same_v<FieldType, int64_t> ||
+                         std::is_same_v<FieldType, long long>) {
+      if constexpr (enc == Encoding::Fixed) {
+        return static_cast<FieldType>(ctx.read_int64(ctx.error()));
+      } else if constexpr (enc == Encoding::Tagged) {
+        return static_cast<FieldType>(ctx.read_tagged_int64(ctx.error()));
+      }
+      return static_cast<FieldType>(ctx.read_var_int64(ctx.error()));
+    } else {
+      return Serializer<FieldType>::read_data(ctx);
+    }
+  } else {
+    return Serializer<FieldType>::read_data(ctx);
+  }
+}
+
+template <typename ValueType, typename StructT, size_t Index, int8_t NodeIndex>
+void write_configured_value(const ValueType &value, WriteContext &ctx,
+                            RefMode ref_mode, bool write_type,
+                            bool has_generics);
+
+template <typename ValueType, typename StructT, size_t Index, int8_t NodeIndex>
+ValueType read_configured_value(ReadContext &ctx, RefMode ref_mode,
+                                bool read_type);
+
+template <typename Container, typename StructT, size_t Index, int8_t NodeIndex,
+          int8_t ElemNode>
+void write_configured_list_data(const Container &coll, WriteContext &ctx) {
+  using Elem = element_type_t<Container>;
+  ctx.write_var_uint32(static_cast<uint32_t>(coll.size()));
+  if (coll.empty()) {
+    return;
+  }
+  ctx.write_uint8(COLL_DECL_ELEMENT_TYPE | COLL_IS_SAME_TYPE);
+  for (const auto &elem : coll) {
+    if constexpr (ElemNode >= 0) {
+      write_configured_value<Elem, StructT, Index, ElemNode>(
+          elem, ctx, RefMode::None, false, true);
+    } else {
+      Serializer<Elem>::write_data(elem, ctx);
+    }
+  }
+}
+
+template <typename Container, typename StructT, size_t Index, int8_t NodeIndex,
+          int8_t ElemNode>
+Container read_configured_list_data(ReadContext &ctx) {
+  using Elem = element_type_t<Container>;
+  uint32_t length = ctx.read_var_uint32(ctx.error());
+  Container result;
+  if constexpr (has_reserve_v<Container>) {
+    result.reserve(length);
+  }
+  if (FORY_PREDICT_FALSE(ctx.has_error()) || length == 0) {
+    return result;
+  }
+  uint8_t bitmap = ctx.read_uint8(ctx.error());
+  if (FORY_PREDICT_FALSE(ctx.has_error())) {
+    return result;
+  }
+  const bool is_decl_type = (bitmap & COLL_DECL_ELEMENT_TYPE) != 0;
+  const bool is_same_type = (bitmap & COLL_IS_SAME_TYPE) != 0;
+  if (is_same_type && !is_decl_type) {
+    (void)ctx.read_any_type_info(ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return result;
+    }
+  }
+  for (uint32_t i = 0; i < length; ++i) {
+    if constexpr (ElemNode >= 0) {
+      auto elem = read_configured_value<Elem, StructT, Index, ElemNode>(
+          ctx, RefMode::None, false);
+      collection_insert(result, std::move(elem));
+    } else {
+      auto elem = Serializer<Elem>::read_data(ctx);
+      collection_insert(result, std::move(elem));
+    }
+  }
+  return result;
+}
+
+template <typename MapType, typename StructT, size_t Index, int8_t KeyNode,
+          int8_t ValueNode>
+void write_configured_map_data(const MapType &map, WriteContext &ctx) {
+  using Key = key_type_t<MapType>;
+  using Value = mapped_type_t<MapType>;
+  ctx.write_var_uint32(static_cast<uint32_t>(map.size()));
+  if (map.empty()) {
+    return;
+  }
+  size_t header_offset = 0;
+  uint8_t pair_counter = 0;
+  bool need_write_header = true;
+  for (const auto &[key, value] : map) {
+    if (need_write_header) {
+      ctx.enter_flush_barrier();
+      header_offset = ctx.buffer().writer_index();
+      ctx.write_uint16(0);
+      ctx.buffer().unsafe_put_byte(
+          header_offset, static_cast<uint8_t>(DECL_KEY_TYPE | DECL_VALUE_TYPE));
+      need_write_header = false;
+    }
+    if constexpr (KeyNode >= 0) {
+      write_configured_value<Key, StructT, Index, KeyNode>(
+          key, ctx, RefMode::None, false, true);
+    } else {
+      Serializer<Key>::write_data(key, ctx);
+    }
+    if constexpr (ValueNode >= 0) {
+      write_configured_value<Value, StructT, Index, ValueNode>(
+          value, ctx, RefMode::None, false, true);
+    } else {
+      Serializer<Value>::write_data(value, ctx);
+    }
+    ++pair_counter;
+    if (pair_counter == MAX_CHUNK_SIZE) {
+      write_chunk_size(ctx, header_offset, pair_counter);
+      ctx.exit_flush_barrier();
+      ctx.try_flush();
+      pair_counter = 0;
+      need_write_header = true;
+    }
+  }
+  if (pair_counter > 0) {
+    write_chunk_size(ctx, header_offset, pair_counter);
+    ctx.exit_flush_barrier();
+    ctx.try_flush();
+  }
+}
+
+template <typename MapType, typename StructT, size_t Index, int8_t KeyNode,
+          int8_t ValueNode>
+MapType read_configured_map_data(ReadContext &ctx) {
+  using Key = key_type_t<MapType>;
+  using Value = mapped_type_t<MapType>;
+  uint32_t length = ctx.read_var_uint32(ctx.error());
+  MapType result;
+  MapReserver<MapType>::reserve(result, length);
+  uint32_t read_count = 0;
+  while (read_count < length && !ctx.has_error()) {
+    uint8_t header = ctx.read_uint8(ctx.error());
+    uint8_t chunk_size = ctx.read_uint8(ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return result;
+    }
+    const bool key_decl = (header & DECL_KEY_TYPE) != 0;
+    const bool value_decl = (header & DECL_VALUE_TYPE) != 0;
+    if (!key_decl) {
+      (void)ctx.read_any_type_info(ctx.error());
+    }
+    if (!value_decl) {
+      (void)ctx.read_any_type_info(ctx.error());
+    }
+    for (uint8_t i = 0; i < chunk_size && read_count < length; ++i) {
+      Key key = [&]() {
+        if constexpr (KeyNode >= 0) {
+          return read_configured_value<Key, StructT, Index, KeyNode>(
+              ctx, RefMode::None, false);
+        } else {
+          return Serializer<Key>::read_data(ctx);
+        }
+      }();
+      Value value = [&]() {
+        if constexpr (ValueNode >= 0) {
+          return read_configured_value<Value, StructT, Index, ValueNode>(
+              ctx, RefMode::None, false);
+        } else {
+          return Serializer<Value>::read_data(ctx);
+        }
+      }();
+      result.emplace(std::move(key), std::move(value));
+      ++read_count;
+    }
+  }
+  return result;
+}
+
+template <typename ValueType, typename StructT, size_t Index, int8_t NodeIndex>
+void write_configured_value(const ValueType &value, WriteContext &ctx,
+                            RefMode ref_mode, bool write_type,
+                            bool has_generics) {
+  constexpr FieldNodeKind kind =
+      configured_node_kind<StructT, Index, NodeIndex>();
+  constexpr FieldScalarKind scalar_kind =
+      configured_node_scalar<StructT, Index, NodeIndex>();
+  static_assert(configured_scalar_kind_matches<ValueType, scalar_kind>(),
+                "fory::T typed scalar spec does not match the C++ field type");
+  if constexpr (is_optional_v<ValueType>) {
+    using Inner = typename ValueType::value_type;
+    if (!value.has_value()) {
+      if (ref_mode != RefMode::None) {
+        ctx.write_int8(NULL_FLAG);
+      }
+      return;
+    }
+    write_not_null_ref_flag(ctx, ref_mode);
+    constexpr int8_t child =
+        kind == FieldNodeKind::Inner
+            ? configured_node_child<StructT, Index, NodeIndex, 0>()
+            : NodeIndex;
+    write_configured_value<Inner, StructT, Index, child>(
+        *value, ctx, RefMode::None, false, has_generics);
+  } else if constexpr ((is_vector_v<ValueType> || is_list_v<ValueType> ||
+                        is_deque_v<ValueType> || is_set_like_v<ValueType>) &&
+                       (kind == FieldNodeKind::List ||
+                        kind == FieldNodeKind::Set)) {
+    if constexpr (configured_vector_primitive_array_spec<ValueType, StructT,
+                                                         Index, NodeIndex>()) {
+      Serializer<ValueType>::write(value, ctx, ref_mode, false, has_generics);
+    } else {
+      write_not_null_ref_flag(ctx, ref_mode);
+      constexpr int8_t child =
+          configured_node_child<StructT, Index, NodeIndex, 0>();
+      write_configured_list_data<ValueType, StructT, Index, NodeIndex, child>(
+          value, ctx);
+    }
+  } else if constexpr (is_map_like_v<ValueType> && kind == FieldNodeKind::Map) {
+    write_not_null_ref_flag(ctx, ref_mode);
+    constexpr int8_t key_child =
+        configured_node_child<StructT, Index, NodeIndex, 0>();
+    constexpr int8_t value_child =
+        configured_node_child<StructT, Index, NodeIndex, 1>();
+    write_configured_map_data<ValueType, StructT, Index, key_child,
+                              value_child>(value, ctx);
+  } else if constexpr (kind == FieldNodeKind::Scalar ||
+                       configured_node_encoding<StructT, Index, NodeIndex>() !=
+                           Encoding::Default) {
+    write_not_null_ref_flag(ctx, ref_mode);
+    write_configured_scalar<ValueType, StructT, Index, NodeIndex>(value, ctx);
+  } else {
+    Serializer<ValueType>::write(value, ctx, ref_mode, write_type,
+                                 has_generics);
+  }
+}
+
+template <typename ValueType, typename StructT, size_t Index, int8_t NodeIndex>
+ValueType read_configured_value(ReadContext &ctx, RefMode ref_mode,
+                                bool read_type) {
+  constexpr FieldNodeKind kind =
+      configured_node_kind<StructT, Index, NodeIndex>();
+  constexpr FieldScalarKind scalar_kind =
+      configured_node_scalar<StructT, Index, NodeIndex>();
+  static_assert(configured_scalar_kind_matches<ValueType, scalar_kind>(),
+                "fory::T typed scalar spec does not match the C++ field type");
+  if constexpr (is_optional_v<ValueType>) {
+    using Inner = typename ValueType::value_type;
+    if (!read_null_only_flag(ctx, ref_mode)) {
+      return std::nullopt;
+    }
+    constexpr int8_t child =
+        kind == FieldNodeKind::Inner
+            ? configured_node_child<StructT, Index, NodeIndex, 0>()
+            : NodeIndex;
+    Inner inner = read_configured_value<Inner, StructT, Index, child>(
+        ctx, RefMode::None, false);
+    return ValueType{std::move(inner)};
+  } else if constexpr ((is_vector_v<ValueType> || is_list_v<ValueType> ||
+                        is_deque_v<ValueType> || is_set_like_v<ValueType>) &&
+                       (kind == FieldNodeKind::List ||
+                        kind == FieldNodeKind::Set)) {
+    if constexpr (configured_vector_primitive_array_spec<ValueType, StructT,
+                                                         Index, NodeIndex>()) {
+      return Serializer<ValueType>::read(ctx, ref_mode, false);
+    } else {
+      if (!read_null_only_flag(ctx, ref_mode)) {
+        return ValueType{};
+      }
+      constexpr int8_t child =
+          configured_node_child<StructT, Index, NodeIndex, 0>();
+      return read_configured_list_data<ValueType, StructT, Index, NodeIndex,
+                                       child>(ctx);
+    }
+  } else if constexpr (is_map_like_v<ValueType> && kind == FieldNodeKind::Map) {
+    if (!read_null_only_flag(ctx, ref_mode)) {
+      return ValueType{};
+    }
+    constexpr int8_t key_child =
+        configured_node_child<StructT, Index, NodeIndex, 0>();
+    constexpr int8_t value_child =
+        configured_node_child<StructT, Index, NodeIndex, 1>();
+    return read_configured_map_data<ValueType, StructT, Index, key_child,
+                                    value_child>(ctx);
+  } else if constexpr (kind == FieldNodeKind::Scalar ||
+                       configured_node_encoding<StructT, Index, NodeIndex>() !=
+                           Encoding::Default) {
+    if (!read_null_only_flag(ctx, ref_mode)) {
+      return ValueType{};
+    }
+    return read_configured_scalar<ValueType, StructT, Index, NodeIndex>(ctx);
+  } else {
+    return Serializer<ValueType>::read(ctx, ref_mode, read_type);
+  }
+}
+
 template <typename T, typename Func, size_t... Indices>
 void dispatch_field_index_impl(size_t target_index, Func &&func,
                                std::index_sequence<Indices...>, bool &handled) {
@@ -509,17 +1093,47 @@ template <typename T> struct CompileTimeFieldHelpers {
   static inline constexpr auto ptrs = FieldDescriptor::ptrs();
   using FieldPtrs = decltype(ptrs);
 
+  template <size_t... Indices>
+  static constexpr bool any_field_has_id(std::index_sequence<Indices...>) {
+    if constexpr (sizeof...(Indices) == 0) {
+      return false;
+    } else {
+      return ((::fory::detail::GetFieldConfigEntry<T, Indices>::has_id) || ...);
+    }
+  }
+
+  template <size_t... Indices>
+  static constexpr bool all_fields_have_id(std::index_sequence<Indices...>) {
+    if constexpr (sizeof...(Indices) == 0) {
+      return true;
+    } else {
+      return ((::fory::detail::GetFieldConfigEntry<T, Indices>::has_id) && ...);
+    }
+  }
+
+  static constexpr bool any_id_mode_field =
+      any_field_has_id(std::make_index_sequence<FieldCount>{});
+  static constexpr bool all_id_mode_fields =
+      all_fields_have_id(std::make_index_sequence<FieldCount>{});
+
+  static_assert(!any_id_mode_field || all_id_mode_fields,
+                "FORY_STRUCT must use exactly one identity mode: either all "
+                "fields use fory::F(id), or no fields use ids.");
+
   template <size_t Index> static constexpr uint32_t field_type_id() {
     if constexpr (FieldCount == 0) {
       return 0;
     } else {
       using PtrT = std::tuple_element_t<Index, FieldPtrs>;
       using RawFieldType = meta::RemoveMemberPointerCVRefT<PtrT>;
-      // unwrap fory::field<> to get the actual type for serialization
       using FieldType = unwrap_field_t<RawFieldType>;
 
-      // Check for encoding override from FORY_FIELD_CONFIG
       if constexpr (::fory::detail::has_field_config_v<T>) {
+        constexpr uint32_t effective_tid =
+            configured_effective_type_id<FieldType, T, Index, 0>();
+        if constexpr (effective_tid != 0) {
+          return effective_tid;
+        }
         constexpr uint32_t unsigned_tid =
             compute_unsigned_type_id<FieldType, T, Index>();
         if constexpr (unsigned_tid != 0) {
@@ -541,11 +1155,8 @@ template <typename T> struct CompileTimeFieldHelpers {
   }
 
   /// Returns true if the field at Index is nullable for fingerprint
-  /// computation. This checks:
-  /// 1. If the field is a fory::field<>, use its is_nullable metadata
-  /// 2. Else if FORY_FIELD_TAGS is defined, use that metadata
-  /// 3. Otherwise, use xlang defaults: only std::optional is nullable
-  ///    (For xlang: nullable=false by default, except for Optional types)
+  /// computation. Configured fields may opt in to nullable behavior; otherwise
+  /// xlang defaults make only std::optional nullable.
   template <size_t Index> static constexpr bool field_nullable() {
     if constexpr (FieldCount == 0) {
       return false;
@@ -553,19 +1164,9 @@ template <typename T> struct CompileTimeFieldHelpers {
       using PtrT = std::tuple_element_t<Index, FieldPtrs>;
       using RawFieldType = meta::RemoveMemberPointerCVRefT<PtrT>;
 
-      // If it's a fory::field<> wrapper, use its metadata
       if constexpr (is_fory_field_v<RawFieldType>) {
         return RawFieldType::is_nullable;
-      }
-      // Else if FORY_FIELD_TAGS is defined, use that metadata
-      else if constexpr (::fory::detail::has_field_tags_v<T>) {
-        if constexpr (::fory::detail::GetFieldTagEntry<T, Index>::has_entry) {
-          return ::fory::detail::GetFieldTagEntry<T, Index>::is_nullable;
-        }
-        return field_is_nullable_v<RawFieldType>;
-      }
-      // Else if FORY_FIELD_CONFIG is defined, use nullable from config
-      else if constexpr (::fory::detail::has_field_config_v<T>) {
+      } else if constexpr (::fory::detail::has_field_config_v<T>) {
         if constexpr (::fory::detail::GetFieldConfigEntry<T,
                                                           Index>::has_entry &&
                       ::fory::detail::GetFieldConfigEntry<T, Index>::nullable) {
@@ -599,14 +1200,9 @@ template <typename T> struct CompileTimeFieldHelpers {
           return config_id;
         }
       }
-      // If it's a fory::field<> wrapper, use its tag_id
       if constexpr (is_fory_field_v<RawFieldType>) {
         return RawFieldType::tag_id;
       }
-      // Else if FORY_FIELD_TAGS is defined, use that metadata
-      else if constexpr (::fory::detail::has_field_tags_v<T>) {
-        return ::fory::detail::GetFieldTagEntry<T, Index>::id;
-      }
       // No tag ID defined
       else {
         return -1;
@@ -633,16 +1229,9 @@ template <typename T> struct CompileTimeFieldHelpers {
       using PtrT = std::tuple_element_t<Index, FieldPtrs>;
       using RawFieldType = meta::RemoveMemberPointerCVRefT<PtrT>;
 
-      // If it's a fory::field<> wrapper, use its track_ref metadata
       if constexpr (is_fory_field_v<RawFieldType>) {
         return RawFieldType::track_ref;
-      }
-      // Else if FORY_FIELD_TAGS is defined, use that metadata
-      else if constexpr (::fory::detail::has_field_tags_v<T>) {
-        return ::fory::detail::GetFieldTagEntry<T, Index>::track_ref;
-      }
-      // Else if FORY_FIELD_CONFIG is defined, use ref from config
-      else if constexpr (::fory::detail::has_field_config_v<T>) {
+      } else if constexpr (::fory::detail::has_field_config_v<T>) {
         if constexpr (::fory::detail::GetFieldConfigEntry<T,
                                                           Index>::has_entry &&
                       ::fory::detail::GetFieldConfigEntry<T, Index>::ref) {
@@ -668,16 +1257,9 @@ template <typename T> struct CompileTimeFieldHelpers {
       using PtrT = std::tuple_element_t<Index, FieldPtrs>;
       using RawFieldType = meta::RemoveMemberPointerCVRefT<PtrT>;
 
-      // If it's a fory::field<> wrapper, use its dynamic_value metadata
       if constexpr (is_fory_field_v<RawFieldType>) {
         return RawFieldType::dynamic_value;
-      }
-      // Else if FORY_FIELD_TAGS is defined, use that metadata
-      else if constexpr (::fory::detail::has_field_tags_v<T>) {
-        return ::fory::detail::GetFieldTagEntry<T, Index>::dynamic_value;
-      }
-      // Else if FORY_FIELD_CONFIG is defined, use dynamic_value from config
-      else if constexpr (::fory::detail::has_field_config_v<T>) {
+      } else if constexpr (::fory::detail::has_field_config_v<T>) {
         constexpr int dynamic_value =
             ::fory::detail::GetFieldConfigEntry<T, Index>::dynamic_value;
         if constexpr (dynamic_value != -1) {
@@ -733,7 +1315,7 @@ template <typename T> struct CompileTimeFieldHelpers {
       !any_field_needs_type_info_in_compatible(
           std::make_index_sequence<FieldCount>{});
 
-  /// get the underlying field type (unwraps fory::field<> if present)
+  /// get the underlying field type.
   template <size_t Index> struct UnwrappedFieldTypeHelper {
     using PtrT = std::tuple_element_t<Index, FieldPtrs>;
     using RawFieldType = meta::RemoveMemberPointerCVRefT<PtrT>;
@@ -1635,7 +2217,7 @@ FORY_ALWAYS_INLINE void write_single_fixed_field(const T &obj, Buffer &buffer,
   using RawFieldType =
       typename meta::RemoveMemberPointerCVRefT<decltype(field_ptr)>;
   using FieldType = unwrap_field_t<RawFieldType>;
-  // get the actual value (unwrap fory::field<> if needed)
+  // get the actual field value.
   const FieldType &field_value = [&]() -> const FieldType & {
     if constexpr (is_fory_field_v<RawFieldType>) {
       return (obj.*field_ptr).value;
@@ -1677,7 +2259,7 @@ FORY_ALWAYS_INLINE void write_single_varint_field(const T &obj, Buffer &buffer,
   using RawFieldType =
       typename meta::RemoveMemberPointerCVRefT<decltype(field_ptr)>;
   using FieldType = unwrap_field_t<RawFieldType>;
-  // get the actual value (unwrap fory::field<> if needed)
+  // get the actual field value.
   const FieldType &field_value = [&]() -> const FieldType & {
     if constexpr (is_fory_field_v<RawFieldType>) {
       return (obj.*field_ptr).value;
@@ -1720,7 +2302,7 @@ write_single_remaining_field(const T &obj, Buffer &buffer, uint32_t &offset) {
   using RawFieldType =
       typename meta::RemoveMemberPointerCVRefT<decltype(field_ptr)>;
   using FieldType = unwrap_field_t<RawFieldType>;
-  // get the actual value (unwrap fory::field<> if needed)
+  // get the actual field value.
   const FieldType &field_value = [&]() -> const FieldType & {
     if constexpr (is_fory_field_v<RawFieldType>) {
       return (obj.*field_ptr).value;
@@ -1803,10 +2385,9 @@ void write_single_field(const T &obj, WriteContext &ctx,
   const auto field_ptr = std::get<Index>(field_ptrs);
   using RawFieldType =
       typename meta::RemoveMemberPointerCVRefT<decltype(field_ptr)>;
-  // unwrap fory::field<> to get the actual type for serialization
   using FieldType = unwrap_field_t<RawFieldType>;
 
-  // get the actual value (unwrap fory::field<> if needed)
+  // get the actual field value.
   const auto &raw_field_ref = obj.*field_ptr;
   const FieldType &field_value = [&]() -> const FieldType & {
     if constexpr (is_fory_field_v<RawFieldType>) {
@@ -1819,12 +2400,28 @@ void write_single_field(const T &obj, WriteContext &ctx,
   constexpr TypeId field_type_id = Serializer<FieldType>::type_id;
   constexpr bool is_primitive_field = is_primitive_type_id(field_type_id);
 
-  // get field metadata from fory::field<> or FORY_FIELD_TAGS or defaults
+  // get field metadata from the effective field spec or defaults.
   constexpr bool is_nullable = Helpers::template field_nullable<Index>();
   constexpr bool track_ref = Helpers::template field_track_ref<Index>();
   // Some wrapper types always require ref/null flags in the wire format.
   constexpr bool field_type_is_nullable = is_nullable_v<FieldType>;
 
+  if constexpr (configured_node_has_override<T, Index>()) {
+    constexpr RefMode field_ref_mode =
+        make_ref_mode(is_nullable || field_type_is_nullable, track_ref);
+    constexpr bool is_struct = is_struct_type(field_type_id);
+    constexpr bool is_ext = is_ext_type(field_type_id);
+    constexpr bool is_polymorphic = field_type_id == TypeId::UNKNOWN;
+    constexpr int dynamic_val = Helpers::template field_dynamic_value<Index>();
+    constexpr bool polymorphic_write_type =
+        (dynamic_val == 1) || (dynamic_val == -1 && is_polymorphic);
+    bool write_type = polymorphic_write_type ||
+                      ((is_struct || is_ext) && ctx.is_compatible());
+    write_configured_value<FieldType, T, Index, 0>(
+        field_value, ctx, field_ref_mode, write_type, has_generics);
+    return;
+  }
+
   // Special handling for std::optional<uint32_t/uint64_t> with encoding config
   // This must come BEFORE the general primitive check because optional requires
   // ref metadata but we want to use encoding-specific serialization.
@@ -2229,7 +2826,6 @@ void read_single_field_by_index(T &obj, ReadContext &ctx) {
   const auto field_ptr = std::get<Index>(field_ptrs);
   using RawFieldType =
       typename meta::RemoveMemberPointerCVRefT<decltype(field_ptr)>;
-  // unwrap fory::field<> to get the actual type for deserialization
   using FieldType = unwrap_field_t<RawFieldType>;
 
   // In non-compatible mode, no type info for fields except for polymorphic
@@ -2257,7 +2853,7 @@ void read_single_field_by_index(T &obj, ReadContext &ctx) {
                    (dynamic_val == -1 && is_polymorphic_field) ||
                    ((is_struct_field || is_ext_field) && ctx.is_compatible());
 
-  // get field metadata from fory::field<> or FORY_FIELD_TAGS or defaults
+  // get field metadata from the effective field spec or defaults.
   constexpr bool is_nullable = Helpers::template field_nullable<Index>();
   constexpr bool track_ref = Helpers::template field_track_ref<Index>();
 
@@ -2271,6 +2867,18 @@ void read_single_field_by_index(T &obj, ReadContext &ctx) {
   // Per xlang protocol: non-nullable fields skip ref flag entirely
   constexpr RefMode field_ref_mode =
       make_ref_mode(is_nullable || field_type_is_nullable, track_ref);
+
+  if constexpr (configured_node_has_override<T, Index>()) {
+    FieldType result = read_configured_value<FieldType, T, Index, 0>(
+        ctx, field_ref_mode, read_type);
+    if constexpr (is_fory_field_v<RawFieldType>) {
+      (obj.*field_ptr).value = std::move(result);
+    } else {
+      obj.*field_ptr = std::move(result);
+    }
+    return;
+  }
+
   // OPTIMIZATION: For raw primitive fields (not wrappers like optional,
   // shared_ptr) that don't need ref metadata, bypass Serializer<T>::read
   // and use direct buffer reads with Error&.
@@ -2283,7 +2891,7 @@ void read_single_field_by_index(T &obj, ReadContext &ctx) {
       }
       return read_primitive_field_direct<FieldType>(ctx, ctx.error());
     };
-    // Assign to field (handle fory::field<> wrapper if needed)
+    // Assign to field.
     if constexpr (is_fory_field_v<RawFieldType>) {
       (obj.*field_ptr).value = read_value();
     } else {
@@ -2382,7 +2990,7 @@ void read_single_field_by_index(T &obj, ReadContext &ctx) {
         obj.*field_ptr = std::optional<InnerType>(value);
       }
     } else {
-      // Assign to field (handle fory::field<> wrapper if needed)
+      // Assign to field.
       FieldType result =
           Serializer<FieldType>::read(ctx, field_ref_mode, read_type);
       if constexpr (is_fory_field_v<RawFieldType>) {
@@ -2396,19 +3004,19 @@ void read_single_field_by_index(T &obj, ReadContext &ctx) {
 
 /// Helper to read a single field by index in compatible mode using
 /// remote field metadata to decide reference flag presence.
-/// @param remote_type_id The type_id from the remote schema (for encoding)
+/// @param remote_field_type The field type tree from the remote schema.
 template <size_t Index, typename T>
 void read_single_field_by_index_compatible(T &obj, ReadContext &ctx,
-                                           RefMode remote_ref_mode,
-                                           uint32_t remote_type_id) {
+                                           const FieldType &remote_field_type) {
   using Helpers = CompileTimeFieldHelpers<T>;
   const auto field_info = fory_field_info(obj);
   const auto &field_ptrs = decltype(field_info)::ptrs_ref();
   const auto field_ptr = std::get<Index>(field_ptrs);
   using RawFieldType =
       typename meta::RemoveMemberPointerCVRefT<decltype(field_ptr)>;
-  // unwrap fory::field<> to get the actual type for deserialization
   using FieldType = unwrap_field_t<RawFieldType>;
+  const RefMode remote_ref_mode = remote_field_type.ref_mode;
+  const uint32_t remote_type_id = remote_field_type.type_id;
 
   constexpr TypeId field_type_id = Serializer<FieldType>::type_id;
   // Check if field is a struct type - use type_id to handle shared_ptr<Struct>
@@ -2527,6 +3135,17 @@ void read_single_field_by_index_compatible(T &obj, ReadContext &ctx,
     }
   }
 
+  if constexpr (configured_node_has_override<T, Index>()) {
+    FieldType result = read_configured_value<FieldType, T, Index, 0>(
+        ctx, remote_ref_mode, read_type);
+    if constexpr (is_fory_field_v<RawFieldType>) {
+      (obj.*field_ptr).value = std::move(result);
+    } else {
+      obj.*field_ptr = std::move(result);
+    }
+    return;
+  }
+
   // For non-primitive types, use the standard serializer path
   FieldType result =
       Serializer<FieldType>::read(ctx, remote_ref_mode, read_type);
@@ -2540,11 +3159,13 @@ void read_single_field_by_index_compatible(T &obj, ReadContext &ctx,
 /// Helper to dispatch field reading by field_id in compatible mode.
 /// Uses fold expression with short-circuit to avoid lambda overhead.
 /// Sets handled=true if field was matched.
-/// @param remote_type_id The type_id from the remote schema (for encoding)
+/// @param remote_field_type The field type tree from the remote schema.
 template <typename T, size_t... Indices>
-FORY_ALWAYS_INLINE void dispatch_compatible_field_read_impl(
-    T &obj, ReadContext &ctx, int16_t field_id, RefMode remote_ref_mode,
-    uint32_t remote_type_id, bool &handled, std::index_sequence<Indices...>) {
+FORY_ALWAYS_INLINE void
+dispatch_compatible_field_read_impl(T &obj, ReadContext &ctx, int16_t field_id,
+                                    const FieldType &remote_field_type,
+                                    bool &handled,
+                                    std::index_sequence<Indices...>) {
   using Helpers = CompileTimeFieldHelpers<T>;
 
   // Short-circuit fold: stops at first match
@@ -2552,8 +3173,8 @@ FORY_ALWAYS_INLINE void dispatch_compatible_field_read_impl(
   (void)((static_cast<int16_t>(Indices) == field_id
               ? (handled = true,
                  read_single_field_by_index_compatible<
-                     Helpers::sorted_indices[Indices]>(
-                     obj, ctx, remote_ref_mode, remote_type_id),
+                     Helpers::sorted_indices[Indices]>(obj, ctx,
+                                                       remote_field_type),
                  true)
               : false) ||
          ...);
@@ -2667,7 +3288,7 @@ FORY_ALWAYS_INLINE void read_single_fixed_field(T &obj, Buffer &buffer,
   using FieldType = unwrap_field_t<RawFieldType>;
   FieldType result =
       read_fixed_primitive_at<FieldType>(buffer, base_offset + field_offset);
-  // Assign to field (handle fory::field<> wrapper if needed)
+  // Assign to field.
   if constexpr (is_fory_field_v<RawFieldType>) {
     (obj.*field_ptr).value = result;
   } else {
@@ -2753,7 +3374,7 @@ FORY_ALWAYS_INLINE void read_single_varint_field(T &obj, Buffer &buffer,
     result = read_varint_at<FieldType>(buffer, offset);
   }
 
-  // Assign to field (handle fory::field<> wrapper if needed)
+  // Assign to field.
   if constexpr (is_fory_field_v<RawFieldType>) {
     (obj.*field_ptr).value = result;
   } else {
@@ -2926,11 +3547,11 @@ void read_struct_fields_compatible(T &obj, ReadContext &ctx,
 
     // Dispatch to the correct local field by field_id
     // Uses fold expression with short-circuit - no lambda overhead
-    // Pass remote type_id for correct encoding in compatible mode
+    // Pass remote field type for correct encoding and ref metadata.
     bool handled = false;
-    dispatch_compatible_field_read_impl<T>(
-        obj, ctx, field_id, remote_ref_mode, remote_field.field_type.type_id,
-        handled, std::index_sequence<Indices...>{});
+    dispatch_compatible_field_read_impl<T>(obj, ctx, field_id,
+                                           remote_field.field_type, handled,
+                                           std::index_sequence<Indices...>{});
 
     if (!handled) {
       // Shouldn't happen if TypeMeta::assign_field_ids worked correctly
diff --git a/cpp/fory/serialization/struct_test.cc b/cpp/fory/serialization/struct_test.cc
index 7330ee6bc..83ab3e0c0 100644
--- a/cpp/fory/serialization/struct_test.cc
+++ b/cpp/fory/serialization/struct_test.cc
@@ -88,24 +88,10 @@ struct FieldConfigTaggedStruct {
   bool operator==(const FieldConfigTaggedStruct &other) const {
     return a == other.a && b == other.b && c == other.c;
   }
-  FORY_STRUCT(FieldConfigTaggedStruct, a, b, c);
+  FORY_STRUCT(FieldConfigTaggedStruct, (a, fory::F(1)), (b, fory::F(2)),
+              (c, fory::F(3)));
 };
 
-struct FieldTagsTaggedStruct {
-  int32_t a;
-  int64_t b;
-  std::string c;
-
-  bool operator==(const FieldTagsTaggedStruct &other) const {
-    return a == other.a && b == other.b && c == other.c;
-  }
-  FORY_STRUCT(FieldTagsTaggedStruct, a, b, c);
-};
-
-FORY_FIELD_CONFIG(FieldConfigTaggedStruct, (a, fory::F().id(1)),
-                  (b, fory::F().id(2)), (c, fory::F().id(3)));
-FORY_FIELD_TAGS(FieldTagsTaggedStruct, (a, 1), (b, 2), (c, 3));
-
 class PrivateFieldsStruct {
 public:
   PrivateFieldsStruct() = default;
@@ -242,6 +228,15 @@ struct VectorStruct {
   FORY_STRUCT(VectorStruct, numbers, strings, points);
 };
 
+struct VectorBoolStruct {
+  std::vector<bool> flags;
+
+  bool operator==(const VectorBoolStruct &other) const {
+    return flags == other.flags;
+  }
+  FORY_STRUCT(VectorBoolStruct, flags);
+};
+
 struct NamedItem {
   int32_t id;
   std::string name;
@@ -274,6 +269,41 @@ struct NestedContainerStruct {
   FORY_STRUCT(NestedContainerStruct, matrix, grouped_numbers);
 };
 
+namespace T = fory::T;
+
+struct NestedAnnotatedStruct {
+  std::map<uint32_t, std::vector<int64_t>> map;
+
+  bool operator==(const NestedAnnotatedStruct &other) const {
+    return map == other.map;
+  }
+  FORY_STRUCT(NestedAnnotatedStruct,
+              (map,
+               fory::F().map().key(T::varint()).value(T::list(T::tagged()))));
+};
+
+struct PartialMapAnnotatedStruct {
+  std::map<uint32_t, std::vector<int64_t>> key_only;
+  std::map<uint32_t, std::vector<int64_t>> value_only;
+
+  bool operator==(const PartialMapAnnotatedStruct &other) const {
+    return key_only == other.key_only && value_only == other.value_only;
+  }
+  FORY_STRUCT(PartialMapAnnotatedStruct,
+              (key_only, fory::F().map().key(T::varint())),
+              (value_only, fory::F().map().value(T::list(T::tagged()))));
+};
+
+struct OptionalNestedAnnotatedStruct {
+  std::optional<std::vector<uint32_t>> values;
+
+  bool operator==(const OptionalNestedAnnotatedStruct &other) const {
+    return values == other.values;
+  }
+  FORY_STRUCT(OptionalNestedAnnotatedStruct,
+              (values, fory::F().inner(T::list(T::varint()))));
+};
+
 // Optional fields
 struct OptionalFieldsStruct {
   std::string name;
@@ -439,8 +469,12 @@ inline void register_all_test_types(Fory &fory) {
   fory.register_struct<BoundingBox>(type_id++);
   fory.register_struct<Scene>(type_id++);
   fory.register_struct<VectorStruct>(type_id++);
+  fory.register_struct<VectorBoolStruct>(type_id++);
   fory.register_struct<MapStruct>(type_id++);
   fory.register_struct<NestedContainerStruct>(type_id++);
+  fory.register_struct<NestedAnnotatedStruct>(type_id++);
+  fory.register_struct<PartialMapAnnotatedStruct>(type_id++);
+  fory.register_struct<OptionalNestedAnnotatedStruct>(type_id++);
   fory.register_struct<OptionalFieldsStruct>(type_id++);
   fory.register_struct<EnumStruct>(type_id++);
   fory.register_struct<UserProfile>(type_id++);
@@ -455,6 +489,19 @@ inline void register_all_test_types(Fory &fory) {
   fory.register_enum<Status>(type_id++);
 }
 
+inline FieldType make_test_field_type(TypeId type_id,
+                                      std::vector<FieldType> generics = {}) {
+  return FieldType(static_cast<uint32_t>(type_id), false, false,
+                   std::move(generics));
+}
+
+inline FieldInfo make_test_field_info(std::string name, int16_t field_id,
+                                      FieldType field_type) {
+  FieldInfo info(std::move(name), std::move(field_type));
+  info.field_id = field_id;
+  return info;
+}
+
 template <typename T> void test_roundtrip(const T &original) {
   auto fory = Fory::builder().xlang(true).track_ref(false).build();
   register_all_test_types(fory);
@@ -500,28 +547,6 @@ TEST(StructComprehensiveTest, ManyFieldsStruct) {
                                   -9223372036854775807LL - 1, -1.0f, -1.0, ""});
 }
 
-TEST(StructComprehensiveTest, FieldTagsMatchFieldConfigSize) {
-  FieldConfigTaggedStruct config_obj{1, 2, "config"};
-  FieldTagsTaggedStruct tags_obj{1, 2, "config"};
-
-  auto fory_config =
-      Fory::builder().xlang(true).compatible(true).track_ref(false).build();
-  auto fory_tags =
-      Fory::builder().xlang(true).compatible(true).track_ref(false).build();
-
-  ASSERT_TRUE(fory_config.register_struct<FieldConfigTaggedStruct>(101).ok());
-  ASSERT_TRUE(fory_tags.register_struct<FieldTagsTaggedStruct>(101).ok());
-
-  auto config_bytes = fory_config.serialize(config_obj);
-  ASSERT_TRUE(config_bytes.ok())
-      << "Serialization failed: " << config_bytes.error().to_string();
-  auto tags_bytes = fory_tags.serialize(tags_obj);
-  ASSERT_TRUE(tags_bytes.ok())
-      << "Serialization failed: " << tags_bytes.error().to_string();
-
-  EXPECT_EQ(config_bytes->size(), tags_bytes->size());
-}
-
 TEST(StructComprehensiveTest, PrivateFieldsStruct) {
   test_roundtrip(PrivateFieldsStruct{42, "secret", {1, 2, 3}});
 }
@@ -582,6 +607,10 @@ TEST(StructComprehensiveTest, VectorStructMultiple) {
   test_roundtrip(VectorStruct{{1, 2, 3}, {"foo", "bar"}, {{0, 0}, {10, 10}}});
 }
 
+TEST(StructComprehensiveTest, VectorBoolStruct) {
+  test_roundtrip(VectorBoolStruct{{true, false, false, true}});
+}
+
 TEST(StructComprehensiveTest, NamedStructElementTypeInfo) {
   std::vector<NamedItem> items{{1, "alpha"}, {2, "beta"}};
 
@@ -615,6 +644,149 @@ TEST(StructComprehensiveTest, NestedContainers) {
   test_roundtrip(NestedContainerStruct{{{1, 2}, {3, 4}}, {{"a", {10, 20}}}});
 }
 
+TEST(StructComprehensiveTest, NestedAnnotatedContainers) {
+  NestedAnnotatedStruct obj{{{1, {10, -20, 30}}, {2, {40, 50}}}};
+  test_roundtrip(obj);
+
+  auto fory =
+      Fory::builder().xlang(true).compatible(true).track_ref(false).build();
+  ASSERT_TRUE(fory.register_struct<NestedAnnotatedStruct>(601).ok());
+  ASSERT_TRUE(fory.serialize(obj).ok());
+  TypeMeta meta =
+      fory.type_resolver().clone_struct_meta<NestedAnnotatedStruct>();
+  const auto &fields = meta.get_field_infos();
+  ASSERT_EQ(fields.size(), 1);
+  const auto &field_type = fields[0].field_type;
+  ASSERT_EQ(field_type.type_id, static_cast<uint32_t>(TypeId::MAP));
+  ASSERT_EQ(field_type.generics.size(), 2);
+  EXPECT_EQ(field_type.generics[0].type_id,
+            static_cast<uint32_t>(TypeId::VAR_UINT32));
+  ASSERT_EQ(field_type.generics[1].type_id,
+            static_cast<uint32_t>(TypeId::LIST));
+  ASSERT_EQ(field_type.generics[1].generics.size(), 1);
+  EXPECT_EQ(field_type.generics[1].generics[0].type_id,
+            static_cast<uint32_t>(TypeId::TAGGED_INT64));
+}
+
+TEST(StructComprehensiveTest, PartialMapAnnotations) {
+  PartialMapAnnotatedStruct obj{
+      {{1, {10, 20}}, {2, {30}}},
+      {{3, {40, -50}}, {4, {60}}},
+  };
+  test_roundtrip(obj);
+
+  auto fory =
+      Fory::builder().xlang(true).compatible(true).track_ref(false).build();
+  ASSERT_TRUE(fory.register_struct<PartialMapAnnotatedStruct>(602).ok());
+  ASSERT_TRUE(fory.serialize(obj).ok());
+  TypeMeta meta =
+      fory.type_resolver().clone_struct_meta<PartialMapAnnotatedStruct>();
+  const auto &fields = meta.get_field_infos();
+  ASSERT_EQ(fields.size(), 2);
+  const FieldInfo *key_only = nullptr;
+  const FieldInfo *value_only = nullptr;
+  for (const auto &field : fields) {
+    if (field.field_name == "key_only") {
+      key_only = &field;
+    } else if (field.field_name == "value_only") {
+      value_only = &field;
+    }
+  }
+  ASSERT_NE(key_only, nullptr);
+  ASSERT_NE(value_only, nullptr);
+  EXPECT_EQ(key_only->field_type.generics[0].type_id,
+            static_cast<uint32_t>(TypeId::VAR_UINT32));
+  EXPECT_EQ(value_only->field_type.generics[1].generics[0].type_id,
+            static_cast<uint32_t>(TypeId::TAGGED_INT64));
+}
+
+TEST(StructComprehensiveTest, OptionalNestedAnnotation) {
+  test_roundtrip(
+      OptionalNestedAnnotatedStruct{{std::vector<uint32_t>{1, 2, 3}}});
+  test_roundtrip(OptionalNestedAnnotatedStruct{std::nullopt});
+
+  auto fory =
+      Fory::builder().xlang(true).compatible(true).track_ref(false).build();
+  ASSERT_TRUE(fory.register_struct<OptionalNestedAnnotatedStruct>(603).ok());
+  ASSERT_TRUE(
+      fory.serialize(OptionalNestedAnnotatedStruct{{std::vector<uint32_t>{1}}})
+          .ok());
+  TypeMeta meta =
+      fory.type_resolver().clone_struct_meta<OptionalNestedAnnotatedStruct>();
+  const auto &fields = meta.get_field_infos();
+  ASSERT_EQ(fields.size(), 1);
+  EXPECT_TRUE(fields[0].field_type.nullable);
+  ASSERT_EQ(fields[0].field_type.type_id, static_cast<uint32_t>(TypeId::LIST));
+  ASSERT_EQ(fields[0].field_type.generics.size(), 1);
+  EXPECT_EQ(fields[0].field_type.generics[0].type_id,
+            static_cast<uint32_t>(TypeId::VAR_UINT32));
+}
+
+TEST(StructComprehensiveTest,
+     FieldTypeCompatibleFingerprintNormalizesEncoding) {
+  FieldType fixed_i32 = make_test_field_type(TypeId::INT32);
+  FieldType var_i32 = make_test_field_type(TypeId::VARINT32);
+  EXPECT_TRUE(field_types_compatible(fixed_i32, var_i32));
+  EXPECT_EQ(fixed_i32.compatible_fingerprint, var_i32.compatible_fingerprint);
+
+  FieldType fixed_list =
+      make_test_field_type(TypeId::LIST, {make_test_field_type(TypeId::INT32)});
+  FieldType var_list = make_test_field_type(
+      TypeId::LIST, {make_test_field_type(TypeId::VARINT32)});
+  EXPECT_TRUE(field_types_compatible(fixed_list, var_list));
+
+  FieldType int64_list = make_test_field_type(
+      TypeId::LIST, {make_test_field_type(TypeId::VARINT64)});
+  EXPECT_FALSE(field_types_compatible(fixed_list, int64_list));
+
+  EXPECT_TRUE(
+      field_types_compatible(make_test_field_type(TypeId::BINARY),
+                             make_test_field_type(TypeId::UINT8_ARRAY)));
+}
+
+TEST(StructComprehensiveTest,
+     AssignFieldIdsRejectsIncompatibleTaggedNestedTypes) {
+  TypeMeta local_type;
+  local_type.field_infos = {make_test_field_info(
+      "items", 7,
+      make_test_field_type(TypeId::LIST,
+                           {make_test_field_type(TypeId::VAR_UINT32)}))};
+
+  std::vector<FieldInfo> incompatible_remote = {make_test_field_info(
+      "items", 7,
+      make_test_field_type(TypeId::MAP,
+                           {make_test_field_type(TypeId::VAR_UINT32),
+                            make_test_field_type(TypeId::VAR_UINT32)}))};
+  TypeMeta::assign_field_ids(&local_type, incompatible_remote);
+  EXPECT_EQ(incompatible_remote[0].field_id, -1);
+
+  std::vector<FieldInfo> compatible_remote = {make_test_field_info(
+      "items", 7,
+      make_test_field_type(TypeId::LIST,
+                           {make_test_field_type(TypeId::UINT32)}))};
+  TypeMeta::assign_field_ids(&local_type, compatible_remote);
+  EXPECT_EQ(compatible_remote[0].field_id, 0);
+
+  TypeMeta name_mode_local;
+  name_mode_local.field_infos = {make_test_field_info(
+      "items", -1,
+      make_test_field_type(TypeId::LIST,
+                           {make_test_field_type(TypeId::VAR_UINT32)}))};
+  std::vector<FieldInfo> mixed_mode_remote = {make_test_field_info(
+      "items", 7,
+      make_test_field_type(TypeId::LIST,
+                           {make_test_field_type(TypeId::UINT32)}))};
+  TypeMeta::assign_field_ids(&name_mode_local, mixed_mode_remote);
+  EXPECT_EQ(mixed_mode_remote[0].field_id, -1);
+
+  std::vector<FieldInfo> name_remote = {make_test_field_info(
+      "items", -1,
+      make_test_field_type(TypeId::LIST,
+                           {make_test_field_type(TypeId::UINT32)}))};
+  TypeMeta::assign_field_ids(&local_type, name_remote);
+  EXPECT_EQ(name_remote[0].field_id, -1);
+}
+
 TEST(StructComprehensiveTest, OptionalFieldsAllEmpty) {
   test_roundtrip(
       OptionalFieldsStruct{"John", std::nullopt, std::nullopt, std::nullopt});
diff --git a/cpp/fory/serialization/type_resolver.cc b/cpp/fory/serialization/type_resolver.cc
index b17f959fb..563afb6b6 100644
--- a/cpp/fory/serialization/type_resolver.cc
+++ b/cpp/fory/serialization/type_resolver.cc
@@ -112,12 +112,12 @@ Result<FieldType, Error> FieldType::read_from(Buffer &buffer, bool read_flag,
   if (tid == static_cast<uint32_t>(TypeId::LIST) ||
       tid == static_cast<uint32_t>(TypeId::SET)) {
     FORY_TRY(generic, FieldType::read_from(buffer, true, false));
-    ft.generics.push_back(std::move(generic));
+    ft.add_generic(std::move(generic));
   } else if (tid == static_cast<uint32_t>(TypeId::MAP)) {
     FORY_TRY(key, FieldType::read_from(buffer, true, false));
     FORY_TRY(val, FieldType::read_from(buffer, true, false));
-    ft.generics.push_back(std::move(key));
-    ft.generics.push_back(std::move(val));
+    ft.add_generic(std::move(key));
+    ft.add_generic(std::move(val));
   }
 
   return ft;
@@ -778,8 +778,6 @@ bool is_compress(uint32_t type_id) {
          type_id == static_cast<uint32_t>(TypeId::TAGGED_UINT64);
 }
 
-bool field_type_needs_user_type_id(uint32_t) { return false; }
-
 std::string field_sort_key(const FieldInfo &field) {
   if (field.field_id >= 0) {
     return std::to_string(field.field_id);
@@ -949,80 +947,42 @@ void TypeMeta::assign_field_ids(const TypeMeta *local_type,
       local_field_id_map.emplace(local_fields[i].field_id, i);
     }
   }
+  const bool local_uses_field_ids = !local_field_id_map.empty();
+  bool remote_uses_field_ids = false;
+  for (const auto &remote_field : remote_fields) {
+    if (remote_field.field_id >= 0) {
+      remote_uses_field_ids = true;
+      break;
+    }
+  }
+
+  if (local_uses_field_ids || remote_uses_field_ids) {
+    for (auto &remote_field : remote_fields) {
+      size_t local_index = static_cast<size_t>(-1);
+      if (local_uses_field_ids && remote_field.field_id >= 0) {
+        auto id_it = local_field_id_map.find(remote_field.field_id);
+        if (id_it != local_field_id_map.end() &&
+            field_types_compatible(local_fields[id_it->second].field_type,
+                                   remote_field.field_type)) {
+          local_index = id_it->second;
+        }
+      }
+      remote_field.field_id = local_index == static_cast<size_t>(-1)
+                                  ? -1
+                                  : static_cast<int16_t>(local_index);
+    }
+    return;
+  }
 
   // Track which local fields have already been matched so that each
   // local field is bound to at most one remote field when we fall
   // back to type-based matching.
   std::vector<bool> used(local_fields.size(), false);
 
-  // Normalize user-defined type IDs so that different encodings of the
-  // same logical category (STRUCT vs COMPATIBLE_STRUCT, ENUM vs
-  // NAMED_ENUM, EXT vs NAMED_EXT, and their ID-based variants) are
-  // treated as equal for schema-evolution matching.
-  auto normalize_type_id = [](uint32_t tid) {
-    switch (static_cast<TypeId>(tid)) {
-    case TypeId::STRUCT:
-    case TypeId::COMPATIBLE_STRUCT:
-    case TypeId::NAMED_STRUCT:
-    case TypeId::NAMED_COMPATIBLE_STRUCT:
-    case TypeId::UNKNOWN:
-      // UNKNOWN is used for polymorphic types (e.g., Java interfaces) and
-      // should match STRUCT for cross-language schema evolution.
-      return static_cast<uint32_t>(TypeId::STRUCT);
-    case TypeId::ENUM:
-    case TypeId::NAMED_ENUM:
-      return static_cast<uint32_t>(TypeId::ENUM);
-    case TypeId::EXT:
-    case TypeId::NAMED_EXT:
-      return static_cast<uint32_t>(TypeId::EXT);
-    case TypeId::BINARY:
-    case TypeId::INT8_ARRAY:
-    case TypeId::UINT8_ARRAY:
-      return static_cast<uint32_t>(TypeId::BINARY);
-    default:
-      return tid;
-    }
-  };
-
-  // Recursive logical type comparison that ignores language-specific
-  // encoding details (such as embedded user IDs or named vs unnamed
-  // variants) while still matching full generic structure.
-  std::function<bool(const FieldType &, const FieldType &)> types_match;
-  types_match = [&](const FieldType &a, const FieldType &b) -> bool {
-    if (normalize_type_id(a.type_id) != normalize_type_id(b.type_id)) {
-      return false;
-    }
-    if (field_type_needs_user_type_id(a.type_id) ||
-        field_type_needs_user_type_id(b.type_id)) {
-      if ((a.user_type_id != kInvalidUserTypeId ||
-           b.user_type_id != kInvalidUserTypeId) &&
-          a.user_type_id != b.user_type_id) {
-        return false;
-      }
-    }
-    if (a.generics.size() != b.generics.size()) {
-      return false;
-    }
-    for (size_t i = 0; i < a.generics.size(); ++i) {
-      if (!types_match(a.generics[i], b.generics[i])) {
-        return false;
-      }
-    }
-    return true;
-  };
-
   // For each remote field, assign field ID (sorted index in local schema)
   for (auto &remote_field : remote_fields) {
     size_t local_index = static_cast<size_t>(-1);
 
-    // 0) If remote field carries a tag ID, map directly by ID.
-    if (remote_field.field_id >= 0) {
-      auto id_it = local_field_id_map.find(remote_field.field_id);
-      if (id_it != local_field_id_map.end()) {
-        local_index = id_it->second;
-      }
-    }
-
     // 1) Try exact name + type match first (fast path for same-language
     //    schemas and most C++-only cases).
     if (local_index == static_cast<size_t>(-1)) {
@@ -1030,7 +990,8 @@ void TypeMeta::assign_field_ids(const TypeMeta *local_type,
       if (it != local_field_index_map.end()) {
         size_t idx = it->second;
         const FieldInfo &local_field = local_fields[idx];
-        if (types_match(remote_field.field_type, local_field.field_type)) {
+        if (field_types_compatible(local_field.field_type,
+                                   remote_field.field_type)) {
           local_index = idx;
         }
       }
@@ -1043,7 +1004,8 @@ void TypeMeta::assign_field_ids(const TypeMeta *local_type,
         if (used[i]) {
           continue;
         }
-        if (types_match(remote_field.field_type, local_fields[i].field_type)) {
+        if (field_types_compatible(local_fields[i].field_type,
+                                   remote_field.field_type)) {
           local_index = i;
           break;
         }
@@ -1208,7 +1170,6 @@ std::string TypeMeta::compute_struct_fingerprint(
     }
     fingerprint.append(std::to_string(effective_type_id));
     fingerprint.push_back(',');
-    // Use field-level ref tracking flag from FORY_FIELD_TAGS or fory::field<>
     fingerprint.push_back(fi.field_type.track_ref ? '1' : '0');
     fingerprint.push_back(',');
     fingerprint.append(fi.field_type.nullable ? "1;" : "0;");
diff --git a/cpp/fory/serialization/type_resolver.h b/cpp/fory/serialization/type_resolver.h
index e5c746781..c03970f98 100644
--- a/cpp/fory/serialization/type_resolver.h
+++ b/cpp/fory/serialization/type_resolver.h
@@ -24,7 +24,6 @@
 #include <array>
 #include <cstdint>
 #include <deque>
-#include <functional>
 #include <list>
 #include <map>
 #include <memory>
@@ -110,17 +109,40 @@ public:
   bool track_ref;
   RefMode ref_mode; // Precomputed from nullable and track_ref
   std::vector<FieldType> generics;
+  uint64_t compatible_fingerprint;
+
+  static uint64_t
+  compute_compatible_fingerprint(uint32_t tid,
+                                 const std::vector<FieldType> &generic_types);
 
   FieldType()
       : type_id(0), user_type_id(kInvalidUserTypeId), nullable(false),
-        track_ref(false), ref_mode(RefMode::None) {}
+        track_ref(false), ref_mode(RefMode::None), generics(),
+        compatible_fingerprint(
+            compute_compatible_fingerprint(type_id, generics)) {}
 
   FieldType(uint32_t tid, bool null, bool ref_track = false,
             std::vector<FieldType> gens = {},
             uint32_t user_tid = kInvalidUserTypeId)
       : type_id(tid), user_type_id(user_tid), nullable(null),
         track_ref(ref_track), ref_mode(make_ref_mode(null, ref_track)),
-        generics(std::move(gens)) {}
+        generics(std::move(gens)),
+        compatible_fingerprint(
+            compute_compatible_fingerprint(type_id, generics)) {}
+
+  void refresh_compatible_fingerprint() {
+    compatible_fingerprint = compute_compatible_fingerprint(type_id, generics);
+  }
+
+  void set_type_id(uint32_t tid) {
+    type_id = tid;
+    refresh_compatible_fingerprint();
+  }
+
+  void add_generic(FieldType generic) {
+    generics.push_back(std::move(generic));
+    refresh_compatible_fingerprint();
+  }
 
   /// write field type to buffer
   /// @param buffer Target buffer
@@ -147,6 +169,70 @@ public:
   bool operator!=(const FieldType &other) const { return !(*this == other); }
 };
 
+inline uint32_t compatible_fingerprint_type_id(uint32_t type_id) {
+  switch (static_cast<TypeId>(type_id)) {
+  case TypeId::STRUCT:
+  case TypeId::COMPATIBLE_STRUCT:
+  case TypeId::NAMED_STRUCT:
+  case TypeId::NAMED_COMPATIBLE_STRUCT:
+  case TypeId::UNKNOWN:
+    return static_cast<uint32_t>(TypeId::STRUCT);
+  case TypeId::ENUM:
+  case TypeId::NAMED_ENUM:
+    return static_cast<uint32_t>(TypeId::ENUM);
+  case TypeId::EXT:
+  case TypeId::NAMED_EXT:
+    return static_cast<uint32_t>(TypeId::EXT);
+  case TypeId::BINARY:
+  case TypeId::INT8_ARRAY:
+  case TypeId::UINT8_ARRAY:
+    return static_cast<uint32_t>(TypeId::BINARY);
+  case TypeId::INT32:
+  case TypeId::VARINT32:
+    return static_cast<uint32_t>(TypeId::VARINT32);
+  case TypeId::INT64:
+  case TypeId::VARINT64:
+  case TypeId::TAGGED_INT64:
+    return static_cast<uint32_t>(TypeId::VARINT64);
+  case TypeId::UINT32:
+  case TypeId::VAR_UINT32:
+    return static_cast<uint32_t>(TypeId::VAR_UINT32);
+  case TypeId::UINT64:
+  case TypeId::VAR_UINT64:
+  case TypeId::TAGGED_UINT64:
+    return static_cast<uint32_t>(TypeId::VAR_UINT64);
+  default:
+    return type_id;
+  }
+}
+
+inline uint64_t FieldType::compute_compatible_fingerprint(
+    uint32_t tid, const std::vector<FieldType> &generic_types) {
+  uint64_t hash = 14695981039346656037ULL;
+  hash = fnv1a_64_combine(hash, compatible_fingerprint_type_id(tid));
+  hash = fnv1a_64_combine(hash, static_cast<uint64_t>(generic_types.size()));
+  for (const auto &generic : generic_types) {
+    hash = fnv1a_64_combine(hash, generic.compatible_fingerprint);
+  }
+  return hash;
+}
+
+inline bool collection_type_allows_empty_generic_fallback(uint32_t type_id) {
+  return type_id == static_cast<uint32_t>(TypeId::LIST) ||
+         type_id == static_cast<uint32_t>(TypeId::SET) ||
+         type_id == static_cast<uint32_t>(TypeId::MAP);
+}
+
+inline bool field_types_compatible(const FieldType &local,
+                                   const FieldType &remote) {
+  if (local.compatible_fingerprint == remote.compatible_fingerprint) {
+    return true;
+  }
+  return local.type_id == remote.type_id &&
+         collection_type_allows_empty_generic_fallback(local.type_id) &&
+         (local.generics.empty() || remote.generics.empty());
+}
+
 // ============================================================================
 // FieldInfo - Field metadata (name, type, id)
 // ============================================================================
@@ -377,7 +463,7 @@ struct FieldTypeBuilder<T, std::enable_if_t<is_vector_v<decay_t<T>>>> {
     if constexpr (vec_type_id == TypeId::LIST) {
       FieldType elem = FieldTypeBuilder<Element>::build(false);
       FieldType ft(to_type_id(vec_type_id), nullable);
-      ft.generics.push_back(std::move(elem));
+      ft.add_generic(std::move(elem));
       return ft;
     } else {
       return FieldType(to_type_id(vec_type_id), nullable);
@@ -392,7 +478,7 @@ struct FieldTypeBuilder<T, std::enable_if_t<is_list_v<decay_t<T>>>> {
   static FieldType build(bool nullable) {
     FieldType elem = FieldTypeBuilder<Element>::build(false);
     FieldType ft(to_type_id(TypeId::LIST), nullable);
-    ft.generics.push_back(std::move(elem));
+    ft.add_generic(std::move(elem));
     return ft;
   }
 };
@@ -404,7 +490,7 @@ struct FieldTypeBuilder<T, std::enable_if_t<is_deque_v<decay_t<T>>>> {
   static FieldType build(bool nullable) {
     FieldType elem = FieldTypeBuilder<Element>::build(false);
     FieldType ft(to_type_id(TypeId::LIST), nullable);
-    ft.generics.push_back(std::move(elem));
+    ft.add_generic(std::move(elem));
     return ft;
   }
 };
@@ -416,7 +502,7 @@ struct FieldTypeBuilder<T, std::enable_if_t<is_forward_list_v<decay_t<T>>>> {
   static FieldType build(bool nullable) {
     FieldType elem = FieldTypeBuilder<Element>::build(false);
     FieldType ft(to_type_id(TypeId::LIST), nullable);
-    ft.generics.push_back(std::move(elem));
+    ft.add_generic(std::move(elem));
     return ft;
   }
 };
@@ -428,7 +514,7 @@ struct FieldTypeBuilder<T, std::enable_if_t<is_set_like_v<decay_t<T>>>> {
   static FieldType build(bool nullable) {
     FieldType elem = FieldTypeBuilder<Element>::build(false);
     FieldType ft(to_type_id(Serializer<Set>::type_id), nullable);
-    ft.generics.push_back(std::move(elem));
+    ft.add_generic(std::move(elem));
     return ft;
   }
 };
@@ -442,8 +528,8 @@ struct FieldTypeBuilder<T, std::enable_if_t<is_map_like_v<decay_t<T>>>> {
     FieldType key_ft = FieldTypeBuilder<Key>::build(false);
     FieldType value_ft = FieldTypeBuilder<Value>::build(false);
     FieldType ft(to_type_id(Serializer<Map>::type_id), nullable);
-    ft.generics.push_back(std::move(key_ft));
-    ft.generics.push_back(std::move(value_ft));
+    ft.add_generic(std::move(key_ft));
+    ft.add_generic(std::move(value_ft));
     return ft;
   }
 };
@@ -463,7 +549,7 @@ struct FieldTypeBuilder<T, std::enable_if_t<is_tuple_v<decay_t<T>>>> {
 
   template <size_t... Is>
   static void add_element_types(FieldType &ft, std::index_sequence<Is...>) {
-    (ft.generics.push_back(
+    (ft.add_generic(
          FieldTypeBuilder<std::tuple_element_t<Is, Tuple>>::build(false)),
      ...);
   }
@@ -475,8 +561,7 @@ struct FieldTypeBuilder<T, std::enable_if_t<is_tuple_v<decay_t<T>>>> {
       add_element_types(ft, std::make_index_sequence<tuple_size>{});
     } else {
       // Empty tuple: use UNKNOWN as stub element type for schema encoding
-      ft.generics.push_back(
-          FieldType(static_cast<uint32_t>(TypeId::UNKNOWN), false));
+      ft.add_generic(FieldType(static_cast<uint32_t>(TypeId::UNKNOWN), false));
     }
     return ft;
   }
@@ -510,19 +595,200 @@ struct FieldTypeBuilder<
   }
 };
 
-inline bool field_type_needs_user_type_id(uint32_t) { return false; }
-
 template <typename T>
 Result<FieldType, Error> build_field_type_with_resolver(TypeResolver &resolver,
                                                         bool nullable);
 
+inline bool field_node_has_override(const FieldNodeSpec &spec,
+                                    int8_t node_index) {
+  return node_index >= 0 &&
+         (spec.kind_[node_index] != FieldNodeKind::Default ||
+          spec.encoding_[node_index] != Encoding::Default ||
+          spec.scalar_[node_index] != FieldScalarKind::Inferred);
+}
+
+template <typename Element>
+TypeId vector_type_id_for_spec(const FieldNodeSpec &spec, int8_t node_index,
+                               TypeId default_type_id) {
+  if (node_index < 0 || spec.kind_[node_index] != FieldNodeKind::List) {
+    return default_type_id;
+  }
+  const int8_t child = spec.child0_[node_index];
+  if (child < 0 || spec.kind_[child] != FieldNodeKind::Scalar ||
+      spec.encoding_[child] != Encoding::Default) {
+    return TypeId::LIST;
+  }
+  if constexpr (std::is_same_v<decay_t<Element>, int8_t>) {
+    if (spec.scalar_[child] == FieldScalarKind::Int8) {
+      return TypeId::INT8_ARRAY;
+    }
+  } else if constexpr (std::is_same_v<decay_t<Element>, uint8_t>) {
+    if (spec.scalar_[child] == FieldScalarKind::UInt8) {
+      return TypeId::UINT8_ARRAY;
+    }
+  }
+  return TypeId::LIST;
+}
+
+template <typename FieldT>
+void apply_integer_encoding(FieldType &ft, const FieldNodeSpec &spec,
+                            int8_t node_index) {
+  using Decayed = decay_t<FieldT>;
+  const Encoding enc =
+      node_index >= 0 ? spec.encoding_[node_index] : Encoding::Default;
+  if (enc == Encoding::Default) {
+    return;
+  }
+  if constexpr (std::is_same_v<Decayed, uint8_t>) {
+    ft.set_type_id(static_cast<uint32_t>(TypeId::UINT8));
+  } else if constexpr (std::is_same_v<Decayed, uint16_t>) {
+    ft.set_type_id(static_cast<uint32_t>(TypeId::UINT16));
+  } else if constexpr (std::is_same_v<Decayed, uint32_t>) {
+    ft.set_type_id(static_cast<uint32_t>(
+        enc == Encoding::Varint ? TypeId::VAR_UINT32 : TypeId::UINT32));
+  } else if constexpr (std::is_same_v<Decayed, uint64_t>) {
+    ft.set_type_id(static_cast<uint32_t>(enc == Encoding::Varint
+                                             ? TypeId::VAR_UINT64
+                                             : (enc == Encoding::Tagged
+                                                    ? TypeId::TAGGED_UINT64
+                                                    : TypeId::UINT64)));
+  } else if constexpr (std::is_same_v<Decayed, int32_t> ||
+                       std::is_same_v<Decayed, int>) {
+    ft.set_type_id(static_cast<uint32_t>(
+        enc == Encoding::Fixed ? TypeId::INT32 : TypeId::VARINT32));
+  } else if constexpr (std::is_same_v<Decayed, int64_t> ||
+                       std::is_same_v<Decayed, long long>) {
+    ft.set_type_id(static_cast<uint32_t>(
+        enc == Encoding::Fixed ? TypeId::INT64
+                               : (enc == Encoding::Tagged ? TypeId::TAGGED_INT64
+                                                          : TypeId::VARINT64)));
+  }
+}
+
+template <typename T>
+FieldType build_field_type_with_spec(bool nullable, const FieldNodeSpec &spec,
+                                     int8_t node_index = 0) {
+  using Decayed = decay_t<T>;
+  const FieldNodeKind node_kind =
+      node_index >= 0 ? spec.kind_[node_index] : FieldNodeKind::Default;
+
+  if constexpr (is_optional_v<Decayed>) {
+    using Inner = typename Decayed::value_type;
+    const int8_t child = node_kind == FieldNodeKind::Inner
+                             ? spec.child0_[node_index]
+                             : node_index;
+    FieldType inner = build_field_type_with_spec<Inner>(true, spec, child);
+    inner.nullable = true;
+    return inner;
+  } else if constexpr (is_shared_ptr_v<Decayed>) {
+    using Inner = typename Decayed::element_type;
+    if constexpr (std::is_polymorphic_v<Inner>) {
+      return FieldType(to_type_id(TypeId::UNKNOWN), true);
+    } else {
+      const int8_t child = node_kind == FieldNodeKind::Inner
+                               ? spec.child0_[node_index]
+                               : node_index;
+      FieldType inner = build_field_type_with_spec<Inner>(true, spec, child);
+      inner.nullable = true;
+      return inner;
+    }
+  } else if constexpr (::fory::detail::is_shared_weak_v<Decayed>) {
+    using Inner = nullable_element_t<Decayed>;
+    if constexpr (std::is_polymorphic_v<Inner>) {
+      return FieldType(to_type_id(TypeId::UNKNOWN), true);
+    } else {
+      const int8_t child = node_kind == FieldNodeKind::Inner
+                               ? spec.child0_[node_index]
+                               : node_index;
+      FieldType inner = build_field_type_with_spec<Inner>(true, spec, child);
+      inner.nullable = true;
+      return inner;
+    }
+  } else if constexpr (is_unique_ptr_v<Decayed>) {
+    using Inner = typename Decayed::element_type;
+    if constexpr (std::is_polymorphic_v<Inner>) {
+      return FieldType(to_type_id(TypeId::UNKNOWN), true);
+    } else {
+      const int8_t child = node_kind == FieldNodeKind::Inner
+                               ? spec.child0_[node_index]
+                               : node_index;
+      FieldType inner = build_field_type_with_spec<Inner>(true, spec, child);
+      inner.nullable = true;
+      return inner;
+    }
+  } else if constexpr (is_vector_v<Decayed>) {
+    using Element = element_type_t<Decayed>;
+    constexpr TypeId default_type_id = Serializer<Decayed>::type_id;
+    const bool force_list = node_kind == FieldNodeKind::List;
+    const TypeId type_id =
+        vector_type_id_for_spec<Element>(spec, node_index, default_type_id);
+    if (type_id == TypeId::LIST) {
+      const int8_t child = force_list ? spec.child0_[node_index] : -1;
+      FieldType elem =
+          field_node_has_override(spec, child)
+              ? build_field_type_with_spec<Element>(false, spec, child)
+              : FieldTypeBuilder<Element>::build(false);
+      FieldType ft(to_type_id(TypeId::LIST), nullable);
+      ft.add_generic(std::move(elem));
+      return ft;
+    }
+    return FieldType(to_type_id(type_id), nullable);
+  } else if constexpr (is_list_v<Decayed> || is_deque_v<Decayed> ||
+                       is_forward_list_v<Decayed>) {
+    using Element = typename Decayed::value_type;
+    const int8_t child =
+        node_kind == FieldNodeKind::List ? spec.child0_[node_index] : -1;
+    FieldType elem =
+        field_node_has_override(spec, child)
+            ? build_field_type_with_spec<Element>(false, spec, child)
+            : FieldTypeBuilder<Element>::build(false);
+    FieldType ft(to_type_id(TypeId::LIST), nullable);
+    ft.add_generic(std::move(elem));
+    return ft;
+  } else if constexpr (is_set_like_v<Decayed>) {
+    using Element = element_type_t<Decayed>;
+    const int8_t child =
+        node_kind == FieldNodeKind::Set ? spec.child0_[node_index] : -1;
+    FieldType elem =
+        field_node_has_override(spec, child)
+            ? build_field_type_with_spec<Element>(false, spec, child)
+            : FieldTypeBuilder<Element>::build(false);
+    FieldType ft(to_type_id(Serializer<Decayed>::type_id), nullable);
+    ft.add_generic(std::move(elem));
+    return ft;
+  } else if constexpr (is_map_like_v<Decayed>) {
+    using Key = key_type_t<Decayed>;
+    using Value = mapped_type_t<Decayed>;
+    const int8_t key_child =
+        node_kind == FieldNodeKind::Map ? spec.child0_[node_index] : -1;
+    const int8_t value_child =
+        node_kind == FieldNodeKind::Map ? spec.child1_[node_index] : -1;
+    FieldType key_ft =
+        field_node_has_override(spec, key_child)
+            ? build_field_type_with_spec<Key>(false, spec, key_child)
+            : FieldTypeBuilder<Key>::build(false);
+    FieldType value_ft =
+        field_node_has_override(spec, value_child)
+            ? build_field_type_with_spec<Value>(false, spec, value_child)
+            : FieldTypeBuilder<Value>::build(false);
+    FieldType ft(to_type_id(Serializer<Decayed>::type_id), nullable);
+    ft.add_generic(std::move(key_ft));
+    ft.add_generic(std::move(value_ft));
+    return ft;
+  } else {
+    FieldType ft = FieldTypeBuilder<Decayed>::build(nullable);
+    apply_integer_encoding<Decayed>(ft, spec, node_index);
+    return ft;
+  }
+}
+
 template <typename Tuple, size_t Index>
 Result<void, Error> add_tuple_element_types(TypeResolver &resolver,
                                             FieldType &ft) {
   if constexpr (Index < std::tuple_size_v<Tuple>) {
     using Elem = std::tuple_element_t<Index, Tuple>;
     FORY_TRY(elem_ft, build_field_type_with_resolver<Elem>(resolver, false));
-    ft.generics.push_back(std::move(elem_ft));
+    ft.add_generic(std::move(elem_ft));
     return add_tuple_element_types<Tuple, Index + 1>(resolver, ft);
   } else {
     return Result<void, Error>();
@@ -572,7 +838,7 @@ Result<FieldType, Error> build_field_type_with_resolver(TypeResolver &resolver,
     if constexpr (vec_type_id == TypeId::LIST) {
       FORY_TRY(elem, build_field_type_with_resolver<Element>(resolver, false));
       FieldType ft(to_type_id(vec_type_id), nullable);
-      ft.generics.push_back(std::move(elem));
+      ft.add_generic(std::move(elem));
       return ft;
     } else {
       return FieldType(to_type_id(vec_type_id), nullable);
@@ -581,26 +847,26 @@ Result<FieldType, Error> build_field_type_with_resolver(TypeResolver &resolver,
     using Element = typename Decayed::value_type;
     FORY_TRY(elem, build_field_type_with_resolver<Element>(resolver, false));
     FieldType ft(to_type_id(TypeId::LIST), nullable);
-    ft.generics.push_back(std::move(elem));
+    ft.add_generic(std::move(elem));
     return ft;
   } else if constexpr (is_deque_v<Decayed>) {
     using Element = typename Decayed::value_type;
     FORY_TRY(elem, build_field_type_with_resolver<Element>(resolver, false));
     FieldType ft(to_type_id(TypeId::LIST), nullable);
-    ft.generics.push_back(std::move(elem));
+    ft.add_generic(std::move(elem));
     return ft;
   } else if constexpr (is_forward_list_v<Decayed>) {
     using Element = typename Decayed::value_type;
     FORY_TRY(elem, build_field_type_with_resolver<Element>(resolver, false));
     FieldType ft(to_type_id(TypeId::LIST), nullable);
-    ft.generics.push_back(std::move(elem));
+    ft.add_generic(std::move(elem));
     return ft;
   } else if constexpr (is_set_like_v<Decayed>) {
     using Set = Decayed;
     using Element = element_type_t<Set>;
     FORY_TRY(elem, build_field_type_with_resolver<Element>(resolver, false));
     FieldType ft(to_type_id(Serializer<Set>::type_id), nullable);
-    ft.generics.push_back(std::move(elem));
+    ft.add_generic(std::move(elem));
     return ft;
   } else if constexpr (is_map_like_v<Decayed>) {
     using Map = Decayed;
@@ -609,8 +875,8 @@ Result<FieldType, Error> build_field_type_with_resolver(TypeResolver &resolver,
     FORY_TRY(key_ft, build_field_type_with_resolver<Key>(resolver, false));
     FORY_TRY(val_ft, build_field_type_with_resolver<Value>(resolver, false));
     FieldType ft(to_type_id(Serializer<Map>::type_id), nullable);
-    ft.generics.push_back(std::move(key_ft));
-    ft.generics.push_back(std::move(val_ft));
+    ft.add_generic(std::move(key_ft));
+    ft.add_generic(std::move(val_ft));
     return ft;
   } else if constexpr (is_string_view_v<Decayed>) {
     using StringT = std::basic_string<typename Decayed::value_type,
@@ -622,8 +888,7 @@ Result<FieldType, Error> build_field_type_with_resolver(TypeResolver &resolver,
     if constexpr (tuple_size > 0) {
       FORY_RETURN_IF_ERROR((add_tuple_element_types<Decayed, 0>(resolver, ft)));
     } else {
-      ft.generics.push_back(
-          FieldType(static_cast<uint32_t>(TypeId::UNKNOWN), false));
+      ft.add_generic(FieldType(static_cast<uint32_t>(TypeId::UNKNOWN), false));
     }
     return ft;
   } else {
@@ -636,7 +901,7 @@ Result<FieldType, Error> build_field_type_with_resolver(TypeResolver &resolver,
           if (resolved_type_id == static_cast<uint32_t>(TypeId::NAMED_ENUM)) {
             resolved_type_id = static_cast<uint32_t>(TypeId::ENUM);
           }
-          ft.type_id = resolved_type_id;
+          ft.set_type_id(resolved_type_id);
         }
       } else {
         FORY_TRY(info, get_type_info_with_resolver<Decayed>(resolver));
@@ -652,27 +917,149 @@ Result<FieldType, Error> build_field_type_with_resolver(TypeResolver &resolver,
         default:
           break;
         }
-        ft.type_id = resolved_type_id;
+        ft.set_type_id(resolved_type_id);
       }
     }
     return ft;
   }
 }
 
+template <typename T>
+Result<FieldType, Error>
+build_field_type_with_resolver_and_spec(TypeResolver &resolver, bool nullable,
+                                        const FieldNodeSpec &spec,
+                                        int8_t node_index = 0) {
+  using Decayed = decay_t<T>;
+  const FieldNodeKind node_kind =
+      node_index >= 0 ? spec.kind_[node_index] : FieldNodeKind::Default;
+
+  if constexpr (is_optional_v<Decayed>) {
+    using Inner = typename Decayed::value_type;
+    const int8_t child = node_kind == FieldNodeKind::Inner
+                             ? spec.child0_[node_index]
+                             : node_index;
+    FORY_TRY(inner, (build_field_type_with_resolver_and_spec<Inner>(
+                        resolver, true, spec, child)));
+    inner.nullable = true;
+    return inner;
+  } else if constexpr (is_shared_ptr_v<Decayed>) {
+    using Inner = typename Decayed::element_type;
+    if constexpr (std::is_polymorphic_v<Inner>) {
+      return FieldType(to_type_id(TypeId::UNKNOWN), true);
+    } else {
+      const int8_t child = node_kind == FieldNodeKind::Inner
+                               ? spec.child0_[node_index]
+                               : node_index;
+      FORY_TRY(inner, (build_field_type_with_resolver_and_spec<Inner>(
+                          resolver, true, spec, child)));
+      inner.nullable = true;
+      return inner;
+    }
+  } else if constexpr (::fory::detail::is_shared_weak_v<Decayed>) {
+    using Inner = nullable_element_t<Decayed>;
+    if constexpr (std::is_polymorphic_v<Inner>) {
+      return FieldType(to_type_id(TypeId::UNKNOWN), true);
+    } else {
+      const int8_t child = node_kind == FieldNodeKind::Inner
+                               ? spec.child0_[node_index]
+                               : node_index;
+      FORY_TRY(inner, (build_field_type_with_resolver_and_spec<Inner>(
+                          resolver, true, spec, child)));
+      inner.nullable = true;
+      return inner;
+    }
+  } else if constexpr (is_unique_ptr_v<Decayed>) {
+    using Inner = typename Decayed::element_type;
+    if constexpr (std::is_polymorphic_v<Inner>) {
+      return FieldType(to_type_id(TypeId::UNKNOWN), true);
+    } else {
+      const int8_t child = node_kind == FieldNodeKind::Inner
+                               ? spec.child0_[node_index]
+                               : node_index;
+      FORY_TRY(inner, (build_field_type_with_resolver_and_spec<Inner>(
+                          resolver, true, spec, child)));
+      inner.nullable = true;
+      return inner;
+    }
+  } else if constexpr (is_vector_v<Decayed>) {
+    using Element = element_type_t<Decayed>;
+    constexpr TypeId default_type_id = Serializer<Decayed>::type_id;
+    const bool force_list = node_kind == FieldNodeKind::List;
+    const TypeId type_id =
+        vector_type_id_for_spec<Element>(spec, node_index, default_type_id);
+    if (type_id == TypeId::LIST) {
+      const int8_t child = force_list ? spec.child0_[node_index] : -1;
+      FORY_TRY(elem, (field_node_has_override(spec, child)
+                          ? build_field_type_with_resolver_and_spec<Element>(
+                                resolver, false, spec, child)
+                          : build_field_type_with_resolver<Element>(resolver,
+                                                                    false)));
+      FieldType ft(to_type_id(TypeId::LIST), nullable);
+      ft.add_generic(std::move(elem));
+      return ft;
+    }
+    return FieldType(to_type_id(type_id), nullable);
+  } else if constexpr (is_list_v<Decayed> || is_deque_v<Decayed> ||
+                       is_forward_list_v<Decayed>) {
+    using Element = typename Decayed::value_type;
+    const int8_t child =
+        node_kind == FieldNodeKind::List ? spec.child0_[node_index] : -1;
+    FORY_TRY(elem,
+             (field_node_has_override(spec, child)
+                  ? build_field_type_with_resolver_and_spec<Element>(
+                        resolver, false, spec, child)
+                  : build_field_type_with_resolver<Element>(resolver, false)));
+    FieldType ft(to_type_id(TypeId::LIST), nullable);
+    ft.add_generic(std::move(elem));
+    return ft;
+  } else if constexpr (is_set_like_v<Decayed>) {
+    using Element = element_type_t<Decayed>;
+    const int8_t child =
+        node_kind == FieldNodeKind::Set ? spec.child0_[node_index] : -1;
+    FORY_TRY(elem,
+             (field_node_has_override(spec, child)
+                  ? build_field_type_with_resolver_and_spec<Element>(
+                        resolver, false, spec, child)
+                  : build_field_type_with_resolver<Element>(resolver, false)));
+    FieldType ft(to_type_id(Serializer<Decayed>::type_id), nullable);
+    ft.add_generic(std::move(elem));
+    return ft;
+  } else if constexpr (is_map_like_v<Decayed>) {
+    using Key = key_type_t<Decayed>;
+    using Value = mapped_type_t<Decayed>;
+    const int8_t key_child =
+        node_kind == FieldNodeKind::Map ? spec.child0_[node_index] : -1;
+    const int8_t value_child =
+        node_kind == FieldNodeKind::Map ? spec.child1_[node_index] : -1;
+    FORY_TRY(key_ft,
+             (field_node_has_override(spec, key_child)
+                  ? build_field_type_with_resolver_and_spec<Key>(
+                        resolver, false, spec, key_child)
+                  : build_field_type_with_resolver<Key>(resolver, false)));
+    FORY_TRY(value_ft,
+             (field_node_has_override(spec, value_child)
+                  ? build_field_type_with_resolver_and_spec<Value>(
+                        resolver, false, spec, value_child)
+                  : build_field_type_with_resolver<Value>(resolver, false)));
+    FieldType ft(to_type_id(Serializer<Decayed>::type_id), nullable);
+    ft.add_generic(std::move(key_ft));
+    ft.add_generic(std::move(value_ft));
+    return ft;
+  } else {
+    FORY_TRY(ft, (build_field_type_with_resolver<Decayed>(resolver, nullable)));
+    apply_integer_encoding<Decayed>(ft, spec, node_index);
+    return ft;
+  }
+}
+
 // Helper template functions to compute is_nullable and track_ref at compile
 // time. These replace constexpr lambdas which have issues on MSVC.
 template <typename ActualFieldType, typename T, size_t Index,
           typename UnwrappedFieldType>
 constexpr bool compute_is_nullable() {
-  if constexpr (is_fory_field_v<ActualFieldType>) {
-    return ActualFieldType::is_nullable;
-  } else if constexpr (::fory::detail::has_field_tags_v<T>) {
-    return ::fory::detail::GetFieldTagEntry<T, Index>::is_nullable;
-  } else if constexpr (::fory::detail::has_field_config_v<T> &&
-                       ::fory::detail::GetFieldConfigEntry<T,
-                                                           Index>::has_entry &&
-                       ::fory::detail::GetFieldConfigEntry<T,
-                                                           Index>::nullable) {
+  if constexpr (::fory::detail::has_field_config_v<T> &&
+                ::fory::detail::GetFieldConfigEntry<T, Index>::has_entry &&
+                ::fory::detail::GetFieldConfigEntry<T, Index>::nullable) {
     return true;
   } else {
     // Default: nullable if std::optional or smart pointers.
@@ -684,14 +1071,9 @@ constexpr bool compute_is_nullable() {
 
 template <typename ActualFieldType, typename T, size_t Index>
 constexpr bool compute_track_ref() {
-  if constexpr (is_fory_field_v<ActualFieldType>) {
-    return ActualFieldType::track_ref;
-  } else if constexpr (::fory::detail::has_field_tags_v<T>) {
-    return ::fory::detail::GetFieldTagEntry<T, Index>::track_ref;
-  } else if constexpr (::fory::detail::has_field_config_v<T> &&
-                       ::fory::detail::GetFieldConfigEntry<T,
-                                                           Index>::has_entry &&
-                       ::fory::detail::GetFieldConfigEntry<T, Index>::ref) {
+  if constexpr (::fory::detail::has_field_config_v<T> &&
+                ::fory::detail::GetFieldConfigEntry<T, Index>::has_entry &&
+                ::fory::detail::GetFieldConfigEntry<T, Index>::ref) {
     return true;
   } else {
     using UnwrappedFieldType = fory::unwrap_field_t<ActualFieldType>;
@@ -709,15 +1091,6 @@ constexpr int16_t compute_field_id() {
       return config_id;
     }
   }
-  if constexpr (is_fory_field_v<ActualFieldType>) {
-    return field_tag_id_v<ActualFieldType>;
-  }
-  if constexpr (::fory::detail::has_field_tags_v<T>) {
-    constexpr int16_t tag_id = ::fory::detail::GetFieldTagEntry<T, Index>::id;
-    if constexpr (tag_id >= 0) {
-      return tag_id;
-    }
-  }
   return -1;
 }
 
@@ -741,10 +1114,10 @@ struct unwrap_optional_inner<T, std::enable_if_t<is_optional_v<decay_t<T>>>> {
 template <typename T>
 using unwrap_optional_inner_t = typename unwrap_optional_inner<T>::type;
 
-// Helper to compute the correct type_id for unsigned types based on encoding
+// Helper to compute the correct type_id for unsigned types based on the
+// effective field spec.
 template <typename FieldT, typename StructT, size_t Index>
 constexpr uint32_t compute_unsigned_type_id() {
-  // For unsigned types, check if FORY_FIELD_CONFIG specifies an encoding
   if constexpr (::fory::detail::has_field_config_v<StructT>) {
     constexpr auto enc =
         ::fory::detail::GetFieldConfigEntry<StructT, Index>::encoding;
@@ -770,7 +1143,7 @@ constexpr uint32_t compute_unsigned_type_id() {
       }
     }
   }
-  // Not an unsigned type with field config, use default
+  // Not an unsigned type with configured encoding; use the type default.
   return 0;
 }
 
@@ -817,42 +1190,17 @@ template <typename T, size_t Index> struct FieldInfoBuilder {
         typename meta::RemoveMemberPointerCVRefT<decltype(field_ptr)>;
     using ActualFieldType =
         std::remove_cv_t<std::remove_reference_t<RawFieldType>>;
-    // unwrap fory::field<> to get the underlying type for FieldTypeBuilder
     using UnwrappedFieldType = fory::unwrap_field_t<ActualFieldType>;
 
-    // get nullable and track_ref from field tags (FORY_FIELD_TAGS or
-    // fory::field<>)
     constexpr bool is_nullable =
         compute_is_nullable<ActualFieldType, T, Index, UnwrappedFieldType>();
     constexpr bool track_ref = compute_track_ref<ActualFieldType, T, Index>();
     constexpr int16_t field_id = compute_field_id<ActualFieldType, T, Index>();
 
-    FieldType field_type = FieldTypeBuilder<UnwrappedFieldType>::build(false);
-
-    // Override type_id for unsigned types based on encoding from
-    // FORY_FIELD_CONFIG
-    using InnerType = unwrap_optional_inner_t<UnwrappedFieldType>;
-    constexpr uint32_t unsigned_tid =
-        compute_unsigned_type_id<UnwrappedFieldType, T, Index>();
-    if constexpr (unsigned_tid != 0 && is_unsigned_integer_v<InnerType>) {
-      field_type.type_id = unsigned_tid;
-    }
-
-    // Override type_id for signed types based on encoding from
-    // FORY_FIELD_CONFIG
-    constexpr uint32_t signed_tid =
-        compute_signed_type_id<UnwrappedFieldType, T, Index>();
-    if constexpr (signed_tid != 0) {
-      field_type.type_id = signed_tid;
-    }
-
-    if constexpr (::fory::detail::has_field_config_v<T>) {
-      constexpr int16_t override_id =
-          ::fory::detail::GetFieldConfigEntry<T, Index>::type_id_override;
-      if constexpr (override_id >= 0) {
-        field_type.type_id = static_cast<uint32_t>(override_id);
-      }
-    }
+    constexpr FieldNodeSpec spec =
+        ::fory::detail::GetFieldConfigEntry<T, Index>::spec;
+    FieldType field_type =
+        build_field_type_with_spec<UnwrappedFieldType>(false, spec);
 
     // Override nullable and track_ref from field-level metadata
     field_type.nullable = is_nullable;
@@ -862,9 +1210,9 @@ template <typename T, size_t Index> struct FieldInfoBuilder {
     // DEBUG: Print field info for debugging fingerprint mismatch
     std::cerr << "[xlang][debug] FieldInfoBuilder T=" << typeid(T).name()
               << " Index=" << Index << " field=" << field_name
-              << " type_id=" << field_type.type_id << " has_tags="
-              << ::fory::detail::has_field_tags_v<T> << " is_nullable="
-              << is_nullable << " track_ref=" << track_ref << std::endl;
+              << " type_id=" << field_type.type_id
+              << " is_nullable=" << is_nullable << " track_ref=" << track_ref
+              << std::endl;
 #endif
     FieldInfo info(std::move(field_name), std::move(field_type));
     info.field_id = field_id;
@@ -889,43 +1237,18 @@ template <typename T, size_t Index> struct FieldInfoBuilder {
         typename meta::RemoveMemberPointerCVRefT<decltype(field_ptr)>;
     using ActualFieldType =
         std::remove_cv_t<std::remove_reference_t<RawFieldType>>;
-    // unwrap fory::field<> to get the underlying type for FieldTypeBuilder
     using UnwrappedFieldType = fory::unwrap_field_t<ActualFieldType>;
 
-    // get nullable and track_ref from field tags (FORY_FIELD_TAGS or
-    // fory::field<>)
     constexpr bool is_nullable =
         compute_is_nullable<ActualFieldType, T, Index, UnwrappedFieldType>();
     constexpr bool track_ref = compute_track_ref<ActualFieldType, T, Index>();
     constexpr int16_t field_id = compute_field_id<ActualFieldType, T, Index>();
 
-    FORY_TRY(field_type, build_field_type_with_resolver<UnwrappedFieldType>(
-                             resolver, false));
-
-    // Override type_id for unsigned types based on encoding from
-    // FORY_FIELD_CONFIG
-    using InnerType = unwrap_optional_inner_t<UnwrappedFieldType>;
-    constexpr uint32_t unsigned_tid =
-        compute_unsigned_type_id<UnwrappedFieldType, T, Index>();
-    if constexpr (unsigned_tid != 0 && is_unsigned_integer_v<InnerType>) {
-      field_type.type_id = unsigned_tid;
-    }
-
-    // Override type_id for signed types based on encoding from
-    // FORY_FIELD_CONFIG
-    constexpr uint32_t signed_tid =
-        compute_signed_type_id<UnwrappedFieldType, T, Index>();
-    if constexpr (signed_tid != 0) {
-      field_type.type_id = signed_tid;
-    }
-
-    if constexpr (::fory::detail::has_field_config_v<T>) {
-      constexpr int16_t override_id =
-          ::fory::detail::GetFieldConfigEntry<T, Index>::type_id_override;
-      if constexpr (override_id >= 0) {
-        field_type.type_id = static_cast<uint32_t>(override_id);
-      }
-    }
+    constexpr FieldNodeSpec spec =
+        ::fory::detail::GetFieldConfigEntry<T, Index>::spec;
+    FORY_TRY(field_type,
+             (build_field_type_with_resolver_and_spec<UnwrappedFieldType>(
+                 resolver, false, spec)));
 
     // Override nullable and track_ref from field-level metadata
     field_type.nullable = is_nullable;
@@ -935,9 +1258,9 @@ template <typename T, size_t Index> struct FieldInfoBuilder {
     // DEBUG: Print field info for debugging fingerprint mismatch
     std::cerr << "[xlang][debug] FieldInfoBuilder T=" << typeid(T).name()
               << " Index=" << Index << " field=" << field_name
-              << " type_id=" << field_type.type_id << " has_tags="
-              << ::fory::detail::has_field_tags_v<T> << " is_nullable="
-              << is_nullable << " track_ref=" << track_ref << std::endl;
+              << " type_id=" << field_type.type_id
+              << " is_nullable=" << is_nullable << " track_ref=" << track_ref
+              << std::endl;
 #endif
     FieldInfo info(std::move(field_name), std::move(field_type));
     info.field_id = field_id;
diff --git a/cpp/fory/serialization/union_serializer.h b/cpp/fory/serialization/union_serializer.h
index c90d3d0d2..7d5820f4f 100644
--- a/cpp/fory/serialization/union_serializer.h
+++ b/cpp/fory/serialization/union_serializer.h
@@ -25,6 +25,7 @@
 #include "fory/serialization/serializer.h"
 #include "fory/serialization/serializer_traits.h"
 #include "fory/serialization/skip.h"
+#include "fory/serialization/struct_serializer.h"
 #include "fory/type/type.h"
 #include "fory/util/error.h"
 
@@ -175,6 +176,78 @@ template <typename T>
 using union_unwrap_optional_inner_t =
     typename union_unwrap_optional_inner<T>::type;
 
+template <typename UnionT, size_t Index> struct UnionCaseSpecProvider {
+  static inline constexpr FieldNodeSpec spec =
+      UnionInfo<UnionT>::template Meta<Index>::value.spec_;
+};
+
+template <typename SpecProvider, int8_t NodeIndex = 0>
+constexpr FieldNodeKind union_node_kind() {
+  constexpr auto spec = SpecProvider::spec;
+  return NodeIndex >= 0 ? spec.kind_[NodeIndex] : FieldNodeKind::Default;
+}
+
+template <typename SpecProvider, int8_t NodeIndex = 0>
+constexpr Encoding union_node_encoding() {
+  constexpr auto spec = SpecProvider::spec;
+  return NodeIndex >= 0 ? spec.encoding_[NodeIndex] : Encoding::Default;
+}
+
+template <typename SpecProvider, int8_t NodeIndex = 0>
+constexpr bool union_node_has_override() {
+  constexpr auto spec = SpecProvider::spec;
+  return NodeIndex >= 0 &&
+         (spec.kind_[NodeIndex] != FieldNodeKind::Default ||
+          spec.encoding_[NodeIndex] != Encoding::Default ||
+          spec.scalar_[NodeIndex] != FieldScalarKind::Inferred);
+}
+
+template <typename SpecProvider, int8_t NodeIndex = 0>
+constexpr FieldScalarKind union_node_scalar() {
+  constexpr auto spec = SpecProvider::spec;
+  return NodeIndex >= 0 ? spec.scalar_[NodeIndex] : FieldScalarKind::Inferred;
+}
+
+template <typename SpecProvider, int8_t NodeIndex, int ChildSlot>
+constexpr int8_t union_node_child() {
+  constexpr auto spec = SpecProvider::spec;
+  if constexpr (ChildSlot == 0) {
+    return NodeIndex >= 0 ? spec.child0_[NodeIndex] : -1;
+  } else {
+    return NodeIndex >= 0 ? spec.child1_[NodeIndex] : -1;
+  }
+}
+
+template <typename ValueType, typename SpecProvider, int8_t NodeIndex>
+constexpr bool union_vector_primitive_array_spec() {
+  if constexpr (!is_vector_v<ValueType>) {
+    return false;
+  } else {
+    using Element = element_type_t<ValueType>;
+    if constexpr (!std::is_same_v<decay_t<Element>, int8_t> &&
+                  !std::is_same_v<decay_t<Element>, uint8_t>) {
+      return false;
+    } else {
+      constexpr FieldNodeKind kind = union_node_kind<SpecProvider, NodeIndex>();
+      constexpr int8_t child = union_node_child<SpecProvider, NodeIndex, 0>();
+      if constexpr (kind != FieldNodeKind::List || child < 0) {
+        return false;
+      } else if constexpr (union_node_kind<SpecProvider, child>() !=
+                               FieldNodeKind::Scalar ||
+                           union_node_encoding<SpecProvider, child>() !=
+                               Encoding::Default) {
+        return false;
+      } else if constexpr (std::is_same_v<decay_t<Element>, int8_t>) {
+        return union_node_scalar<SpecProvider, child>() ==
+               FieldScalarKind::Int8;
+      } else {
+        return union_node_scalar<SpecProvider, child>() ==
+               FieldScalarKind::UInt8;
+      }
+    }
+  }
+}
+
 template <typename T>
 constexpr uint32_t resolve_union_type_id(const ::fory::FieldMeta &meta) {
   if (meta.type_id_override_ >= 0) {
@@ -218,6 +291,21 @@ constexpr uint32_t resolve_union_type_id(const ::fory::FieldMeta &meta) {
   return static_cast<uint32_t>(Serializer<Inner>::type_id);
 }
 
+template <typename T, typename SpecProvider>
+constexpr uint32_t
+resolve_union_type_id_for_spec(const ::fory::FieldMeta &meta) {
+  using Inner = union_unwrap_optional_inner_t<T>;
+  if constexpr (union_vector_primitive_array_spec<Inner, SpecProvider, 0>()) {
+    using Element = element_type_t<Inner>;
+    if constexpr (std::is_same_v<decay_t<Element>, int8_t>) {
+      return static_cast<uint32_t>(TypeId::INT8_ARRAY);
+    } else {
+      return static_cast<uint32_t>(TypeId::UINT8_ARRAY);
+    }
+  }
+  return resolve_union_type_id<T>(meta);
+}
+
 template <typename T>
 constexpr bool needs_manual_encoding(const ::fory::FieldMeta &meta) {
   if (meta.type_id_override_ >= 0 ||
@@ -229,6 +317,362 @@ constexpr bool needs_manual_encoding(const ::fory::FieldMeta &meta) {
          resolve_union_type_id<T>(meta);
 }
 
+template <typename FieldType, typename SpecProvider, int8_t NodeIndex>
+FORY_ALWAYS_INLINE void write_union_configured_scalar(const FieldType &value,
+                                                      WriteContext &ctx) {
+  constexpr Encoding enc = union_node_encoding<SpecProvider, NodeIndex>();
+  if constexpr (is_configurable_int_v<FieldType>) {
+    if constexpr (std::is_same_v<FieldType, uint32_t>) {
+      if constexpr (enc == Encoding::Varint) {
+        ctx.write_var_uint32(value);
+      } else {
+        ctx.buffer().write_int32(static_cast<int32_t>(value));
+      }
+    } else if constexpr (std::is_same_v<FieldType, uint64_t>) {
+      if constexpr (enc == Encoding::Varint) {
+        ctx.write_var_uint64(value);
+      } else if constexpr (enc == Encoding::Tagged) {
+        ctx.write_tagged_uint64(value);
+      } else {
+        ctx.buffer().write_int64(static_cast<int64_t>(value));
+      }
+    } else if constexpr (std::is_same_v<FieldType, int32_t> ||
+                         std::is_same_v<FieldType, int>) {
+      if constexpr (enc == Encoding::Fixed) {
+        ctx.buffer().write_int32(static_cast<int32_t>(value));
+      } else {
+        ctx.write_var_int32(static_cast<int32_t>(value));
+      }
+    } else if constexpr (std::is_same_v<FieldType, int64_t> ||
+                         std::is_same_v<FieldType, long long>) {
+      if constexpr (enc == Encoding::Fixed) {
+        ctx.buffer().write_int64(static_cast<int64_t>(value));
+      } else if constexpr (enc == Encoding::Tagged) {
+        ctx.write_tagged_int64(static_cast<int64_t>(value));
+      } else {
+        ctx.write_var_int64(static_cast<int64_t>(value));
+      }
+    } else {
+      Serializer<FieldType>::write_data(value, ctx);
+    }
+  } else {
+    Serializer<FieldType>::write_data(value, ctx);
+  }
+}
+
+template <typename FieldType, typename SpecProvider, int8_t NodeIndex>
+FORY_ALWAYS_INLINE FieldType read_union_configured_scalar(ReadContext &ctx) {
+  if constexpr (is_configurable_int_v<FieldType>) {
+    constexpr Encoding enc = union_node_encoding<SpecProvider, NodeIndex>();
+    if constexpr (std::is_same_v<FieldType, uint32_t>) {
+      if constexpr (enc == Encoding::Varint) {
+        return static_cast<FieldType>(ctx.read_var_uint32(ctx.error()));
+      }
+      return static_cast<FieldType>(ctx.read_int32(ctx.error()));
+    } else if constexpr (std::is_same_v<FieldType, uint64_t>) {
+      if constexpr (enc == Encoding::Varint) {
+        return static_cast<FieldType>(ctx.read_var_uint64(ctx.error()));
+      } else if constexpr (enc == Encoding::Tagged) {
+        return static_cast<FieldType>(ctx.read_tagged_uint64(ctx.error()));
+      }
+      return static_cast<FieldType>(ctx.read_uint64(ctx.error()));
+    } else if constexpr (std::is_same_v<FieldType, int32_t> ||
+                         std::is_same_v<FieldType, int>) {
+      if constexpr (enc == Encoding::Fixed) {
+        return static_cast<FieldType>(ctx.read_int32(ctx.error()));
+      }
+      return static_cast<FieldType>(ctx.read_var_int32(ctx.error()));
+    } else if constexpr (std::is_same_v<FieldType, int64_t> ||
+                         std::is_same_v<FieldType, long long>) {
+      if constexpr (enc == Encoding::Fixed) {
+        return static_cast<FieldType>(ctx.read_int64(ctx.error()));
+      } else if constexpr (enc == Encoding::Tagged) {
+        return static_cast<FieldType>(ctx.read_tagged_int64(ctx.error()));
+      }
+      return static_cast<FieldType>(ctx.read_var_int64(ctx.error()));
+    } else {
+      return Serializer<FieldType>::read_data(ctx);
+    }
+  } else {
+    return Serializer<FieldType>::read_data(ctx);
+  }
+}
+
+template <typename ValueType, typename SpecProvider, int8_t NodeIndex>
+void write_union_configured_value(const ValueType &value, WriteContext &ctx,
+                                  RefMode ref_mode, bool write_type,
+                                  bool has_generics);
+
+template <typename ValueType, typename SpecProvider, int8_t NodeIndex>
+ValueType read_union_configured_value(ReadContext &ctx, RefMode ref_mode,
+                                      bool read_type);
+
+template <typename Container, typename SpecProvider, int8_t ElemNode>
+void write_union_configured_list_data(const Container &coll,
+                                      WriteContext &ctx) {
+  using Elem = element_type_t<Container>;
+  ctx.write_var_uint32(static_cast<uint32_t>(coll.size()));
+  if (coll.empty()) {
+    return;
+  }
+  ctx.write_uint8(COLL_DECL_ELEMENT_TYPE | COLL_IS_SAME_TYPE);
+  for (const auto &elem : coll) {
+    if constexpr (ElemNode >= 0) {
+      write_union_configured_value<Elem, SpecProvider, ElemNode>(
+          elem, ctx, RefMode::None, false, true);
+    } else {
+      Serializer<Elem>::write_data(elem, ctx);
+    }
+  }
+}
+
+template <typename Container, typename SpecProvider, int8_t ElemNode>
+Container read_union_configured_list_data(ReadContext &ctx) {
+  using Elem = element_type_t<Container>;
+  uint32_t length = ctx.read_var_uint32(ctx.error());
+  Container result;
+  if constexpr (has_reserve_v<Container>) {
+    result.reserve(length);
+  }
+  if (FORY_PREDICT_FALSE(ctx.has_error()) || length == 0) {
+    return result;
+  }
+  uint8_t bitmap = ctx.read_uint8(ctx.error());
+  if (FORY_PREDICT_FALSE(ctx.has_error())) {
+    return result;
+  }
+  const bool is_decl_type = (bitmap & COLL_DECL_ELEMENT_TYPE) != 0;
+  const bool is_same_type = (bitmap & COLL_IS_SAME_TYPE) != 0;
+  if (is_same_type && !is_decl_type) {
+    (void)ctx.read_any_type_info(ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return result;
+    }
+  }
+  for (uint32_t i = 0; i < length; ++i) {
+    if constexpr (ElemNode >= 0) {
+      auto elem = read_union_configured_value<Elem, SpecProvider, ElemNode>(
+          ctx, RefMode::None, false);
+      collection_insert(result, std::move(elem));
+    } else {
+      auto elem = Serializer<Elem>::read_data(ctx);
+      collection_insert(result, std::move(elem));
+    }
+  }
+  return result;
+}
+
+template <typename MapType, typename SpecProvider, int8_t KeyNode,
+          int8_t ValueNode>
+void write_union_configured_map_data(const MapType &map, WriteContext &ctx) {
+  using Key = key_type_t<MapType>;
+  using Value = mapped_type_t<MapType>;
+  ctx.write_var_uint32(static_cast<uint32_t>(map.size()));
+  if (map.empty()) {
+    return;
+  }
+  size_t header_offset = 0;
+  uint8_t pair_counter = 0;
+  bool need_write_header = true;
+  for (const auto &[key, value] : map) {
+    if (need_write_header) {
+      ctx.enter_flush_barrier();
+      header_offset = ctx.buffer().writer_index();
+      ctx.write_uint16(0);
+      ctx.buffer().unsafe_put_byte(
+          header_offset, static_cast<uint8_t>(DECL_KEY_TYPE | DECL_VALUE_TYPE));
+      need_write_header = false;
+    }
+    if constexpr (KeyNode >= 0) {
+      write_union_configured_value<Key, SpecProvider, KeyNode>(
+          key, ctx, RefMode::None, false, true);
+    } else {
+      Serializer<Key>::write_data(key, ctx);
+    }
+    if constexpr (ValueNode >= 0) {
+      write_union_configured_value<Value, SpecProvider, ValueNode>(
+          value, ctx, RefMode::None, false, true);
+    } else {
+      Serializer<Value>::write_data(value, ctx);
+    }
+    ++pair_counter;
+    if (pair_counter == MAX_CHUNK_SIZE) {
+      write_chunk_size(ctx, header_offset, pair_counter);
+      ctx.exit_flush_barrier();
+      ctx.try_flush();
+      pair_counter = 0;
+      need_write_header = true;
+    }
+  }
+  if (pair_counter > 0) {
+    write_chunk_size(ctx, header_offset, pair_counter);
+    ctx.exit_flush_barrier();
+    ctx.try_flush();
+  }
+}
+
+template <typename MapType, typename SpecProvider, int8_t KeyNode,
+          int8_t ValueNode>
+MapType read_union_configured_map_data(ReadContext &ctx) {
+  using Key = key_type_t<MapType>;
+  using Value = mapped_type_t<MapType>;
+  uint32_t length = ctx.read_var_uint32(ctx.error());
+  MapType result;
+  MapReserver<MapType>::reserve(result, length);
+  uint32_t read_count = 0;
+  while (read_count < length && !ctx.has_error()) {
+    uint8_t header = ctx.read_uint8(ctx.error());
+    uint8_t chunk_size = ctx.read_uint8(ctx.error());
+    if (FORY_PREDICT_FALSE(ctx.has_error())) {
+      return result;
+    }
+    const bool key_decl = (header & DECL_KEY_TYPE) != 0;
+    const bool value_decl = (header & DECL_VALUE_TYPE) != 0;
+    if (!key_decl) {
+      (void)ctx.read_any_type_info(ctx.error());
+    }
+    if (!value_decl) {
+      (void)ctx.read_any_type_info(ctx.error());
+    }
+    for (uint8_t i = 0; i < chunk_size && read_count < length; ++i) {
+      Key key = [&]() {
+        if constexpr (KeyNode >= 0) {
+          return read_union_configured_value<Key, SpecProvider, KeyNode>(
+              ctx, RefMode::None, false);
+        } else {
+          return Serializer<Key>::read_data(ctx);
+        }
+      }();
+      Value value = [&]() {
+        if constexpr (ValueNode >= 0) {
+          return read_union_configured_value<Value, SpecProvider, ValueNode>(
+              ctx, RefMode::None, false);
+        } else {
+          return Serializer<Value>::read_data(ctx);
+        }
+      }();
+      result.emplace(std::move(key), std::move(value));
+      ++read_count;
+    }
+  }
+  return result;
+}
+
+template <typename ValueType, typename SpecProvider, int8_t NodeIndex>
+void write_union_configured_value(const ValueType &value, WriteContext &ctx,
+                                  RefMode ref_mode, bool write_type,
+                                  bool has_generics) {
+  constexpr FieldNodeKind kind = union_node_kind<SpecProvider, NodeIndex>();
+  constexpr FieldScalarKind scalar_kind =
+      union_node_scalar<SpecProvider, NodeIndex>();
+  static_assert(configured_scalar_kind_matches<ValueType, scalar_kind>(),
+                "fory::T typed scalar spec does not match the C++ union case "
+                "type");
+  if constexpr (is_optional_v<ValueType>) {
+    using Inner = typename ValueType::value_type;
+    if (!value.has_value()) {
+      if (ref_mode != RefMode::None) {
+        ctx.write_int8(NULL_FLAG);
+      }
+      return;
+    }
+    write_not_null_ref_flag(ctx, ref_mode);
+    constexpr int8_t child =
+        kind == FieldNodeKind::Inner
+            ? union_node_child<SpecProvider, NodeIndex, 0>()
+            : NodeIndex;
+    write_union_configured_value<Inner, SpecProvider, child>(
+        *value, ctx, RefMode::None, false, has_generics);
+  } else if constexpr ((is_vector_v<ValueType> || is_list_v<ValueType> ||
+                        is_deque_v<ValueType> || is_set_like_v<ValueType>) &&
+                       (kind == FieldNodeKind::List ||
+                        kind == FieldNodeKind::Set)) {
+    if constexpr (union_vector_primitive_array_spec<ValueType, SpecProvider,
+                                                    NodeIndex>()) {
+      Serializer<ValueType>::write(value, ctx, ref_mode, false, has_generics);
+    } else {
+      write_not_null_ref_flag(ctx, ref_mode);
+      constexpr int8_t child = union_node_child<SpecProvider, NodeIndex, 0>();
+      write_union_configured_list_data<ValueType, SpecProvider, child>(value,
+                                                                       ctx);
+    }
+  } else if constexpr (is_map_like_v<ValueType> && kind == FieldNodeKind::Map) {
+    write_not_null_ref_flag(ctx, ref_mode);
+    constexpr int8_t key_child = union_node_child<SpecProvider, NodeIndex, 0>();
+    constexpr int8_t value_child =
+        union_node_child<SpecProvider, NodeIndex, 1>();
+    write_union_configured_map_data<ValueType, SpecProvider, key_child,
+                                    value_child>(value, ctx);
+  } else if constexpr (kind == FieldNodeKind::Scalar ||
+                       union_node_encoding<SpecProvider, NodeIndex>() !=
+                           Encoding::Default) {
+    write_not_null_ref_flag(ctx, ref_mode);
+    write_union_configured_scalar<ValueType, SpecProvider, NodeIndex>(value,
+                                                                      ctx);
+  } else {
+    Serializer<ValueType>::write(value, ctx, ref_mode, write_type,
+                                 has_generics);
+  }
+}
+
+template <typename ValueType, typename SpecProvider, int8_t NodeIndex>
+ValueType read_union_configured_value(ReadContext &ctx, RefMode ref_mode,
+                                      bool read_type) {
+  constexpr FieldNodeKind kind = union_node_kind<SpecProvider, NodeIndex>();
+  constexpr FieldScalarKind scalar_kind =
+      union_node_scalar<SpecProvider, NodeIndex>();
+  static_assert(configured_scalar_kind_matches<ValueType, scalar_kind>(),
+                "fory::T typed scalar spec does not match the C++ union case "
+                "type");
+  if constexpr (is_optional_v<ValueType>) {
+    using Inner = typename ValueType::value_type;
+    if (!read_null_only_flag(ctx, ref_mode)) {
+      return std::nullopt;
+    }
+    constexpr int8_t child =
+        kind == FieldNodeKind::Inner
+            ? union_node_child<SpecProvider, NodeIndex, 0>()
+            : NodeIndex;
+    Inner inner = read_union_configured_value<Inner, SpecProvider, child>(
+        ctx, RefMode::None, false);
+    return ValueType{std::move(inner)};
+  } else if constexpr ((is_vector_v<ValueType> || is_list_v<ValueType> ||
+                        is_deque_v<ValueType> || is_set_like_v<ValueType>) &&
+                       (kind == FieldNodeKind::List ||
+                        kind == FieldNodeKind::Set)) {
+    if constexpr (union_vector_primitive_array_spec<ValueType, SpecProvider,
+                                                    NodeIndex>()) {
+      return Serializer<ValueType>::read(ctx, ref_mode, false);
+    } else {
+      if (!read_null_only_flag(ctx, ref_mode)) {
+        return ValueType{};
+      }
+      constexpr int8_t child = union_node_child<SpecProvider, NodeIndex, 0>();
+      return read_union_configured_list_data<ValueType, SpecProvider, child>(
+          ctx);
+    }
+  } else if constexpr (is_map_like_v<ValueType> && kind == FieldNodeKind::Map) {
+    if (!read_null_only_flag(ctx, ref_mode)) {
+      return ValueType{};
+    }
+    constexpr int8_t key_child = union_node_child<SpecProvider, NodeIndex, 0>();
+    constexpr int8_t value_child =
+        union_node_child<SpecProvider, NodeIndex, 1>();
+    return read_union_configured_map_data<ValueType, SpecProvider, key_child,
+                                          value_child>(ctx);
+  } else if constexpr (kind == FieldNodeKind::Scalar ||
+                       union_node_encoding<SpecProvider, NodeIndex>() !=
+                           Encoding::Default) {
+    if (!read_null_only_flag(ctx, ref_mode)) {
+      return ValueType{};
+    }
+    return read_union_configured_scalar<ValueType, SpecProvider, NodeIndex>(
+        ctx);
+  } else {
+    return Serializer<ValueType>::read(ctx, ref_mode, read_type);
+  }
+}
+
 template <typename T>
 inline bool write_union_ref_flag(const T &value, WriteContext &ctx,
                                  RefMode ref_mode, bool nullable) {
@@ -410,6 +854,12 @@ inline bool dispatch_union_case(uint32_t case_id, F &&fn) {
   return false;
 }
 
+template <typename Factory> struct UnionFactoryArg;
+
+template <typename R, typename Arg> struct UnionFactoryArg<R (*)(Arg)> {
+  using type = Arg;
+};
+
 } // namespace detail
 
 // ============================================================================//
@@ -476,9 +926,13 @@ struct Serializer<T, std::enable_if_t<detail::is_union_type_v<T>>> {
       using CaseT = typename detail::UnionInfo<T>::template CaseT<index>;
       constexpr ::fory::FieldMeta meta =
           detail::UnionInfo<T>::template Meta<index>::value;
+      using SpecProvider = detail::UnionCaseSpecProvider<T, index>;
       constexpr uint32_t field_type_id =
-          detail::resolve_union_type_id<CaseT>(meta);
-      const bool manual = detail::needs_manual_encoding<CaseT>(meta);
+          detail::resolve_union_type_id_for_spec<CaseT, SpecProvider>(meta);
+      constexpr bool configured =
+          detail::union_node_has_override<SpecProvider, 0>();
+      const bool manual =
+          configured || detail::needs_manual_encoding<CaseT>(meta);
       constexpr bool nullable =
           meta.nullable_ || is_nullable_v<detail::decay_t<CaseT>>;
       const RefMode value_ref_mode =
@@ -493,7 +947,12 @@ struct Serializer<T, std::enable_if_t<detail::is_union_type_v<T>>> {
               return;
             }
             ctx.write_uint8(static_cast<uint8_t>(field_type_id));
-            detail::write_union_value_data(value, ctx, field_type_id);
+            if constexpr (detail::union_node_has_override<SpecProvider, 0>()) {
+              detail::write_union_configured_value<CaseT, SpecProvider, 0>(
+                  value, ctx, RefMode::None, false, true);
+            } else {
+              detail::write_union_value_data(value, ctx, field_type_id);
+            }
             return;
           }
           Serializer<CaseT>::write(value, ctx, value_ref_mode, true);
@@ -557,9 +1016,13 @@ struct Serializer<T, std::enable_if_t<detail::is_union_type_v<T>>> {
       using CaseT = typename detail::UnionInfo<T>::template CaseT<index>;
       constexpr ::fory::FieldMeta meta =
           detail::UnionInfo<T>::template Meta<index>::value;
+      using SpecProvider = detail::UnionCaseSpecProvider<T, index>;
       constexpr uint32_t field_type_id =
-          detail::resolve_union_type_id<CaseT>(meta);
-      const bool manual = detail::needs_manual_encoding<CaseT>(meta);
+          detail::resolve_union_type_id_for_spec<CaseT, SpecProvider>(meta);
+      constexpr bool configured =
+          detail::union_node_has_override<SpecProvider, 0>();
+      const bool manual =
+          configured || detail::needs_manual_encoding<CaseT>(meta);
       constexpr bool nullable =
           meta.nullable_ || is_nullable_v<detail::decay_t<CaseT>>;
       const RefMode value_ref_mode =
@@ -587,7 +1050,14 @@ struct Serializer<T, std::enable_if_t<detail::is_union_type_v<T>>> {
           result = default_value();
           return;
         }
-        CaseT value = detail::read_union_value_data<CaseT>(ctx, field_type_id);
+        CaseT value = [&]() {
+          if constexpr (detail::union_node_has_override<SpecProvider, 0>()) {
+            return detail::read_union_configured_value<CaseT, SpecProvider, 0>(
+                ctx, RefMode::None, false);
+          } else {
+            return detail::read_union_value_data<CaseT>(ctx, field_type_id);
+          }
+        }();
         if (FORY_PREDICT_FALSE(ctx.has_error())) {
           result = default_value();
           return;
@@ -633,7 +1103,7 @@ struct Serializer<T, std::enable_if_t<detail::is_union_type_v<T>>> {
         return default_value();
       }
       FieldType field_type;
-      field_type.type_id = type_info->type_id;
+      field_type.set_type_id(type_info->type_id);
       field_type.nullable = false;
       skip_field_value(ctx, field_type, RefMode::None);
       ctx.set_error(Error::invalid_data("Unknown union case id"));
@@ -659,14 +1129,35 @@ private:
 // Union registration macros for generated code
 // ============================================================================//
 
-#define FORY_UNION_CASE_TYPE(tuple) FORY_UNION_CASE_TYPE_IMPL tuple
-#define FORY_UNION_CASE_TYPE_IMPL(type, name, meta) type
-
-#define FORY_UNION_CASE_NAME(tuple) FORY_UNION_CASE_NAME_IMPL tuple
-#define FORY_UNION_CASE_NAME_IMPL(type, name, meta) name
-
-#define FORY_UNION_CASE_META(tuple) FORY_UNION_CASE_META_IMPL tuple
-#define FORY_UNION_CASE_META_IMPL(type, name, meta) meta
+#define FORY_UNION_TUPLE_SIZE(tuple) FORY_UNION_TUPLE_SIZE_IMPL tuple
+#define FORY_UNION_TUPLE_SIZE_IMPL(...)                                        \
+  FORY_UNION_TUPLE_SIZE_SELECT(__VA_ARGS__, 3, 2, 1, 0)
+#define FORY_UNION_TUPLE_SIZE_SELECT(_1, _2, _3, N, ...) N
+
+#define FORY_UNION_CASE_NAME(tuple)                                            \
+  FORY_PP_CONCAT(FORY_UNION_CASE_NAME_, FORY_UNION_TUPLE_SIZE(tuple))(tuple)
+#define FORY_UNION_CASE_NAME_2(tuple) FORY_UNION_CASE_NAME_2_IMPL tuple
+#define FORY_UNION_CASE_NAME_2_IMPL(name, meta) name
+#define FORY_UNION_CASE_NAME_3(tuple) FORY_UNION_CASE_NAME_3_IMPL tuple
+#define FORY_UNION_CASE_NAME_3_IMPL(name, type, meta) name
+
+#define FORY_UNION_CASE_TYPE(Type, tuple)                                      \
+  FORY_PP_CONCAT(FORY_UNION_CASE_TYPE_, FORY_UNION_TUPLE_SIZE(tuple))          \
+  (Type, tuple)
+#define FORY_UNION_CASE_TYPE_2(Type, tuple)                                    \
+  FORY_UNION_CASE_TYPE_2_IMPL(Type, tuple)
+#define FORY_UNION_CASE_TYPE_2_IMPL(Type, tuple)                               \
+  typename ::fory::serialization::detail::UnionFactoryArg<                     \
+      decltype(&Type::FORY_UNION_CASE_NAME(tuple))>::type
+#define FORY_UNION_CASE_TYPE_3(Type, tuple) FORY_UNION_CASE_TYPE_3_IMPL tuple
+#define FORY_UNION_CASE_TYPE_3_IMPL(name, type, meta) type
+
+#define FORY_UNION_CASE_META(tuple)                                            \
+  FORY_PP_CONCAT(FORY_UNION_CASE_META_, FORY_UNION_TUPLE_SIZE(tuple))(tuple)
+#define FORY_UNION_CASE_META_2(tuple) FORY_UNION_CASE_META_2_IMPL tuple
+#define FORY_UNION_CASE_META_2_IMPL(name, meta) meta
+#define FORY_UNION_CASE_META_3(tuple) FORY_UNION_CASE_META_3_IMPL tuple
+#define FORY_UNION_CASE_META_3_IMPL(name, type, meta) meta
 
 #define FORY_UNION_PP_FOREACH_2(M, A, ...)                                     \
   FORY_PP_INVOKE(FORY_PP_CONCAT(FORY_UNION_PP_FOREACH_2_IMPL_,                 \
@@ -808,13 +1299,18 @@ private:
 #define FORY_UNION_CASE_ID(Type, tuple)                                        \
   static_cast<uint32_t>(FORY_UNION_CASE_META(tuple).id_)
 
+#define FORY_UNION_CASE_REQUIRE_ID(Type, tuple)                                \
+  static_assert(FORY_UNION_CASE_META(tuple).has_id_,                           \
+                "FORY_UNION cases must use fory::F(id)");
+
 #define FORY_UNION_CASE_ID_ENTRY(Type, tuple) FORY_UNION_CASE_ID(Type, tuple),
 
-#define FORY_UNION_CASE_TYPE_VALUE(Type, tuple) FORY_UNION_CASE_TYPE(tuple)
+#define FORY_UNION_CASE_TYPE_VALUE(Type, tuple)                                \
+  FORY_UNION_CASE_TYPE(Type, tuple)
 #define FORY_UNION_CASE_META_VALUE(Type, tuple) FORY_UNION_CASE_META(tuple)
 #define FORY_UNION_CASE_ID_VALUE(Type, tuple) FORY_UNION_CASE_ID(Type, tuple)
 #define FORY_UNION_CASE_FACTORY_VALUE(Type, tuple)                             \
-  static_cast<Type (*)(FORY_UNION_CASE_TYPE(tuple))>(                          \
+  static_cast<Type (*)(FORY_UNION_CASE_TYPE(Type, tuple))>(                    \
       &Type::FORY_UNION_CASE_NAME(tuple))
 
 #define FORY_UNION_IDS_DESCRIPTOR_NAME(line)                                   \
@@ -838,6 +1334,8 @@ private:
   static_assert(true)
 
 #define FORY_UNION_CASE(Type, CaseId, CaseType, Factory, MetaExpr)             \
+  static_assert((MetaExpr).has_id_,                                            \
+                "FORY_UNION_CASE metadata must use fory::F(id)");              \
   struct FORY_UNION_CASE_DESCRIPTOR_NAME(__LINE__) {                           \
     using CaseT = CaseType;                                                    \
     static constexpr ::fory::FieldMeta meta = MetaExpr;                        \
@@ -855,6 +1353,7 @@ private:
                 "FORY_UNION supports up to 16 cases; use "                     \
                 "FORY_UNION_IDS/FORY_UNION_CASE "                              \
                 "for larger unions");                                          \
+  FORY_UNION_PP_FOREACH_2(FORY_UNION_CASE_REQUIRE_ID, Type, __VA_ARGS__)       \
   struct FORY_UNION_DESCRIPTOR_NAME(__LINE__) {                                \
     using UnionType = Type;                                                    \
     static constexpr size_t case_count = FORY_PP_NARG(__VA_ARGS__);            \
diff --git a/cpp/fory/serialization/xlang_test_main.cc b/cpp/fory/serialization/xlang_test_main.cc
index db6d02a82..29e84488b 100644
--- a/cpp/fory/serialization/xlang_test_main.cc
+++ b/cpp/fory/serialization/xlang_test_main.cc
@@ -616,6 +616,32 @@ struct CircularRefStruct {
   FORY_STRUCT(CircularRefStruct, name, self_ref);
 };
 
+struct NestedAnnotatedContainerSchemaConsistent {
+  std::map<uint32_t, std::vector<uint64_t>> values;
+
+  bool operator==(const NestedAnnotatedContainerSchemaConsistent &other) const {
+    return values == other.values;
+  }
+
+  FORY_STRUCT(NestedAnnotatedContainerSchemaConsistent,
+              (values,
+               fory::F().map(fory::T::uint32().fixed(),
+                             fory::T::list(fory::T::uint64().tagged()))));
+};
+
+struct NestedAnnotatedContainerCompatible {
+  std::map<uint32_t, std::vector<uint64_t>> values;
+
+  bool operator==(const NestedAnnotatedContainerCompatible &other) const {
+    return values == other.values;
+  }
+
+  FORY_STRUCT(NestedAnnotatedContainerCompatible,
+              (values,
+               fory::F().map(fory::T::uint32().fixed(),
+                             fory::T::list(fory::T::uint64().tagged()))));
+};
+
 // ============================================================================
 // Unsigned Number Test Types
 // ============================================================================
@@ -630,11 +656,9 @@ struct UnsignedSchemaConsistentSimple {
     return u64_tagged == other.u64_tagged &&
            u64_tagged_nullable == other.u64_tagged_nullable;
   }
-  FORY_STRUCT(UnsignedSchemaConsistentSimple, u64_tagged, u64_tagged_nullable);
+  FORY_STRUCT(UnsignedSchemaConsistentSimple, (u64_tagged, fory::F().tagged()),
+              (u64_tagged_nullable, fory::F().nullable().tagged()));
 };
-FORY_FIELD_CONFIG(UnsignedSchemaConsistentSimple,
-                  (u64_tagged, fory::F().tagged()),
-                  (u64_tagged_nullable, fory::F().nullable().tagged()));
 
 // UnsignedSchemaConsistent (type id 501)
 // Test struct for unsigned numbers in SCHEMA_CONSISTENT mode.
@@ -674,26 +698,20 @@ struct UnsignedSchemaConsistent {
            u64_fixed_nullable_field == other.u64_fixed_nullable_field &&
            u64_tagged_nullable_field == other.u64_tagged_nullable_field;
   }
-  FORY_STRUCT(UnsignedSchemaConsistent, u8_field, u16_field, u32_var_field,
-              u32_fixed_field, u64_var_field, u64_fixed_field, u64_tagged_field,
-              u8_nullable_field, u16_nullable_field, u32_var_nullable_field,
-              u32_fixed_nullable_field, u64_var_nullable_field,
-              u64_fixed_nullable_field, u64_tagged_nullable_field);
+  FORY_STRUCT(UnsignedSchemaConsistent, (u8_field, fory::F()),
+              (u16_field, fory::F()), (u32_var_field, fory::F().varint()),
+              (u32_fixed_field, fory::F().fixed()),
+              (u64_var_field, fory::F().varint()),
+              (u64_fixed_field, fory::F().fixed()),
+              (u64_tagged_field, fory::F().tagged()),
+              (u8_nullable_field, fory::F().nullable()),
+              (u16_nullable_field, fory::F().nullable()),
+              (u32_var_nullable_field, fory::F().nullable().varint()),
+              (u32_fixed_nullable_field, fory::F().nullable().fixed()),
+              (u64_var_nullable_field, fory::F().nullable().varint()),
+              (u64_fixed_nullable_field, fory::F().nullable().fixed()),
+              (u64_tagged_nullable_field, fory::F().nullable().tagged()));
 };
-// Use new FORY_FIELD_CONFIG with builder pattern for encoding specification
-FORY_FIELD_CONFIG(UnsignedSchemaConsistent, (u8_field, fory::F()),
-                  (u16_field, fory::F()), (u32_var_field, fory::F().varint()),
-                  (u32_fixed_field, fory::F().fixed()),
-                  (u64_var_field, fory::F().varint()),
-                  (u64_fixed_field, fory::F().fixed()),
-                  (u64_tagged_field, fory::F().tagged()),
-                  (u8_nullable_field, fory::F().nullable()),
-                  (u16_nullable_field, fory::F().nullable()),
-                  (u32_var_nullable_field, fory::F().nullable().varint()),
-                  (u32_fixed_nullable_field, fory::F().nullable().fixed()),
-                  (u64_var_nullable_field, fory::F().nullable().varint()),
-                  (u64_fixed_nullable_field, fory::F().nullable().fixed()),
-                  (u64_tagged_nullable_field, fory::F().nullable().tagged()));
 
 // UnsignedSchemaCompatible (type id 502)
 // Test struct for unsigned numbers in COMPATIBLE mode.
@@ -733,28 +751,20 @@ struct UnsignedSchemaCompatible {
            u64_fixed_field2 == other.u64_fixed_field2 &&
            u64_tagged_field2 == other.u64_tagged_field2;
   }
-  FORY_STRUCT(UnsignedSchemaCompatible, u8_field1, u16_field1, u32_var_field1,
-              u32_fixed_field1, u64_var_field1, u64_fixed_field1,
-              u64_tagged_field1, u8_field2, u16_field2, u32_var_field2,
-              u32_fixed_field2, u64_var_field2, u64_fixed_field2,
-              u64_tagged_field2);
+  FORY_STRUCT(UnsignedSchemaCompatible, (u8_field1, fory::F().nullable()),
+              (u16_field1, fory::F().nullable()),
+              (u32_var_field1, fory::F().nullable().varint()),
+              (u32_fixed_field1, fory::F().nullable().fixed()),
+              (u64_var_field1, fory::F().nullable().varint()),
+              (u64_fixed_field1, fory::F().nullable().fixed()),
+              (u64_tagged_field1, fory::F().nullable().tagged()),
+              (u8_field2, fory::F()), (u16_field2, fory::F()),
+              (u32_var_field2, fory::F().varint()),
+              (u32_fixed_field2, fory::F().fixed()),
+              (u64_var_field2, fory::F().varint()),
+              (u64_fixed_field2, fory::F().fixed()),
+              (u64_tagged_field2, fory::F().tagged()));
 };
-// Use new FORY_FIELD_CONFIG with builder pattern for encoding specification
-// Group 1: nullable in C++ (std::optional), non-nullable in Java
-// Group 2: non-nullable in C++, nullable in Java
-FORY_FIELD_CONFIG(UnsignedSchemaCompatible, (u8_field1, fory::F().nullable()),
-                  (u16_field1, fory::F().nullable()),
-                  (u32_var_field1, fory::F().nullable().varint()),
-                  (u32_fixed_field1, fory::F().nullable().fixed()),
-                  (u64_var_field1, fory::F().nullable().varint()),
-                  (u64_fixed_field1, fory::F().nullable().fixed()),
-                  (u64_tagged_field1, fory::F().nullable().tagged()),
-                  (u8_field2, fory::F()), (u16_field2, fory::F()),
-                  (u32_var_field2, fory::F().varint()),
-                  (u32_fixed_field2, fory::F().fixed()),
-                  (u64_var_field2, fory::F().varint()),
-                  (u64_fixed_field2, fory::F().fixed()),
-                  (u64_tagged_field2, fory::F().tagged()));
 
 namespace fory {
 namespace serialization {
@@ -955,6 +965,10 @@ void run_test_ref_compatible(const std::string &data_file);
 void run_test_collection_element_ref_override(const std::string &data_file);
 void run_test_circular_ref_schema_consistent(const std::string &data_file);
 void run_test_circular_ref_compatible(const std::string &data_file);
+void run_test_nested_annotated_container_schema_consistent(
+    const std::string &data_file);
+void run_test_nested_annotated_container_compatible(
+    const std::string &data_file);
 void run_test_decimal(const std::string &data_file);
 void run_test_unsigned_schema_consistent_simple(const std::string &data_file);
 void run_test_unsigned_schema_consistent(const std::string &data_file);
@@ -1069,6 +1083,11 @@ int main(int argc, char **argv) {
       run_test_circular_ref_schema_consistent(data_file);
     } else if (case_name == "test_circular_ref_compatible") {
       run_test_circular_ref_compatible(data_file);
+    } else if (case_name ==
+               "test_nested_annotated_container_schema_consistent") {
+      run_test_nested_annotated_container_schema_consistent(data_file);
+    } else if (case_name == "test_nested_annotated_container_compatible") {
+      run_test_nested_annotated_container_compatible(data_file);
     } else if (case_name == "test_unsigned_schema_consistent_simple") {
       run_test_unsigned_schema_consistent_simple(data_file);
     } else if (case_name == "test_unsigned_schema_consistent") {
@@ -2790,6 +2809,45 @@ void run_test_circular_ref_compatible(const std::string &data_file) {
   write_file(data_file, out);
 }
 
+std::map<uint32_t, std::vector<uint64_t>>
+build_nested_annotated_container_values() {
+  return {{4000000000u, {7u, 1000000000u}}, {3u, {42u}}};
+}
+
+template <typename T>
+void run_nested_annotated_container_test(Fory &fory,
+                                         const std::string &data_file) {
+  auto bytes = read_file(data_file);
+  Buffer buffer = make_buffer(bytes);
+  auto value = read_next<T>(fory, buffer);
+  const auto expected = build_nested_annotated_container_values();
+  if (value.values != expected) {
+    fail("Nested annotated container values mismatch");
+  }
+
+  std::vector<uint8_t> out;
+  append_serialized(fory, value, out);
+  write_file(data_file, out);
+}
+
+void run_test_nested_annotated_container_schema_consistent(
+    const std::string &data_file) {
+  auto fory = build_fory(false, true, true);
+  ensure_ok(fory.register_struct<NestedAnnotatedContainerSchemaConsistent>(801),
+            "register NestedAnnotatedContainerSchemaConsistent");
+  run_nested_annotated_container_test<NestedAnnotatedContainerSchemaConsistent>(
+      fory, data_file);
+}
+
+void run_test_nested_annotated_container_compatible(
+    const std::string &data_file) {
+  auto fory = build_fory(true, true);
+  ensure_ok(fory.register_struct<NestedAnnotatedContainerCompatible>(802),
+            "register NestedAnnotatedContainerCompatible");
+  run_nested_annotated_container_test<NestedAnnotatedContainerCompatible>(
+      fory, data_file);
+}
+
 // ============================================================================
 // Unsigned Number Tests
 // ============================================================================
diff --git a/docs/benchmarks/cpp/README.md b/docs/benchmarks/cpp/README.md
index 9811c8a54..0d7c25a60 100644
--- a/docs/benchmarks/cpp/README.md
+++ b/docs/benchmarks/cpp/README.md
@@ -1,6 +1,6 @@
 # C++ Benchmark Performance Report
 
-_Generated on 2026-02-19 10:20:08_
+_Generated on 2026-04-29 21:16:35_
 
 ## How to Generate This Report
 
@@ -21,7 +21,7 @@ python benchmark_report.py --json-file build/benchmark_results.json --output-dir
 | CPU Cores (Physical)       | 12                        |
 | CPU Cores (Logical)        | 12                        |
 | Total RAM (GB)             | 48.0                      |
-| Benchmark Date             | 2026-02-19T10:19:31+08:00 |
+| Benchmark Date             | 2026-04-29T21:15:49+08:00 |
 | CPU Cores (from benchmark) | 12                        |
 
 ## Benchmark Plots
@@ -32,65 +32,65 @@ All class-level plots below show throughput (ops/sec).
 
 ![Throughput](throughput.png)
 
-### Mediacontent
-
-![Mediacontent](mediacontent.png)
-
-### Mediacontentlist
+### Struct
 
-![Mediacontentlist](mediacontentlist.png)
+![Struct](struct.png)
 
 ### Sample
 
 ![Sample](sample.png)
 
-### Samplelist
-
-![Samplelist](samplelist.png)
-
-### Struct
+### Mediacontent
 
-![Struct](struct.png)
+![Mediacontent](mediacontent.png)
 
 ### Structlist
 
 ![Structlist](structlist.png)
 
+### Samplelist
+
+![Samplelist](samplelist.png)
+
+### Mediacontentlist
+
+![Mediacontentlist](mediacontentlist.png)
+
 ## Benchmark Results
 
 ### Timing Results (nanoseconds)
 
 | Datatype         | Operation   | fory (ns) | protobuf (ns) | msgpack (ns) | Fastest |
 | ---------------- | ----------- | --------- | ------------- | ------------ | ------- |
-| MediaContent     | Serialize   | 120.4     | 863.8         | 281.1        | fory    |
-| MediaContent     | Deserialize | 397.9     | 1197.9        | 2768.6       | fory    |
-| MediaContentList | Serialize   | 480.9     | 4744.3        | 1407.5       | fory    |
-| MediaContentList | Deserialize | 2022.1    | 6426.0        | 13595.9      | fory    |
-| Sample           | Serialize   | 72.8      | 92.0          | 296.1        | fory    |
-| Sample           | Deserialize | 328.2     | 641.2         | 2642.9       | fory    |
-| SampleList       | Serialize   | 287.3     | 4761.2        | 1506.7       | fory    |
-| SampleList       | Deserialize | 1711.8    | 4875.2        | 13232.0      | fory    |
-| Struct           | Serialize   | 27.3      | 32.4          | 55.2         | fory    |
-| Struct           | Deserialize | 21.4      | 25.0          | 747.3        | fory    |
-| StructList       | Serialize   | 69.4      | 419.8         | 285.9        | fory    |
-| StructList       | Deserialize | 129.4     | 334.4         | 3385.8       | fory    |
+| Struct           | Serialize   | 25.6      | 34.3          | 54.3         | fory    |
+| Struct           | Deserialize | 23.1      | 25.5          | 769.9        | fory    |
+| Sample           | Serialize   | 68.4      | 96.6          | 317.8        | fory    |
+| Sample           | Deserialize | 326.2     | 662.5         | 2633.8       | fory    |
+| MediaContent     | Serialize   | 119.5     | 854.1         | 292.7        | fory    |
+| MediaContent     | Deserialize | 391.2     | 1207.2        | 2809.7       | fory    |
+| StructList       | Serialize   | 72.6      | 421.4         | 286.4        | fory    |
+| StructList       | Deserialize | 122.7     | 364.1         | 3400.5       | fory    |
+| SampleList       | Serialize   | 284.0     | 4898.1        | 1549.8       | fory    |
+| SampleList       | Deserialize | 1824.8    | 5047.4        | 13206.2      | fory    |
+| MediaContentList | Serialize   | 477.8     | 4788.9        | 1461.9       | fory    |
+| MediaContentList | Deserialize | 2053.4    | 6561.8        | 13702.4      | fory    |
 
 ### Throughput Results (ops/sec)
 
 | Datatype         | Operation   | fory TPS   | protobuf TPS | msgpack TPS | Fastest |
 | ---------------- | ----------- | ---------- | ------------ | ----------- | ------- |
-| MediaContent     | Serialize   | 8,306,128  | 1,157,712    | 3,557,700   | fory    |
-| MediaContent     | Deserialize | 2,513,488  | 834,808      | 361,190     | fory    |
-| MediaContentList | Serialize   | 2,079,229  | 210,777      | 710,492     | fory    |
-| MediaContentList | Deserialize | 494,523    | 155,617      | 73,551      | fory    |
-| Sample           | Serialize   | 13,745,041 | 10,871,787   | 3,377,292   | fory    |
-| Sample           | Deserialize | 3,047,224  | 1,559,633    | 378,369     | fory    |
-| SampleList       | Serialize   | 3,481,110  | 210,029      | 663,693     | fory    |
-| SampleList       | Deserialize | 584,168    | 205,121      | 75,574      | fory    |
-| Struct           | Serialize   | 36,672,581 | 30,900,039   | 18,114,682  | fory    |
-| Struct           | Deserialize | 46,637,124 | 39,947,557   | 1,338,225   | fory    |
-| StructList       | Serialize   | 14,419,548 | 2,381,886    | 3,497,903   | fory    |
-| StructList       | Deserialize | 7,729,173  | 2,990,652    | 295,353     | fory    |
+| Struct           | Serialize   | 39,028,134 | 29,190,083   | 18,400,879  | fory    |
+| Struct           | Deserialize | 43,236,159 | 39,162,796   | 1,298,861   | fory    |
+| Sample           | Serialize   | 14,612,190 | 10,356,947   | 3,146,384   | fory    |
+| Sample           | Deserialize | 3,065,887  | 1,509,472    | 379,673     | fory    |
+| MediaContent     | Serialize   | 8,364,768  | 1,170,804    | 3,416,327   | fory    |
+| MediaContent     | Deserialize | 2,556,154  | 828,370      | 355,913     | fory    |
+| StructList       | Serialize   | 13,768,856 | 2,373,115    | 3,491,609   | fory    |
+| StructList       | Deserialize | 8,149,855  | 2,746,704    | 294,075     | fory    |
+| SampleList       | Serialize   | 3,521,702  | 204,162      | 645,233     | fory    |
+| SampleList       | Deserialize | 547,995    | 198,123      | 75,722      | fory    |
+| MediaContentList | Serialize   | 2,092,938  | 208,817      | 684,050     | fory    |
+| MediaContentList | Deserialize | 486,993    | 152,398      | 72,980      | fory    |
 
 ### Serialized Data Sizes (bytes)
 
diff --git a/docs/benchmarks/cpp/mediacontent.png b/docs/benchmarks/cpp/mediacontent.png
index 7fd07e461..6cb966085 100644
Binary files a/docs/benchmarks/cpp/mediacontent.png and b/docs/benchmarks/cpp/mediacontent.png differ
diff --git a/docs/benchmarks/cpp/mediacontentlist.png b/docs/benchmarks/cpp/mediacontentlist.png
index 1fccde649..267ff574f 100644
Binary files a/docs/benchmarks/cpp/mediacontentlist.png and b/docs/benchmarks/cpp/mediacontentlist.png differ
diff --git a/docs/benchmarks/cpp/sample.png b/docs/benchmarks/cpp/sample.png
index 127a6e1aa..bb25b5433 100644
Binary files a/docs/benchmarks/cpp/sample.png and b/docs/benchmarks/cpp/sample.png differ
diff --git a/docs/benchmarks/cpp/samplelist.png b/docs/benchmarks/cpp/samplelist.png
index 2ebde46ef..bdaa4f05d 100644
Binary files a/docs/benchmarks/cpp/samplelist.png and b/docs/benchmarks/cpp/samplelist.png differ
diff --git a/docs/benchmarks/cpp/struct.png b/docs/benchmarks/cpp/struct.png
index 88e586ff6..398ab52f8 100644
Binary files a/docs/benchmarks/cpp/struct.png and b/docs/benchmarks/cpp/struct.png differ
diff --git a/docs/benchmarks/cpp/structlist.png b/docs/benchmarks/cpp/structlist.png
index c676e84df..47c5f680f 100644
Binary files a/docs/benchmarks/cpp/structlist.png and b/docs/benchmarks/cpp/structlist.png differ
diff --git a/docs/benchmarks/cpp/throughput.png b/docs/benchmarks/cpp/throughput.png
index 9ffa742b6..525f58ff7 100644
Binary files a/docs/benchmarks/cpp/throughput.png and b/docs/benchmarks/cpp/throughput.png differ
diff --git a/docs/compiler/generated-code.md b/docs/compiler/generated-code.md
index 583cd79b6..5098fb2af 100644
--- a/docs/compiler/generated-code.md
+++ b/docs/compiler/generated-code.md
@@ -515,7 +515,9 @@ class Animal final {
 };
 ```
 
-Generated headers also include `FORY_UNION`, `FORY_FIELD_CONFIG`, `FORY_ENUM`, and `FORY_STRUCT` macros for serialization metadata.
+Generated headers include `FORY_UNION`, `FORY_ENUM`, and `FORY_STRUCT` macros
+for serialization metadata. Field and payload configuration is embedded in the
+generated `FORY_STRUCT`/`FORY_UNION` entries.
 
 ### Registration
 
diff --git a/docs/compiler/schema-idl.md b/docs/compiler/schema-idl.md
index bffa4eb46..9f514fd0d 100644
--- a/docs/compiler/schema-idl.md
+++ b/docs/compiler/schema-idl.md
@@ -1237,8 +1237,11 @@ message Order {
 Use the `list<...>` type for list fields. `repeated` is accepted as an alias. See [Field Modifiers](#field-modifiers) for
 modifier combinations and language mapping.
 
-Nested collection types are not supported. Use a message wrapper if you need
-`list<list<...>>`, `list<map<...>>`, or `map<..., list<...>>`.
+Nested collection support is target-capability based. The C++ generator accepts
+nested collection specs such as `list<list<...>>`, `list<map<...>>`, and
+`map<..., list<...>>`; targets that have not implemented nested field specs
+continue to reject them. Use a message wrapper when you need portable schemas
+across all targets.
 
 #### Map
 
diff --git a/docs/guide/cpp/field-configuration.md b/docs/guide/cpp/field-configuration.md
index 94f5170dd..02029453a 100644
--- a/docs/guide/cpp/field-configuration.md
+++ b/docs/guide/cpp/field-configuration.md
@@ -19,550 +19,188 @@ license: |
   limitations under the License.
 ---
 
-This page explains how to configure field-level metadata for serialization.
-
-## Overview
-
-Apache Fory™ provides three ways to configure field-level metadata at compile time:
-
-1. **`fory::field<>` template** - Inline metadata in struct definition with wrapper types
-2. **`FORY_FIELD_TAGS` macro** - Non-invasive metadata for basic field configuration
-3. **`FORY_FIELD_CONFIG` macro** - Advanced configuration with builder pattern and encoding control
-
-These enable:
-
-- **Tag IDs**: Assign compact numeric IDs for schema evolution
-- **Nullability**: Mark pointer fields as nullable
-- **Reference Tracking**: Enable reference tracking for shared pointers
-- **Encoding Control**: Specify wire format for integers (varint, fixed, tagged)
-- **Dynamic Dispatch**: Control polymorphic type info for smart pointers
-
-**Comparison:**
-
-| Feature                 | `fory::field<>`       | `FORY_FIELD_TAGS` | `FORY_FIELD_CONFIG`       |
-| ----------------------- | --------------------- | ----------------- | ------------------------- |
-| **Struct modification** | Required (wrap types) | None              | None                      |
-| **Encoding control**    | No                    | No                | Yes (varint/fixed/tagged) |
-| **Builder pattern**     | No                    | No                | Yes                       |
-| **Dynamic control**     | Yes                   | No                | Yes                       |
-| **Compile-time verify** | Yes                   | Limited           | Yes (member pointers)     |
-| **Cross-lang compat**   | Limited               | Limited           | Full                      |
-| **Recommended for**     | Simple structs        | Third-party types | Complex/xlang structs     |
-
-## The fory::field Template
-
-```cpp
-template <typename T, int16_t Id, typename... Options>
-class field;
-```
-
-### Template Parameters
-
-| Parameter | Description                                      |
-| --------- | ------------------------------------------------ |
-| `T`       | The underlying field type                        |
-| `Id`      | Field tag ID (int16_t) for compact serialization |
-| `Options` | Optional tags: `fory::nullable`, `fory::ref`     |
-
-### Basic Usage
+Field configuration is embedded directly in `FORY_STRUCT`. A field entry may be
+bare, or it may be a tuple containing the member name and a `fory::F(...)`
+builder:
 
 ```cpp
 #include "fory/serialization/fory.h"
 
-using namespace fory::serialization;
-
-struct Person {
-  fory::field<std::string, 0> name;
-  fory::field<int32_t, 1> age;
-  fory::field<std::optional<std::string>, 2> nickname;
+struct DataV2 {
+  uint32_t id;
+  uint64_t timestamp;
+  std::optional<uint32_t> version;
 };
-FORY_STRUCT(Person, name, age, nickname);
-```
 
-The `fory::field<>` wrapper is transparent - you can use it like the underlying type:
-
-```cpp
-Person person;
-person.name = "Alice";           // Direct assignment
-person.age = 30;
-std::string n = person.name;     // Implicit conversion
-int a = person.age.get();        // Explicit get()
+FORY_STRUCT(DataV2, id, (timestamp, fory::F().tagged()), version);
 ```
 
-## Tag Types
+The configuration is compile-time metadata. It does not allocate codec objects
+or add virtual dispatch on the serialization path.
 
-### fory::nullable
+## Field Identity
 
-Marks a smart pointer field as nullable (can be `nullptr`):
+`fory::F()` uses name-mode field identity. Bare fields are also name-mode:
 
 ```cpp
-struct Node {
-  fory::field<std::string, 0> name;
-  fory::field<std::shared_ptr<Node>, 1, fory::nullable> next;  // Can be nullptr
-};
-FORY_STRUCT(Node, name, next);
+FORY_STRUCT(DataV2, id, (timestamp, fory::F().tagged()), version);
 ```
 
-**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:
+`fory::F(id)` uses id-mode field identity:
 
 ```cpp
-// Correct: use std::optional for nullable primitives
-fory::field<std::optional<int32_t>, 0> optional_value;
-
-// Wrong: nullable is not allowed for primitives
-// fory::field<int32_t, 0, fory::nullable> value;  // Compile error!
+FORY_STRUCT(DataV2, (id, fory::F(0)), (timestamp, fory::F(1).tagged()),
+            (version, fory::F(2)));
 ```
 
-### fory::not_null
-
-Explicitly marks a pointer field as non-nullable. This is the default for smart pointers, but can be used for documentation:
-
-```cpp
-fory::field<std::shared_ptr<Data>, 0, fory::not_null> data;  // Must not be nullptr
-```
+A struct must use exactly one identity mode. If any field uses `fory::F(id)`,
+every field in that `FORY_STRUCT` must use `fory::F(id)`. Mixed name/id mode is a
+compile-time error.
 
-**Valid for:** `std::shared_ptr<T>`, `fory::serialization::SharedWeak<T>`, `std::unique_ptr<T>`
+## Scalar Encoding
 
-### fory::ref
-
-Enables reference tracking for shared pointer fields. When multiple fields reference the same object, it will be serialized once and shared:
-
-```cpp
-struct Graph {
-  fory::field<std::string, 0> name;
-  fory::field<std::shared_ptr<Graph>, 1, fory::ref> left;    // Ref tracked
-  fory::field<std::shared_ptr<Graph>, 2, fory::ref> right;   // Ref tracked
-};
-FORY_STRUCT(Graph, name, left, right);
-```
-
-**Valid for:** `std::shared_ptr<T>`, `fory::serialization::SharedWeak<T>` (requires shared ownership)
-
-### fory::dynamic\<V\>
-
-Controls whether type info is written for polymorphic smart pointer fields:
-
-- `fory::dynamic<true>`: Force type info to be written (enable runtime subtype support)
-- `fory::dynamic<false>`: skip type info (use declared type directly, no dynamic dispatch)
-
-By default, Fory auto-detects polymorphism via `std::is_polymorphic<T>`. Use this tag to override:
+Integer encoding is configured on the field or on a nested value-node spec:
 
 ```cpp
-// Base class with virtual methods (detected as polymorphic by default)
-struct Animal {
-  virtual ~Animal() = default;
-  virtual std::string speak() const = 0;
+struct Counters {
+  uint32_t fixed_id;
+  uint64_t tagged_time;
+  int64_t signed_score;
 };
 
-struct Zoo {
-  // Auto: type info written because Animal has virtual methods
-  fory::field<std::shared_ptr<Animal>, 0, fory::nullable> animal;
-
-  // Force non-dynamic: skip type info even though Animal has virtual methods
-  // Use when you know the runtime type will always be exactly as declared
-  fory::field<std::shared_ptr<Animal>, 1, fory::nullable, fory::dynamic<false>> fixed_animal;
-};
-FORY_STRUCT(Zoo, animal, fixed_animal);
+FORY_STRUCT(Counters, (fixed_id, fory::F().fixed()),
+            (tagged_time, fory::F().tagged()),
+            (signed_score, fory::F().varint()));
 ```
 
-**Valid for:** `std::shared_ptr<T>`, `std::unique_ptr<T>`
-
-### Combining Tags
+Supported scalar encoding methods are:
 
-Multiple tags can be combined for shared pointers and SharedWeak:
+| Method     | Meaning                                      |
+| ---------- | -------------------------------------------- |
+| `fixed()`  | Fixed-width integer encoding where valid     |
+| `varint()` | Variable-length integer encoding where valid |
+| `tagged()` | Tagged integer encoding where valid          |
 
-```cpp
-// Nullable + ref tracking
-fory::field<std::shared_ptr<Node>, 0, fory::nullable, fory::ref> link;
-```
+Invalid scalar/type combinations fail at compile time.
 
-## Type Rules
+## Nested Specs
 
-| 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
+Use the `fory::T` namespace for value-node specs inside containers and wrapper
+carriers. Untyped specs infer the actual C++ type at that node:
 
 ```cpp
-#include "fory/serialization/fory.h"
-
-using namespace fory::serialization;
-
-// Define a struct with various field configurations
-struct Document {
-  // Required fields (non-nullable)
-  fory::field<std::string, 0> title;
-  fory::field<int32_t, 1> version;
-
-  // Optional primitive using std::optional
-  fory::field<std::optional<std::string>, 2> description;
+namespace T = fory::T;
 
-  // Nullable pointer
-  fory::field<std::unique_ptr<std::string>, 3, fory::nullable> metadata;
-
-  // Reference-tracked shared pointer
-  fory::field<std::shared_ptr<Document>, 4, fory::ref> parent;
-
-  // Nullable + reference-tracked
-  fory::field<std::shared_ptr<Document>, 5, fory::nullable, fory::ref> related;
+struct Foo {
+  std::vector<uint32_t> values;
+  std::map<uint32_t, std::vector<int64_t>> nested;
 };
-FORY_STRUCT(Document, title, version, description, metadata, parent, related);
-
-int main() {
-  auto fory = Fory::builder().xlang(true).build();
-  fory.register_struct<Document>(100);
-
-  Document doc;
-  doc.title = "My Document";
-  doc.version = 1;
-  doc.description = "A sample document";
-  doc.metadata = nullptr;  // Allowed because nullable
-  doc.parent = std::make_shared<Document>();
-  doc.parent.get()->title = "Parent Doc";
-  doc.related = nullptr;  // Allowed because nullable
-
-  auto bytes = fory.serialize(doc).value();
-  auto decoded = fory.deserialize<Document>(bytes).value();
-}
-```
-
-## Compile-Time Validation
-
-Invalid configurations are caught at compile time:
-
-```cpp
-// Error: nullable and not_null are mutually exclusive
-fory::field<std::shared_ptr<int>, 0, fory::nullable, fory::not_null> bad1;
 
-// Error: nullable only valid for smart pointers
-fory::field<int32_t, 0, fory::nullable> bad2;
-
-// Error: ref only valid for shared_ptr
-fory::field<std::unique_ptr<int>, 0, fory::ref> bad3;
-
-// Error: options not allowed for std::optional (inherently nullable)
-fory::field<std::optional<int>, 0, fory::nullable> bad4;
-```
-
-## Backwards Compatibility
-
-Existing structs without `fory::field<>` wrappers continue to work:
-
-```cpp
-// Old style - still works
-struct LegacyPerson {
-  std::string name;
-  int32_t age;
-};
-FORY_STRUCT(LegacyPerson, name, age);
-
-// New style with field metadata
-struct ModernPerson {
-  fory::field<std::string, 0> name;
-  fory::field<int32_t, 1> age;
-};
-FORY_STRUCT(ModernPerson, name, age);
+FORY_STRUCT(Foo,
+            (values, fory::F().list(T::fixed())),
+            (nested, fory::F().map(T::varint(),
+                                   T::list(T::tagged()))));
 ```
 
-## FORY_FIELD_TAGS Macro
-
-The `FORY_FIELD_TAGS` macro provides a non-invasive way to add field metadata without modifying struct definitions. This is useful for:
-
-- **Third-party types**: Add metadata to types you don't own
-- **Clean structs**: Keep struct definitions as pure C++
-- **Isolated dependencies**: Confine Fory headers to serialization config files
-
-### Usage
+Typed specs are optional validators and make the intended node type explicit:
 
 ```cpp
-// user_types.h - NO fory headers needed!
-struct Document {
-  std::string title;
-  int32_t version;
-  std::optional<std::string> description;
-  std::shared_ptr<User> author;
-  std::shared_ptr<User> reviewer;
-  std::shared_ptr<Document> parent;
-  std::unique_ptr<Data> data;
-};
-
-// serialization_config.cpp - fory config isolated here
-#include "fory/serialization/fory.h"
-#include "user_types.h"
-
-FORY_STRUCT(Document, title, version, description, author, reviewer, parent, data)
-
-FORY_FIELD_TAGS(Document,
-  (title, 0),                      // string: non-nullable
-  (version, 1),                    // int: 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 tracking
-  (data, 6, nullable)              // unique_ptr: nullable
-)
+FORY_STRUCT(Foo, (nested, fory::F().map(T::uint32().varint(),
+                                        T::list(T::int64().tagged()))));
 ```
 
-### FORY_FIELD_TAGS Options
-
-| Field Type           | Valid Combinations                                                                       |
-| -------------------- | ---------------------------------------------------------------------------------------- |
-| Primitives, strings  | `(field, id)` only                                                                       |
-| `std::optional<T>`   | `(field, id)` only                                                                       |
-| `std::shared_ptr<T>` | `(field, id)`, `(field, id, nullable)`, `(field, id, ref)`, `(field, id, nullable, ref)` |
-| `std::unique_ptr<T>` | `(field, id)`, `(field, id, nullable)`                                                   |
+Supported recursive composition methods are:
 
-## FORY_FIELD_CONFIG Macro
+| Method              | Applies to                            |
+| ------------------- | ------------------------------------- |
+| `list(elem)`        | `std::vector<T>` and list-like fields |
+| `set(elem)`         | `std::set<T>` and set-like fields     |
+| `map(key, value)`   | `std::map<K, V>` and map-like fields  |
+| `map().key(spec)`   | Override only the map key             |
+| `map().value(spec)` | Override only the map value           |
+| `inner(child)`      | Transparent single-child carriers     |
 
-The `FORY_FIELD_CONFIG` macro is the most powerful and flexible way to configure field-level serialization. It provides:
-
-- **Builder pattern API**: Fluent, chainable configuration with `F(id).option1().option2()`
-- **Encoding control**: Specify how unsigned integers are encoded (varint, fixed, tagged)
-- **Compile-time verification**: Field names are verified against member pointers
-- **Cross-language compatibility**: Configure encoding to match other languages (Java, Rust, etc.)
-
-### Basic Syntax
+Partial map overrides are useful when only one side needs a non-default
+encoding:
 
 ```cpp
-FORY_FIELD_CONFIG(StructType,
-    (field1, fory::F(0)),                           // Simple: just ID
-    (field2, fory::F(1).nullable()),                // With nullable
-    (field3, fory::F(2).varint()),                  // With encoding
-    (field4, fory::F(3).nullable().ref()),          // Multiple options
-    (field5, 4)                                     // Backward compatible: integer ID
-);
+FORY_STRUCT(Foo,
+            (nested, fory::F().map().key(T::varint())),
+            (other, fory::F().map().value(T::list(T::tagged()))));
 ```
 
-### The F() Builder
-
-The `fory::F(id)` factory creates a `FieldMeta` object that supports method chaining:
+## Carrier Inner Specs
 
-```cpp
-fory::F(0)                    // Create with field ID 0
-    .nullable()               // Mark as nullable
-    .ref()                    // Enable reference tracking
-    .varint()                 // Use variable-length encoding
-    .fixed()                  // Use fixed-size encoding
-    .tagged()                 // Use tagged encoding
-    .dynamic(false)           // skip type info (no dynamic dispatch)
-    .dynamic(true)            // Force type info (enable dynamic dispatch)
-    .compress(false)          // Disable compression
-```
-
-**Tip:** To use `F()` without the `fory::` prefix, add a using declaration:
+Use `.inner(...)` for wrapper-like carriers. The carrier kind still comes from
+the actual C++ type, and controls nullable/reference behavior:
 
 ```cpp
-using fory::F;
+struct WrapperFields {
+  std::optional<std::vector<uint32_t>> maybe_values;
+  std::shared_ptr<std::vector<int64_t>> shared_values;
+};
 
-FORY_FIELD_CONFIG(MyStruct,
-    (field1, F(0).varint()),      // No prefix needed
-    (field2, F(1).nullable())
-);
+FORY_STRUCT(WrapperFields,
+            (maybe_values, fory::F().inner(T::list(T::varint()))),
+            (shared_values,
+             fory::F().nullable().ref().inner(T::list(T::tagged()))));
 ```
 
-### Encoding Options for Unsigned Integers
+`.inner(...)` is the only public combinator for `std::optional<T>`,
+`std::shared_ptr<T>`, `std::unique_ptr<T>`, and
+`fory::serialization::SharedWeak<T>`.
 
-For `uint32_t` and `uint64_t` fields, you can specify the wire encoding:
+## Nullability, Reference Tracking, And Dynamic Fields
 
-| Method      | Type ID       | Description                                    | Use Case                              |
-| ----------- | ------------- | ---------------------------------------------- | ------------------------------------- |
-| `.varint()` | VAR_UINT32/64 | Variable-length encoding (1-5 or 1-10 bytes)   | Values typically small                |
-| `.fixed()`  | UINT32/64     | Fixed-size encoding (always 4 or 8 bytes)      | Values uniformly distributed          |
-| `.tagged()` | TAGGED_UINT64 | Tagged hybrid encoding with size hint (uint64) | Mixed small and large values (uint64) |
-
-**Note:** `uint8_t` and `uint16_t` always use fixed encoding (UINT8, UINT16).
-
-### Complete Example
+`std::optional<T>` is nullable by default. Smart pointers may be marked nullable
+or reference-tracked in the field spec:
 
 ```cpp
-#include "fory/serialization/fory.h"
-
-using namespace fory::serialization;
-
-// Define struct with unsigned integer fields
-struct MetricsData {
-  // Counters - often small values, use varint for space efficiency
-  uint32_t request_count;
-  uint64_t bytes_sent;
-
-  // IDs - uniformly distributed, use fixed for consistent performance
-  uint32_t user_id;
-  uint64_t session_id;
-
-  // Timestamps - use tagged encoding for mixed value ranges
-  uint64_t created_at;
-
-  // Nullable fields
-  std::optional<uint32_t> error_count;
-  std::optional<uint64_t> last_access_time;
+struct Node {
+  std::string name;
+  std::shared_ptr<Node> next;
 };
 
-FORY_STRUCT(MetricsData, request_count, bytes_sent, user_id, session_id,
-            created_at, error_count, last_access_time);
-
-// Configure field encoding
-FORY_FIELD_CONFIG(MetricsData,
-    // Small counters - varint saves space
-    (request_count, fory::F(0).varint()),
-    (bytes_sent, fory::F(1).varint()),
-
-    // IDs - fixed for consistent performance
-    (user_id, fory::F(2).fixed()),
-    (session_id, fory::F(3).fixed()),
-
-    // Timestamp - tagged encoding
-    (created_at, fory::F(4).tagged()),
-
-    // Nullable fields
-    (error_count, fory::F(5).nullable().varint()),
-    (last_access_time, fory::F(6).nullable().tagged())
-);
-
-int main() {
-  auto fory = Fory::builder().xlang(true).build();
-  fory.register_struct<MetricsData>(100);
-
-  MetricsData data;
-  data.request_count = 42;
-  data.bytes_sent = 1024;
-  data.user_id = 12345678;
-  data.session_id = 9876543210;
-  data.created_at = 1704067200000000000ULL; // 2024-01-01 in nanoseconds
-  data.error_count = 3;
-  data.last_access_time = std::nullopt;
-
-  auto bytes = fory.serialize(data).value();
-  auto decoded = fory.deserialize<MetricsData>(bytes).value();
-}
+FORY_STRUCT(Node, name, (next, fory::F().nullable().ref()));
 ```
 
-### Cross-Language Compatibility
-
-When serializing data to be read by other languages, use `FORY_FIELD_CONFIG` to match their encoding expectations:
-
-**Java Compatibility:**
+For polymorphic pointer fields, use `.dynamic(true)` to always write runtime
+type information, `.dynamic(false)` to use the declared type directly, or omit
+it to let Fory infer the behavior from the C++ type:
 
 ```cpp
-// Java uses these type IDs for unsigned integers:
-// - Byte (u8): UINT8 (fixed)
-// - Short (u16): UINT16 (fixed)
-// - Integer (u32): VAR_UINT32 (varint) or UINT32 (fixed)
-// - Long (u64): VAR_UINT64 (varint), UINT64 (fixed), or TAGGED_UINT64
-
-struct JavaCompatible {
-  uint8_t byte_field;      // Maps to Java Byte
-  uint16_t short_field;    // Maps to Java Short
-  uint32_t int_var_field;   // Maps to Java Integer with varint
-  uint32_t int_fixed_field; // Maps to Java Integer with fixed
-  uint64_t long_var_field;  // Maps to Java Long with varint
-  uint64_t long_tagged;    // Maps to Java Long with tagged
+struct Zoo {
+  std::shared_ptr<Animal> star;
+  std::shared_ptr<Animal> mascot;
 };
 
-FORY_STRUCT(JavaCompatible, byte_field, short_field, int_var_field,
-            int_fixed_field, long_var_field, long_tagged);
-
-FORY_FIELD_CONFIG(JavaCompatible,
-    (byte_field, fory::F(0)),                    // UINT8 (auto)
-    (short_field, fory::F(1)),                   // UINT16 (auto)
-    (int_var_field, fory::F(2).varint()),         // VAR_UINT32
-    (int_fixed_field, fory::F(3).fixed()),        // UINT32
-    (long_var_field, fory::F(4).varint()),        // VAR_UINT64
-    (long_tagged, fory::F(5).tagged())           // TAGGED_UINT64
-);
+FORY_STRUCT(Zoo, (star, fory::F().nullable().dynamic(true)),
+            (mascot, fory::F().nullable().dynamic(false)));
 ```
 
-### Schema Evolution with FORY_FIELD_CONFIG
+## Unions
 
-In compatible mode, fields can have different nullability between sender and receiver:
+`FORY_UNION` cases must use explicit ids. Name-mode `fory::F()` is invalid for
+union metadata:
 
 ```cpp
-// Version 1: All fields non-nullable
-struct DataV1 {
-  uint32_t id;
-  uint64_t timestamp;
-};
-FORY_STRUCT(DataV1, id, timestamp);
-FORY_FIELD_CONFIG(DataV1,
-    (id, fory::F(0).varint()),
-    (timestamp, fory::F(1).tagged())
-);
+struct Choice {
+  std::variant<std::string, uint32_t> value;
 
-// Version 2: Added nullable fields
-struct DataV2 {
-  uint32_t id;
-  uint64_t timestamp;
-  std::optional<uint32_t> version;  // New nullable field
+  static Choice text(std::string value);
+  static Choice code(uint32_t value);
 };
-FORY_STRUCT(DataV2, id, timestamp, version);
-FORY_FIELD_CONFIG(DataV2,
-    (id, fory::F(0).varint()),
-    (timestamp, fory::F(1).tagged()),
-    (version, fory::F(2).nullable().varint())  // New field with nullable
-);
-```
-
-### FORY_FIELD_CONFIG Options Reference
-
-| 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**: 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:
+FORY_UNION(Choice, (text, std::string, fory::F(1)),
+           (code, uint32_t, fory::F(2).fixed()));
+```
 
-- A field can be null (use `std::optional<T>` or mark with `nullable()`)
-- A field needs reference tracking for shared/circular objects (use `ref()`)
-- Integer types need specific encoding for cross-language compatibility
-- You want to reduce metadata size (use field IDs)
+Generated C++ may omit the explicit case type when it can infer the payload type
+from a non-overloaded one-argument factory:
 
 ```cpp
-// Xlang mode: explicit configuration required
-struct User {
-    std::string name;                              // Non-nullable by default
-    std::optional<std::string> email;              // Nullable (std::optional)
-    std::shared_ptr<User> friend_ptr;              // Ref tracking by default
-};
-
-FORY_STRUCT(User, name, email, friend_ptr);
-
-FORY_FIELD_CONFIG(User,
-    (name, fory::F(0)),
-    (email, fory::F(1)),                           // nullable implicit for optional
-    (friend_ptr, fory::F(2).nullable().ref())      // explicit nullable + ref
-);
+FORY_UNION(GeneratedChoice, (text, fory::F(1)),
+           (code, fory::F(2).fixed()));
 ```
 
-### Default Values Summary
-
-| 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
-
-- [Type Registration](type-registration.md) - Registering types with FORY_STRUCT
-- [Schema Evolution](schema-evolution.md) - Using tag IDs for schema evolution
-- [Configuration](configuration.md) - Enabling reference tracking globally
-- [Cross-Language](cross-language.md) - Interoperability with Java, Rust, Python
+The three-element form is the stable public form for handwritten code.
diff --git a/docs/guide/cpp/polymorphism.md b/docs/guide/cpp/polymorphism.md
index 97f5bad11..4465553ef 100644
--- a/docs/guide/cpp/polymorphism.md
+++ b/docs/guide/cpp/polymorphism.md
@@ -137,7 +137,8 @@ struct Container2 {
 
 ## Controlling Dynamic Dispatch
 
-Use `fory::dynamic<V>` to override automatic polymorphism detection:
+Use `fory::F().dynamic(V)` in `FORY_STRUCT` to override automatic
+polymorphism detection:
 
 ```cpp
 struct Animal {
@@ -150,23 +151,24 @@ struct Pet {
   std::shared_ptr<Animal> animal1;
 
   // Force dynamic: type info written explicitly
-  fory::field<std::shared_ptr<Animal>, 0, fory::dynamic<true>> animal2;
+  std::shared_ptr<Animal> animal2;
 
   // Force non-dynamic: skip type info (faster but no runtime subtyping)
-  fory::field<std::shared_ptr<Animal>, 1, fory::dynamic<false>> animal3;
+  std::shared_ptr<Animal> animal3;
 };
-FORY_STRUCT(Pet, animal1, animal2, animal3);
+FORY_STRUCT(Pet, animal1, (animal2, fory::F().dynamic(true)),
+            (animal3, fory::F().dynamic(false)));
 ```
 
-**When to use `fory::dynamic<false>`:**
+**When to use `dynamic(false)`:**
 
 - You know the runtime type will always match the declared type
 - Performance is critical and you don't need subtype support
 - Working with monomorphic data despite having a polymorphic base class
 
-### Field Configuration Without Wrapper Types
+### Field Configuration
 
-Use `FORY_FIELD_CONFIG` to configure fields without `fory::field<>` wrapper:
+Configure field metadata directly in `FORY_STRUCT`:
 
 ```cpp
 struct Zoo {
@@ -174,17 +176,13 @@ struct Zoo {
   std::shared_ptr<Animal> backup;    // Nullable polymorphic field
   std::shared_ptr<Animal> mascot;    // Non-dynamic (no subtype dispatch)
 };
-FORY_STRUCT(Zoo, star, backup, mascot);
-
-// Configure fields with tag IDs and options
-FORY_FIELD_CONFIG(Zoo,
-    (star, fory::F(0)),                    // Tag ID 0, default options
-    (backup, fory::F(1).nullable()),       // Tag ID 1, allow nullptr
-    (mascot, fory::F(2).dynamic(false))    // Tag ID 2, disable polymorphism
-);
+FORY_STRUCT(Zoo, (star, fory::F(0)),
+            (backup, fory::F(1).nullable()),
+            (mascot, fory::F(2).dynamic(false)));
 ```
 
-See [Field Configuration](field-configuration.md) for complete details on `fory::nullable`, `fory::ref`, and other field-level options
+See [Field Configuration](field-configuration.md) for complete details on
+`nullable()`, `ref()`, and other field-level options.
 
 ## std::unique_ptr Polymorphism
 
@@ -307,8 +305,8 @@ assert(!result.ok());  // Fails with depth exceeded error
 ## Nullability for Polymorphic Fields
 
 By default, `std::shared_ptr<T>` and `std::unique_ptr<T>` fields are treated as
-non-nullable in the schema. To allow `nullptr`, wrap the field with
-`fory::field<>` (or `FORY_FIELD_TAGS`) and opt in with `fory::nullable`.
+non-nullable in the schema. To allow `nullptr`, mark the field nullable in
+`FORY_STRUCT`.
 
 ```cpp
 struct Pet {
@@ -316,9 +314,9 @@ struct Pet {
   std::shared_ptr<Animal> primary;
 
   // Nullable via explicit field metadata
-  fory::field<std::shared_ptr<Animal>, 0, fory::nullable> optional;
+  std::shared_ptr<Animal> optional;
 };
-FORY_STRUCT(Pet, primary, optional);
+FORY_STRUCT(Pet, primary, (optional, fory::F().nullable()));
 ```
 
 See [Field Configuration](field-configuration.md) for more details.
@@ -415,10 +413,10 @@ auto fory = Fory::builder()
    auto fory = Fory::builder().max_dyn_depth(10).build();
    ```
 
-7. **Use `fory::nullable`** for optional polymorphic fields:
+7. **Use `nullable()`** for optional polymorphic fields:
 
    ```cpp
-   fory::field<std::shared_ptr<Base>, 0, fory::nullable> optional_ptr;
+   FORY_STRUCT(Holder, (optional_ptr, fory::F().nullable()));
    ```
 
 ## Error Handling
@@ -455,10 +453,10 @@ if (!decoded_result.ok()) {
 
 **Optimization tips:**
 
-1. **Use `fory::dynamic<false>`** when runtime type matches declared type:
+1. **Use `dynamic(false)`** when runtime type matches declared type:
 
    ```cpp
-   fory::field<std::shared_ptr<Base>, 0, fory::dynamic<false>> fixed_type;
+   FORY_STRUCT(Holder, (fixed_type, fory::F().dynamic(false)));
    ```
 
 2. **Minimize nesting depth** to reduce metadata overhead
diff --git a/docs/guide/xlang/field-nullability.md b/docs/guide/xlang/field-nullability.md
index ab888a563..de7f9dc2b 100644
--- a/docs/guide/xlang/field-nullability.md
+++ b/docs/guide/xlang/field-nullability.md
@@ -200,17 +200,32 @@ public class Config {
 }
 ```
 
-### C++: fory::field Wrapper
+### C++: FORY_STRUCT Field Config
 
 ```cpp
 struct Config {
-    // Explicitly mark as nullable
-    fory::field<std::string, 1, fory::nullable<true>> optional_setting;
+    std::optional<std::string> optional_setting;
+    std::string required_setting;
+};
+
+FORY_STRUCT(Config,
+    (optional_setting, fory::F(1)),
+    (required_setting, fory::F(2))
+);
+```
 
-    // Explicitly mark as non-nullable (default)
-    fory::field<std::string, 2, fory::nullable<false>> required_setting;
+For nullable pointer carriers, opt in with `.nullable()`:
+
+```cpp
+struct ConfigRef {
+    std::shared_ptr<std::string> optional_setting;
+    std::shared_ptr<std::string> required_setting;
 };
-FORY_STRUCT(Config, optional_setting, required_setting);
+
+FORY_STRUCT(ConfigRef,
+    (optional_setting, fory::F(1).nullable()),
+    (required_setting, fory::F(2))
+);
 ```
 
 ## Null Value Handling
diff --git a/docs/guide/xlang/field-reference-tracking.md b/docs/guide/xlang/field-reference-tracking.md
index 1ed94bd3d..2aafbf2ee 100644
--- a/docs/guide/xlang/field-reference-tracking.md
+++ b/docs/guide/xlang/field-reference-tracking.md
@@ -141,7 +141,7 @@ public class Document {
 }
 ```
 
-#### C++: fory::field Wrapper
+#### C++: FORY_STRUCT Field Config
 
 ```cpp
 struct Document {
@@ -151,10 +151,14 @@ struct Document {
     std::shared_ptr<Author> author;
     fory::serialization::SharedWeak<Data> data;
 
-    // Explicitly mark ref tracking when using field wrappers (optional)
-    fory::field<std::shared_ptr<Tag>, 1, fory::ref> tag_owner;
+    std::shared_ptr<Tag> tag_owner;
 };
-FORY_STRUCT(Document, title, author, data, tag_owner);
+FORY_STRUCT(Document,
+    title,
+    author,
+    data,
+    (tag_owner, fory::F().ref())
+);
 ```
 
 To disable reference tracking for C++ entirely, set
diff --git a/docs/guide/xlang/field-type-meta.md b/docs/guide/xlang/field-type-meta.md
index 1c6880c85..58870fd36 100644
--- a/docs/guide/xlang/field-type-meta.md
+++ b/docs/guide/xlang/field-type-meta.md
@@ -85,9 +85,7 @@ public class Container {
 
 ### C++
 
-C++ uses the `fory::dynamic<V>` template tag or `.dynamic(bool)` builder method:
-
-**Using `fory::field<>` template**:
+C++ uses the `.dynamic(bool)` builder method inside `FORY_STRUCT`:
 
 ```cpp
 #include "fory/serialization/fory.h"
@@ -100,29 +98,15 @@ struct Animal {
 
 struct Zoo {
     // Auto: type info written because Animal is polymorphic (std::is_polymorphic)
-    fory::field<std::shared_ptr<Animal>, 0, fory::nullable> animal;
+    std::shared_ptr<Animal> animal;
 
     // Force non-dynamic: skip type info even though Animal is polymorphic
-    fory::field<std::shared_ptr<Animal>, 1, fory::nullable, fory::dynamic<false>> fixed_animal;
+    std::shared_ptr<Animal> fixed_animal;
 
     // Force dynamic: write type info even for non-polymorphic types
-    fory::field<std::shared_ptr<Data>, 2, fory::dynamic<true>> polymorphic_data;
-};
-FORY_STRUCT(Zoo, animal, fixed_animal, polymorphic_data);
-```
-
-**Using `FORY_FIELD_CONFIG` macro**:
-
-```cpp
-struct Zoo {
-    std::shared_ptr<Animal> animal;
-    std::shared_ptr<Animal> fixed_animal;
     std::shared_ptr<Data> polymorphic_data;
 };
-
-FORY_STRUCT(Zoo, animal, fixed_animal, polymorphic_data);
-
-FORY_FIELD_CONFIG(Zoo,
+FORY_STRUCT(Zoo,
     (animal, fory::F(0).nullable()),                    // Auto-detect polymorphism
     (fixed_animal, fory::F(1).nullable().dynamic(false)), // Skip type info
     (polymorphic_data, fory::F(2).dynamic(true))        // Force type info
@@ -286,9 +270,7 @@ struct Zoo {
     std::shared_ptr<Dog> maybe_mixed_breed;
 };
 
-FORY_STRUCT(Zoo, animal, maybe_mixed_breed);
-
-FORY_FIELD_CONFIG(Zoo,
+FORY_STRUCT(Zoo,
     (animal, fory::F(0).nullable()),              // Auto-detect (Animal is polymorphic)
     (maybe_mixed_breed, fory::F(1).dynamic(true)) // Force dynamic for concrete type
 );
diff --git a/java/fory-core/src/test/java/org/apache/fory/xlang/CPPXlangTest.java b/java/fory-core/src/test/java/org/apache/fory/xlang/CPPXlangTest.java
index 8a02157ae..c1f320d8a 100644
--- a/java/fory-core/src/test/java/org/apache/fory/xlang/CPPXlangTest.java
+++ b/java/fory-core/src/test/java/org/apache/fory/xlang/CPPXlangTest.java
@@ -210,6 +210,18 @@ public class CPPXlangTest extends XlangTestBase {
     super.testStructWithMap(enableCodegen);
   }
 
+  @Test(groups = "xlang", dataProvider = "enableCodegenParallel")
+  public void testNestedAnnotatedContainerSchemaConsistent(boolean enableCodegen)
+      throws java.io.IOException {
+    super.testNestedAnnotatedContainerSchemaConsistent(enableCodegen);
+  }
+
+  @Test(groups = "xlang", dataProvider = "enableCodegenParallel")
+  public void testNestedAnnotatedContainerCompatible(boolean enableCodegen)
+      throws java.io.IOException {
+    super.testNestedAnnotatedContainerCompatible(enableCodegen);
+  }
+
   @Test(groups = "xlang", dataProvider = "enableCodegenParallel")
   public void testCollectionElementRefOverride(boolean enableCodegen) throws java.io.IOException {
     super.testCollectionElementRefOverride(enableCodegen);


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@fory.apache.org
For additional commands, e-mail: commits-help@fory.apache.org