| 2493 | /// Helper to write a single field |
| 2494 | template <typename T, size_t Index, typename FieldPtrs> |
| 2495 | void write_single_field(const T &obj, WriteContext &ctx, |
| 2496 | const FieldPtrs &field_ptrs, bool has_generics) { |
| 2497 | using Helpers = CompileTimeFieldHelpers<T>; |
| 2498 | const auto field_entry = std::get<Index>(field_ptrs); |
| 2499 | using RawFieldType = meta::FieldRawTypeT<T, decltype(field_entry)>; |
| 2500 | using FieldType = unwrap_field_t<RawFieldType>; |
| 2501 | |
| 2502 | auto &&field_value = [&]() -> decltype(auto) { |
| 2503 | if constexpr (Helpers::template is_direct_field<Index>()) { |
| 2504 | if constexpr (is_fory_field_v<RawFieldType>) { |
| 2505 | return (obj.*field_entry).value; |
| 2506 | } else { |
| 2507 | return (obj.*field_entry); |
| 2508 | } |
| 2509 | } else { |
| 2510 | return field_value_get(obj, field_entry); |
| 2511 | } |
| 2512 | }(); |
| 2513 | |
| 2514 | constexpr TypeId field_type_id = Serializer<FieldType>::type_id; |
| 2515 | constexpr bool is_primitive_field = is_primitive_type_id(field_type_id); |
| 2516 | |
| 2517 | // get field metadata from the effective field spec or defaults. |
| 2518 | constexpr bool is_nullable = Helpers::template field_nullable<Index>(); |
| 2519 | constexpr bool track_ref = Helpers::template field_track_ref<Index>(); |
| 2520 | // Some wrapper types always require ref/null flags in the wire format. |
| 2521 | constexpr bool field_type_is_nullable = is_nullable_v<FieldType>; |
| 2522 | |
| 2523 | if constexpr (configured_node_has_override<T, Index>()) { |
| 2524 | constexpr RefMode field_ref_mode = |
| 2525 | make_ref_mode(is_nullable || field_type_is_nullable, track_ref); |
| 2526 | constexpr bool is_struct = is_struct_type(field_type_id); |
| 2527 | constexpr bool is_ext = is_ext_type(field_type_id); |
| 2528 | constexpr bool is_polymorphic = field_type_id == TypeId::UNKNOWN; |
| 2529 | constexpr int dynamic_val = Helpers::template field_dynamic_value<Index>(); |
| 2530 | constexpr bool polymorphic_write_type = |
| 2531 | (dynamic_val == 1) || (dynamic_val == -1 && is_polymorphic); |
| 2532 | bool write_type = polymorphic_write_type || |
| 2533 | ((is_struct || is_ext) && ctx.is_compatible()); |
| 2534 | write_configured_value<FieldType, T, Index, 0>( |
| 2535 | field_value, ctx, field_ref_mode, write_type, has_generics); |
| 2536 | return; |
| 2537 | } |
| 2538 | |
| 2539 | // Special handling for std::optional<uint32_t/uint64_t> with encoding config |
| 2540 | // This must come BEFORE the general primitive check because optional requires |
| 2541 | // ref metadata but we want to use encoding-specific serialization. |
| 2542 | constexpr bool is_encoded_optional_uint = |
| 2543 | ::fory::detail::has_field_config_v<T> && |
| 2544 | (std::is_same_v<FieldType, std::optional<uint32_t>> || |
| 2545 | std::is_same_v<FieldType, std::optional<uint64_t>>); |
| 2546 | constexpr bool is_encoded_optional_int = |
| 2547 | ::fory::detail::has_field_config_v<T> && |
| 2548 | (std::is_same_v<FieldType, std::optional<int32_t>> || |
| 2549 | std::is_same_v<FieldType, std::optional<int64_t>> || |
| 2550 | std::is_same_v<FieldType, std::optional<int>> || |
| 2551 | std::is_same_v<FieldType, std::optional<long long>>); |
| 2552 |
nothing calls this directly
no test coverage detected