Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ngcpp/proxy
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: ngcpp/proxy
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: feature/v5
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 17 commits
  • 62 files changed
  • 1 contributor

Commits on Jul 22, 2026

  1. Configuration menu
    Copy the full SHA
    7f847ed View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    d63b436 View commit details
    Browse the repository at this point in the history

Commits on Jul 24, 2026

  1. Code cleanup (#63)

    mingxwa authored Jul 24, 2026
    Configuration menu
    Copy the full SHA
    4b49a1a View commit details
    Browse the repository at this point in the history

Commits on Aug 28, 2026

  1. Refactor lifetime model selection into type traits (#69)

    Replace the `*_impl` proxy creation functions with type traits that name the
    selected lifetime model, so that the models can be reused by types other than
    `proxy` (e.g. an upcoming `box`). No observable behavior change.
    
    - Rename the internal `allocated_ptr` class template to `wide_ptr`, and add
      `allocated_ptr`, `owned_ptr` and `shared_ptr` type traits that select a
      lifetime model instead of constructing a `proxy` directly. The creation
      functions now construct `proxy<F>` from the selected pointer type.
    - Let `inplace_ptr` ignore its first constructor argument so that all owning
      lifetime models share a uniform `(alloc, args...)` construction signature.
    - Order the `T&& value` overload first in each creation function family, and
      renumber the corresponding specs to match.
    mingxwa authored Aug 28, 2026
    Configuration menu
    Copy the full SHA
    b76fd39 View commit details
    Browse the repository at this point in the history

Commits on Aug 30, 2026

  1. Introduce the ProBasicMeta and ProMeta named requirements (#66)

    A "meta" is an object holding metadata deduced from a type at compile
    time, stored in or referenced by a proxy. proxy creates, copies,
    assigns, and destroys metadata in contexts specified not to throw, but
    nothing required a reflector to actually support those operations
    without throwing, or to be default-constructible at all.
    
    Define the ProBasicMeta and ProMeta named requirements, and enforce them
    via the new basic_meta, meta, and basic_reflection concepts.
    is_reflector_well_formed() now checks meta<R, T> rather than plain
    constructibility, so a reflector that can throw, or that cannot be
    default-constructed, copied, or assigned, is no longer proxiable and is
    diagnosed as a reflection that is not implemented.
    
    This tightens proxiable: a reflector whose constructor may throw was
    previously accepted.
    mingxwa authored Aug 30, 2026
    Configuration menu
    Copy the full SHA
    7f5cb7b View commit details
    Browse the repository at this point in the history
  2. Give each convention a single overload type (#67)

    A convention used to carry a tuple-like overload_types, so adding a
    convention had to search Cs for an existing convention with the same
    is_direct and dispatch_type and merge the overload lists into it. That
    merge is the only reason add_conv_t, merge_conv_t, add_conv_reduction,
    and conv_specialization_t exist.
    
    Replace overload_types with a single overload_type, so add_convention
    contributes one convention per overload and merging a convention into Cs
    becomes deduplicating concatenation. Duplicates are still collapsed, so
    build() is unaffected at both compile time and run time.
    
    Accessors still have to be formed per dispatch type, or a dispatch with
    several overloads would contribute several accessor bases instead of one
    overload set. Regroup the conventions by dispatch type via conv_group
    and conv_groups_merge_t just before generating the accessors, which also
    keeps overload shadowing working.
    
    Generalize the reduction helpers to carry extra arguments (reduction_t)
    and add flattening_merge_t, which merge_tuples_t and conv_groups_merge_t
    are both expressed in terms of.
    
    This changes the ProBasicConvention and ProConvention requirements, so a
    hand-written convention type must be updated.
    mingxwa authored Aug 30, 2026
    Configuration menu
    Copy the full SHA
    96e00f2 View commit details
    Browse the repository at this point in the history

Commits on Aug 31, 2026

  1. Invoke through an erased context instead of the proxy type (#68)

    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 authored Aug 31, 2026
    Configuration menu
    Copy the full SHA
    ec7e7de View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    ca59fe2 View commit details
    Browse the repository at this point in the history

Commits on Sep 1, 2026

  1. Reach metadata by conversion instead of by named lookup (#71)

    meta_storage looked a meta up with get<M>(), which resolved to a
    static_cast over a flat set of base classes. That works only while every
    meta is a direct base of the storage, and cannot reach a meta held
    inside an aggregate.
    
    Replace it with proxy_meta, whose contained metas are reached through a
    conversion operator. A meta is contained if the metadata converts to it
    without throwing, so an aggregate that itself converts to a meta makes
    that meta reachable too. unique_types_t reduces the meta list so that
    each entry is contained by exactly one entry, namely itself, which is
    the property that keeps the conversion unambiguous: a duplicate
    collapses onto its leftmost occurrence, and a meta subsumed by an
    aggregate is replaced by that aggregate in place.
    
    Both storages now expose the metadata through operator*, so choosing
    between them is independent of how a meta is looked up.
    static_meta_storage holds a single meta and is chosen by size rather
    than by shape, which separates the metadata layout from the decision to
    hold it out of line. Under pointer authentication, assigning one storage
    from another signs a raw pointer, which meta_ptr now supports.
    
    No functional change.
    mingxwa authored Sep 1, 2026
    Configuration menu
    Copy the full SHA
    07e6520 View commit details
    Browse the repository at this point in the history

Commits on Sep 9, 2026

  1. Reset the proxy when the destructor of the underlying pointer throws (#…

    …82)
    
    * Reset the proxy when the destructor of the underlying pointer throws
    
    proxy::destroy() invoked destruction through an lvalue-qualified
    overload, so the meta_resetting_guard that invoke_impl applies to
    consuming overloads never ran. When the destructor of the underlying
    pointer threw, the metadata still pointed at the destroyed object, so
    has_value() stayed true and the next destruction ran on a dead object.
    Every path that discards a value reached this: reset(),
    operator=(nullptr), both branches of the copy assignment operator, the
    move assignment operator, operator=(P&&) and both emplace overloads.
    
    Destruction is a consuming operation like relocation, so give it the
    same shape. The destroy meta now uses an rvalue-qualified overload,
    which makes erased_context destroy the pointer through destroying_guard
    and makes invoke_impl clear the metadata on both the normal and the
    exceptional path. destroy_dispatch keeps only its tag role and its call
    operator becomes a no-op, because the destruction it used to perform is
    what the rvalue machinery already does.
    
    The added reset is dead on the non-throwing path and the optimizer
    removes it. At -O2 the disassembly of ~proxy, reset and the move
    assignment operator is unchanged for a facade whose destructibility is
    nothrow.
    
    LifetimeTracker gains ThrowingDestructionSession, a Session whose
    destructor throws, which is the first pointer in the suite with a
    potentially throwing destructor and the first use of a facade whose
    destructibility is nontrivial.
    
    * Fix MSVC failure
    mingxwa authored Sep 9, 2026
    Configuration menu
    Copy the full SHA
    7c4e174 View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    e2bae00 View commit details
    Browse the repository at this point in the history
  3. Refuse a mutable proxy_cast on a const contained value (#80)

    proxy_cast_dispatch guarded a reference result with std::is_const_v<T>,
    where T is the deduced operand type and is therefore always a reference.
    A reference type is never const, so the guard never fired and a caller
    could ask a const proxy for a mutable reference to what it contains and
    get one, silently casting the constness away.
    
    The guard now tests the referenced type. proxy_cast<int&> on a proxy
    whose contained value is const throws bad_proxy_cast, and the pointer
    form returns nullptr, which is what the const-qualified overload already
    promised. Reaching a const contained value still works by asking for it
    as const.
    mingxwa authored Sep 9, 2026
    Configuration menu
    Copy the full SHA
    b5ea4a9 View commit details
    Browse the repository at this point in the history

Commits on Sep 10, 2026

  1. Configuration menu
    Copy the full SHA
    f65a24c View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    a9f488f View commit details
    Browse the repository at this point in the history

Commits on Sep 11, 2026

  1. [super! 5/6] Add super facades (#74)

    * Add super facades
    
    add_facade<F> copied F's conventions and reflections into the built
    facade. The two facades then shared behavior but had unrelated metadata,
    so converting a proxy of one to a proxy of the other needed an explicit
    substitution convention and an indirect call to translate.
    
    Give a facade a super_types list. add_facade<F> records F there instead
    of copying, and the metadata of the built facade embeds the metadata of
    each super, so const proxy_meta<Derived>& converts to const
    proxy_meta<Super>&. proxy gains converting constructors and assignment
    operators guarded on exactly that conversion: the metadata is carried
    over directly, and only the contained value is copied or relocated.
    
    A convention whose overload is a facade_aware_overload_t is the
    exception to "not copied": its overload depends on the facade it is
    built into, so it is also checked and made available against the built
    facade. That is what lets as_view declared on a super yield a
    proxy_view of the built facade rather than of the super.
    
    Accessors are formed from the conventions of the facade and of every
    super, regrouped per dispatch type, so an overload set spanning a super
    and the facade that derives from it stays a single overload set.
    observer_facade and weak_facade map super_types through themselves, so
    view-ness and weak-ness are preserved across a conversion to a super.
    
    add_facade<F, true>, deprecated since 4.1.0, is removed.
    The second parameter of add_facade, deprecated since 4.1.0, is retained
    and ignored: a proxy of the built facade already converts to a proxy<F>
    because F is a super. add_facade_with_substitution and
    substitution_dispatch are unchanged and still take precedence where both
    apply; they are addressed in a follow-up.
    
    * Reach the trivial copy and relocation by the source facade
    
    A converting initialization asked the destination facade whether the copy
    or the relocation is trivial, but the triviality that licenses a byte
    copy belongs to the value being moved, which lives in the source. A super
    is never stricter than the facade converting to it, so the byte copy was
    missed whenever the derived facade was the stricter of the two, and the
    value went through the erased invoker instead.
    
    Corrected three lines of the specification along the way. The copy
    assignment is not "as if by auto(rhs).swap(*this)", which is not even
    formed for a facade that forbids relocation. The converting move
    assignment loses the contained value on a throwing relocation exactly as
    the move assignment does. The move constructor is noexcept for trivial
    relocatability, which its documented signature spelled as an equality.
    mingxwa authored Sep 11, 2026
    Configuration menu
    Copy the full SHA
    28e938e View commit details
    Browse the repository at this point in the history

Commits on Sep 13, 2026

  1. Support assignment and swap for a facade that forbids relocation (#77)

    * Fix assignment for a facade that forbids relocation
    
    proxy::operator=(const proxy&) commits a throwing copy through a
    temporary, which needs a move assignment. A facade that forbids
    relocation has none, so the expression re-selected the copy assignment
    itself and recursed until the stack overflowed. proxy::operator=(P&&)
    reaches the same expression and inherited the crash, and failed to
    compile outright for a facade that is not copyable either.
    
    Stage the temporary only where an assignment exists to commit it with,
    and destroy the contained value before constructing the new one
    otherwise. Assignment then works for every facade, at the cost of leaving
    *this without a value when the construction throws, which is the best a
    facade that can neither relocate nor copy without throwing can offer.
    
    Take the staged form whenever the commit cannot throw, which is
    copyability >= nothrow as much as relocatability >= nontrivial. A facade
    with nothrow copyability keeps its old value when the construction
    throws, as one with a trivial copy assignment already did.
    
    * Exchange values by copying when a facade forbids relocation
    
    proxy::swap relocates, so a facade that forbids relocation had no swap at
    all unless it was trivially copyable, even though std::swap already
    exchanged such proxies through the copy constructor and the copy
    assignment, both of which bind an rvalue. The member was missing for
    types the standard library already reports as swappable.
    
    Add an overload for exactly those facades, constrained on relocatability
    == none, that exchanges the values by copying. Splitting it from the
    relocating overload rather than branching inside one keeps both noexcept
    specifications honest: the relocating overload is untouched, and the new
    one is noexcept when the copies and the destructions are. An empty
    operand is exchanged with a single copy rather than through a temporary.
    
    A facade that can relocate keeps relocating, even where the relocation
    can throw and a copy could not. Exchanging by copy is what a facade with
    no other way gets, not a path taken from one that has one.
    
    Documented the constraint the hidden friend has always carried.
    mingxwa authored Sep 13, 2026
    Configuration menu
    Copy the full SHA
    94a64f1 View commit details
    Browse the repository at this point in the history
  2. Reduce direct RTTI to a single reflection (#81)

    direct_rtti added three conventions to carry proxy_cast, so every
    proxiable pointer paid three function pointers of metadata and every cast
    went through one of them. The typeid reflection that the same skill
    installs already records the contained type, and once that type is known
    to match the requested one the cast is a static_cast on the pointer
    storage, so the indirection buys nothing.
    
    direct_rtti now adds one reflection. direct_rtti_reflector inherits
    proxy_typeid_reflector for the type identity and reuses the accessor
    generated for proxy_cast_dispatch, so the five proxy_cast overloads and
    their qualifier mapping stay in one place. proxy_cast_accessor_impl
    gained a private invoke_cast that tests the reflected type and then
    invokes the same dispatch through an erased context with the contained
    type known statically. The indirect path keeps its conventions and
    compiles to the same code as before.
    
    The metadata of a facade built from direct_rtti alone drops from 40 bytes
    to 16, and the lvalue, pointer and rvalue casts compile to 27, 29 and 34
    instructions in place of 46, 37 and 66. No indirect call is left on the
    success path. The rvalue form keeps one on the type mismatch branch,
    where the proxy is reset through the erased destructor.
    mingxwa authored Sep 13, 2026
    Configuration menu
    Copy the full SHA
    5aced7c View commit details
    Browse the repository at this point in the history
Loading