Skip to content

[super! 3/6] Invoke through an erased context instead of the proxy type - #68

Open
mingxwa wants to merge 1 commit into
ngcpp:feature/v5from
mingxwa:user/mingxwa/super-stage3
Open

[super! 3/6] Invoke through an erased context instead of the proxy type#68
mingxwa wants to merge 1 commit into
ngcpp:feature/v5from
mingxwa:user/mingxwa/super-stage3

Conversation

@mingxwa

@mingxwa mingxwa commented Aug 15, 2026

Copy link
Copy Markdown
Member

An invoker's erased function pointer was typed R (*)(proxy<F>&, D, Args...), so its type named the facade it was built for. That is fine while metadata is only ever read by a proxy of exactly that facade, but it prevents one proxy from reusing another's invoker: two proxy types have different layouts, so passing one where the other is expected is not valid. Reusing a base facade's invokers is what super (more PRs coming ahead) needs.

  • Adds erased_context, which carries only a pointer to the storage of the contained value. Invokers become invoker<Ctx, O>.
  • The lifetime dispatches take a void* rather than a target proxy, so their invokers no longer mention F either.
  • Resetting the source metadata after an rvalue-qualified call moves to the call site, where the facade is known.
  • relocate_dispatch becomes a tag with its own erased_context specialization, which absorbs the bitwise-relocation path that used to be selected through internal_dispatch. internal_dispatch is removed.
  • ptr_ is declared ahead of meta_ so that the storage of the contained value lies at offset 0. Every invocation now builds a context from it, and at offset 0 that context is the address of the proxy itself.

Declaring ptr_ first is what keeps the new indirection free at the call site. Where a proxy is invoked more than once, the caller keeps a single value live instead of a separate context pointer. On aarch64 that removes a spill and reload of the context across the first call and shrinks the frame by 16 bytes:

 stp  x29, x30, [sp, #-48]!        stp  x29, x30, [sp, #-32]!
 ...                               ...
 ldr  x1, [x0], #8                 ldr  x1, [x0, #16]
 str  x0, [sp, #40]   <- spill     (none)
 blr  x1                           blr  x1
 ldr  x0, [sp, #40]   <- reload    mov  x0, x19

substitution_dispatch needs the same relocation path, because it relocates a bitwise-relocatable value without requiring it to be move-constructible, so it gets a specialization too. That specialization will be removed together with substitution_dispatch in the last PR of this stack.

Breaking change. Removes the public reinterpret_invoke.

An invoker's erased function pointer was typed R (*)(proxy<F>&, D,
Args...), so its type named the facade it was built for. That is fine
while metadata is only ever read by a proxy of exactly that facade, but
it prevents one proxy from reusing another's invoker: two proxy types
have different layouts, so passing one where the other is expected is
not valid.

Introduce erased_context, which carries only a pointer to the storage of
the contained value, and type invokers as invoker<Ctx, O>. Invocation
now resolves the contained type inside the context rather than through
reinterpret_invoke, and the lifetime dispatches take a void* rather than
a target proxy, so their invokers no longer mention F either. Resetting
the source metadata after an rvalue-qualified call moves to the call
site, where the facade is known.

Declare ptr_ ahead of meta_ so that the storage of the contained value
lies at offset 0. Every invocation now builds a context from it, and at
offset 0 that context is the address of the proxy itself. Where a proxy
is invoked more than once, the caller then keeps a single value live
instead of a separate context pointer: on aarch64 that removes a spill
and reload of the context across the first call, and shrinks the frame
by 16 bytes. Neither sizeof(proxy) nor its alignment changes.

relocate_dispatch becomes a tag with its own erased_context
specialization, which absorbs the bitwise-relocation path that used to
be selected via internal_dispatch, and internal_dispatch is removed.
substitution_dispatch needs the same path, because it relocates a
bitwise-relocatable value without requiring it to be move-constructible,
so it gets a specialization too; both it and the specialization drop out
once `super` replaces substitution.

Removes the public reinterpret_invoke, which had no remaining use: the
invoker macro was its only caller inside the library.
@mingxwa
mingxwa force-pushed the user/mingxwa/super-stage3 branch from 685a990 to 1335a47 比较 August 15, 2026 10:27
注册 for free to join this conversation on GitHub. Already have an account? 登录 to comment

项目

None yet

Development

Successfully merging this pull request may close these issues.

1 participant