Field Configuration
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:
#include "fory/serialization/fory.h"
struct DataV2 {
uint32_t id;
uint64_t timestamp;
std::optional<uint32_t> version;
};
FORY_STRUCT(DataV2, id, (timestamp, fory::F().tagged()), version);
The configuration is compile-time metadata. It does not allocate codec objects or add virtual dispatch on the serialization path.
Field Identity
fory::F() uses name-mode field identity. Bare fields are also name-mode:
FORY_STRUCT(DataV2, id, (timestamp, fory::F().tagged()), version);
fory::F(id) uses explicit id-based field identity. IDs must be
non-negative:
FORY_STRUCT(DataV2, (id, fory::F(0)), (timestamp, fory::F(1).tagged()),
(version, fory::F(2)));
Fields without explicit IDs still use their snake_case field names. Explicit
IDs sort before name-based fields within the same protocol field group, so a
single FORY_STRUCT may mix fory::F(id), fory::F(), and bare fields.
Scalar Encoding
Integer encoding is configured on the field or on a nested value-node spec:
struct Counters {
uint32_t fixed_id;
uint64_t tagged_time;
int64_t signed_score;
};
FORY_STRUCT(Counters, (fixed_id, fory::F().fixed()),
(tagged_time, fory::F().tagged()),
(signed_score, fory::F().varint()));
Supported scalar encoding methods are:
| Method | Meaning |
|---|---|
fixed() | Fixed-width integer encoding where valid |
varint() | Variable-length integer encoding where valid |
tagged() | Tagged integer encoding where valid |
Invalid scalar/type combinations fail at compile time.
Nested Specs
Use the fory::T namespace for value-node specs inside containers and wrapper
carriers. Untyped specs infer the actual C++ type at that node:
namespace T = fory::T;
struct Foo {
std::vector<uint32_t> values;
std::map<uint32_t, std::vector<int64_t>> nested;
};
FORY_STRUCT(Foo,
(values, fory::F().list(T::fixed())),
(nested, fory::F().map(T::varint(),
T::list(T::tagged()))));
Typed specs are optional validators and make the intended node type explicit:
FORY_STRUCT(Foo, (nested, fory::F().map(T::uint32().varint(),
T::list(T::int64().tagged()))));
Supported recursive composition methods are:
| 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 |
Partial map overrides are useful when only one side needs a non-default encoding:
FORY_STRUCT(Foo,
(nested, fory::F().map().key(T::varint())),
(other, fory::F().map().value(T::list(T::tagged()))));
Carrier Inner Specs
Use .inner(...) for wrapper-like carriers. The carrier kind still comes from
the actual C++ type, and controls nullable/reference behavior:
struct WrapperFields {
std::optional<std::vector<uint32_t>> maybe_values;
std::shared_ptr<std::vector<int64_t>> shared_values;
};
FORY_STRUCT(WrapperFields,
(maybe_values, fory::F().inner(T::list(T::varint()))),
(shared_values,
fory::F().nullable().ref().inner(T::list(T::tagged()))));
.inner(...) is the only public combinator for std::optional<T>,
std::shared_ptr<T>, std::unique_ptr<T>, and
fory::serialization::SharedWeak<T>.
Nullability, Reference Tracking, And Dynamic Fields
std::optional<T> is nullable by default. Smart pointers may be marked nullable
or reference-tracked in the field spec:
struct Node {
std::string name;
std::shared_ptr<Node> next;
};
FORY_STRUCT(Node, name, (next, fory::F().nullable().ref()));
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:
struct Zoo {
std::shared_ptr<Animal> star;
std::shared_ptr<Animal> mascot;
};
FORY_STRUCT(Zoo, (star, fory::F().nullable().dynamic(true)),
(mascot, fory::F().nullable().dynamic(false)));
Unions
FORY_UNION cases must use explicit ids. Name-mode fory::F() is invalid for
union metadata:
struct Choice {
std::variant<std::string, uint32_t> value;
static Choice text(std::string value);
static Choice code(uint32_t value);
};
FORY_UNION(Choice, (text, std::string, fory::F(1)),
(code, uint32_t, fory::F(2).fixed()));
Generated C++ may omit the explicit case type when it can infer the payload type from a non-overloaded one-argument factory:
FORY_UNION(GeneratedChoice, (text, fory::F(1)),
(code, fory::F(2).fixed()));
The three-element form is the stable public form for handwritten code.