Skip to content

[feat] Add a metadata policy to proxy - #90

Open
mingxwa wants to merge 3 commits into
ngcpp:feature/v5from
mingxwa:user/mingxwa/metadata-policy
Open

mingxwa wants to merge 3 commits into
ngcpp:feature/v5from
mingxwa:user/mingxwa/metadata-policy

Conversation

@mingxwa

@mingxwa mingxwa commented Sep 22, 2026

Copy link
Copy Markdown
Member

proxy takes a second template parameter that decides how an invocation is erased and where the metadata lives. compact_metadata is the default and keeps the current layout. inline_metadata keeps all metadata inside the proxy, trading size for one fewer indirection per invocation. This helps latency-sensitive code that invokes a long-lived proxy.

Changes

  • Added compact_metadata, inline_metadata and the ProMetadataPolicy named requirement. A policy is checked by static_assert where the proxy naming it is instantiated.
  • Threaded the policy through proxy, proxy_indirect_accessor, proxiable, inplace_proxiable_target, proxiable_target, proxy_view, weak_proxy and the creation functions. Overloads that deduce the target type keep their signatures and use the default policy, except make_proxy_view, which takes the policy right after the facade.
  • Replaced facade_aware_overload_t with proxy_dependent_signature, which instantiates its template with the facade and the policy. This lets skills::as_view, skills::as_weak and weak_proxy::lock keep the policy of the source proxy.
  • Renamed facade_meta_traits.h to metadata_policy.h, facade_traits to proxy_traits, inplace_meta_storage to inline_meta_storage, and ProBasicMeta and ProMeta to ProBasicMetadata and ProMetadata to fit the new vocabulary.
  • Required ProBasicMetadata types, including reflector types, to be class types, so an invoker that cannot be inherited is rejected by the policy check.
  • Simplified nullable to basic metadata that converts to bool, and removed the reset() and has_value() members that restated existing operations.
  • Fixed copying a null invoker or metadata pointer under pointer authentication. This also fixes swapping an empty proxy with a non-empty one under trivial relocation on arm64e.
  • Added unit tests for both policies, their invoker and their storages.

Pure moves with no change in content, so that history follows each
file across the rename. The next commit updates every reference to
them, so this commit neither builds nor passes `mkdocs build --strict`
on its own.

- `facade_meta_traits.h` becomes `metadata_policy.h`.
- `ProBasicMeta.md` becomes `ProBasicMetadata.md`.
- `ProMeta.md` becomes `ProMetadata.md`.
- `facade_aware_overload_t.md` becomes `proxy_dependent_signature.md`.
Give `proxy` a second template parameter `MetadataPolicy` that decides
how an invocation is erased and how the metadata deduced from the
contained type is kept. `compact_metadata`, the default, keeps small
metadata in the proxy and otherwise points at a static metadata object,
while `inline_metadata` always keeps the metadata in the proxy, trading
size for one fewer indirection per invocation. The latter is aimed at
latency-sensitive code, where the static metadata object of a long-lived
proxy costs a cache line the caller would not otherwise touch.

The metadata type of a `proxy<F, MP>` depends on both `F` and `MP`, so
a proxy converts only to a proxy with the same metadata policy. The
policy is chosen where the proxy type is named, and every conversion to
a super preserves it.

- `proxy_dependent_signature` instantiates its template argument with a
  facade and a metadata policy, so a convention can return a proxy that
  keeps the metadata policy of the proxy it was obtained from.
  `skills::as_view`, `skills::as_weak` and `weak_proxy::lock` do exactly
  that.
- The policy is threaded through `proxy`, `proxy_indirect_accessor`,
  `proxiable`, `inplace_proxiable_target`, `proxiable_target`,
  `proxy_view`, `weak_proxy` and every function template that creates a
  proxy. The overloads that deduce the target type keep their existing
  signatures and always use the default policy, so naming a target type
  can never be mistaken for naming a policy.
- Add the *ProMetadataPolicy* named requirement. A policy is not
  constrained by a concept, and one whose invoker or storage is not
  well-formed is diagnosed by `static_assert` where the proxy naming it
  is instantiated.

Renames that prepare the vocabulary:
- `compact_facade_meta_traits`/`flat_facade_meta_traits` become
  `compact_metadata`/`inline_metadata`.
- `inplace_meta_storage` becomes `inline_meta_storage`, matching the
  policy that selects it.
- `facade_aware_overload_t` becomes `proxy_dependent_signature`, and the
  conventions and signature templates that go with it drop the `faw`
  and `overload` spellings.
- `facade_traits` becomes `proxy_traits`, because it now depends on the
  metadata policy as well as the facade.
- *ProBasicMeta* and *ProMeta* become *ProBasicMetadata* and
  *ProMetadata*.

nullable required a meta to name reset() and has_value(), and both
restate an operation the meta already has. has_value() is the contextual
conversion to bool, and reset() is assignment from a value-initialized
meta, which basic_meta already requires to be well formed and nothrow.
The concept becomes basic_meta plus bool-testability, and the named
members are gone from sentinel_meta, invoker_base, static_meta_storage
and proxy_meta_base_impl. code_ptr and meta_ptr follow, trading
operator==(std::nullptr_t) and operator=(std::nullptr_t) for an explicit
operator bool, so the signed wrapper and the plain pointer alias are
spelled the same way.

Stating the null state as a value-initialized meta moves it to the type
that knows it. composite_meta holds the metas as bases, so it owns which
one carries the null state: it value-initializes that first base and
leaves the rest uninitialized, tests it, and copies the rest only when
it holds a value. Its copy constructor delegates to the default
constructor, because GCC's -Wextra flags a copy constructor that does
not explicitly initialize a base with a user-provided constructor.
proxy_meta_base_impl keeps only the conversion to a contained meta, and
no longer distinguishes the first meta from the rest.

Under pointer authentication a null value is stored unsigned, so copying
one must not authenticate it. code_ptr and meta_ptr now skip the resign
for a null value, in an initialize member that writes the destination
from the source as proxy::initialize does. This also repairs swapping an
empty proxy with a non-empty one, which exchanges the metadata words
directly under trivial relocation and trapped on the null one.
SmallMeta and LargeMeta defaulted their default constructor, which left
them trivially default constructible. MSVC then reports C4268 for the
const static object of static_meta_storage, claiming the compiler
generated default constructor fills it with zeros, and /WX turns that
into an error. The claim is false: the in_place_type constructor runs
and the object holds the expected values.

Every meta the library defines already states its null state in a
user-provided constructor, as composite_meta and proxy_meta_base_impl
do, and the metadata a proxy keeps is always proxy_meta, a member-less
wrapper that MSVC never diagnoses. Spell the test metas the same way
rather than suppress the warning where the storage is declared.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant