Today, every Symbol-to-DOM mapping goes through: dom::ValueFrom(I, this) -> tag_invoke(LazyObjectMapTag, io, I, domCorpus) -> mapReflectedType<true>(io, I, domCorpus) -> describe::for_each over the struct's MRDOCS_DESCRIBE_STRUCT members -> io.map(name, value) for each one.
Every property is materialized into the lazy object's pending map up-front, even when the template touches only a few fields. The idea is to skip the eager mapping step and synthesize a dom::LazyObject whose property lookups dispatch directly into the reflection descriptors at access time. Most templates touch a small fraction of the fields per symbol, so the eager mapping is a meaningful cost.
Rough sketch:
A dom::LazyObject backed by a T const& + the type's describe_members descriptor list.
Property lookup: find the descriptor matching the requested name (linear scan over a small list, or a constexpr perfect hash on field names if hot), then obj.*descriptor.pointer -> DOM value via the existing per-type mapping rules.
The generic tag_invoke(LazyObjectMapTag, IO&, T const&, DomCorpus const*) in MapReflectedType.hpp becomes the obvious customization point: instead of io.map(...) for each field up front, it can install the reflection-backed lazy lookup.
Symbol types with custom tag_invoke overloads (currently only RecordSymbol's defaultAccess) keep their custom mapping; the change is purely in the generic path.
Today, every Symbol-to-DOM mapping goes through:
dom::ValueFrom(I, this)->tag_invoke(LazyObjectMapTag, io, I, domCorpus)->mapReflectedType<true>(io, I, domCorpus)->describe::for_eachover the struct'sMRDOCS_DESCRIBE_STRUCT members->io.map(name, value)for each one.Every property is materialized into the lazy object's pending map up-front, even when the template touches only a few fields. The idea is to skip the eager mapping step and synthesize a
dom::LazyObjectwhose property lookups dispatch directly into the reflection descriptors at access time. Most templates touch a small fraction of the fields per symbol, so the eager mapping is a meaningful cost.Rough sketch:
A
dom::LazyObjectbacked by aT const&+ the type'sdescribe_membersdescriptor list.Property lookup: find the descriptor matching the requested name (linear scan over a small list, or a constexpr perfect hash on field names if hot), then
obj.*descriptor.pointer-> DOM value via the existing per-type mapping rules.The generic
tag_invoke(LazyObjectMapTag, IO&, T const&, DomCorpus const*)in MapReflectedType.hpp becomes the obvious customization point: instead ofio.map(...)for each field up front, it can install the reflection-backed lazy lookup.Symbol types with custom
tag_invokeoverloads (currently onlyRecordSymbol'sdefaultAccess) keep their custom mapping; the change is purely in the generic path.