Conversation
Closes boostorg#73. Add <boost/openmethod/interop/std_weak_ptr.hpp>, providing weak_virtual_ptr<Class>, an alias for virtual_ptr<std::weak_ptr<Class>>. It tracks an object with a std::weak_ptr and remembers its v-table pointer, so that lock() returns a shared_virtual_ptr without a hash table lookup. Remembering the vptr is safe: the weak pointer keeps the control block alive, so once the object is destroyed it stays expired for good. It is a storage facility only. It is constructed from a shared_virtual_ptr (or a std::shared_ptr or std::weak_ptr), converts to a weak_virtual_ptr to a base class, and offers lock(), expired(), use_count(), reset(), pointer() and vptr(). It cannot be dereferenced, and it cannot be used as a virtual parameter - neither as virtual_<std::weak_ptr<T>> nor as weak_virtual_ptr<T> - because the object may no longer exist; validate_method_parameter specializations reject both with "a weak pointer cannot be a virtual parameter; call lock() first". std::weak_ptr does not fit the generic smart-pointer specialization of virtual_ptr, which needs get(), operator* and a conversion to bool, so the specialization is written by hand. virtual_traits is deliberately not specialized for std::weak_ptr: that keeps IsSmartPtr false, which is what lets the hand-written specialization win. In core.hpp, the plain virtual_ptr's converting constructor and assignment from another virtual_ptr now also require the source to have get() (detail::has_get), so a weak source is a clean substitution failure instead of a hard error in the body, and is_constructible reports it correctly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… test
The dispatch test registered the static member functions of a local class
as override<> template arguments. That is valid C++17, but gcc before 13
rejects it ("has no linkage"), failing the gcc-10/11/12 and Cygwin 32-bit
jobs. Use namespace-scope function templates instead, as test_util.hpp's
poke_bear does.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jll63
force-pushed
the
feature/weak-ptr-standalone
branch
from
September 15, 2026 12:45
b485a4b to
5d03e1c
Compare
|
An automated preview of the documentation is available at https://110.openmethod.prtest3.cppalliance.org/libs/openmethod/doc/html/index.html If more commits are pushed to the pull request, the docs will rebuild at the same URL. 2026-09-15 14:02:10 UTC |
jll63
force-pushed
the
feature/weak-ptr-standalone
branch
from
September 15, 2026 12:50
5d03e1c to
c1a8a6a
Compare
As a partial specialization of virtual_ptr, weak_virtual_ptr matched everything written for virtual_ptr: is_virtual, so it could not be an ordinary non-virtual method parameter; the converting constructors of the plain virtual_ptr, hence the has_get gate in core.hpp; the free operator==, which then failed inside core.hpp; final_virtual_ptr; and a user virtual_traits<std::weak_ptr> made it ambiguous. A weak pointer is not a virtual_ptr - it cannot be dereferenced or dispatched on - so model it as a class of its own, with the same members. What it needs from a shared virtual_ptr has no public route: the boxed v-table pointer, which under indirect_vptr is the address of the cell that initialize() rewrites and which vptr() unboxes away, and the constructor that takes a v-table pointer, so that lock() skips the lookup. core.hpp therefore gains detail::virtual_ptr_access, a generic door to those two things, befriended by both virtual_ptr specializations; weak_virtual_ptr uses it to copy the pointer from a shared virtual_ptr and hand it back in lock(). core names no client class: the next adaptor - boost::weak_ptr - uses the same door. Fold in the review's findings on the way. Move constructors and assignments are noexcept, so containers relocate by moving. Construction from a std::weak_ptr to another class locks it once, not twice, and an expired source is copied as is, so that it keeps its control block as far as the standard library allows - libstdc++ shares ownership with a source that is expired but not empty, as [util.smartptr.weak.const] requires; libc++ locks first, so there an expired source of a different class yields an empty weak pointer. owner_before and swap support the owner-keyed containers a cache of weak pointers is built on - with a comparator of one's own, since std::owner_less<void> is generic only in libstdc++; MSVC's and libc++'s accept std::shared_ptr and std::weak_ptr alone. The "call lock() first" diagnostic now fires for every form of a weak virtual parameter - value, &, const& and && - and for virtual_<weak_virtual_ptr<T>>; a bare weak_virtual_ptr<T> parameter is valid. A virtual_ptr<std::weak_ptr<T>>, which is what final_virtual_ptr(std::weak_ptr) would build, is rejected with "use weak_virtual_ptr", unless virtual_traits is specialized for std::weak_ptr. The doc comment says what a remembered v-table pointer is safe against, and what it is not: a second initialize(), unless the registry uses indirect_vptr. Like the smart pointer aliases, weak_virtual_ptr defaults its registry to the affinity its class declares, so that it agrees with the shared_virtual_ptr it converts to and from. The compile-fail markers no longer contain a `;`: PASS_REGULAR_EXPRESSION is a CMake list, so it split each regex into two alternatives, and a test passed on either half. The CMake loop now refuses such a marker. Docs: the Weak Pointers section no longer swallows the unique_ptr AST example, describes a class rather than an alias, and ref_headers.adoc no longer lists the header under "use in virtual parameters". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018FzFtVWNk6d4Wut5SboCZC
jll63
force-pushed
the
feature/weak-ptr-standalone
branch
from
September 15, 2026 13:58
c1a8a6a to
ee6e1df
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
(Written by Claude Code, on behalf of @jll63.)
Closes #73. Supersedes #100, whose two commits this branch carries unchanged, plus one more.
Summary
Adds
<boost/openmethod/interop/std_weak_ptr.hpp>, providingweak_virtual_ptr<Class>: a class that tracks an object with astd::weak_ptrand remembers its v-table pointer, so thatlock()returns ashared_virtual_ptrwith no hash table lookup — it costs exactlystd::weak_ptr::lock()— and the class need not be polymorphic. Remembering the vptr is safe with respect to the object's lifetime: the weak pointer keeps the control block alive, so once the object is destroyed it stays expired for good. As for anyvirtual_ptr, the vptr is invalidated by a secondinitialize()unless the registry usesindirect_vptr.weak_virtual_ptris not avirtual_ptr. #100 modelled it as a partial specializationvirtual_ptr<std::weak_ptr<Class>>, and a/code-review maxpass found that shape to be the root cause of most of its findings: being avirtual_ptrmade it matchis_virtual(so it could not be an ordinary, non-virtual method parameter), the plainvirtual_ptr's converting constructors (hence ahas_getgate incore.hpp), the freeoperator==(which then failed insidecore.hpp),final_virtual_ptr, and a uservirtual_traits<std::weak_ptr>made the specialization ambiguous. As a class of its own it matches none of that:shared_virtual_ptr(copies the vptr), anotherweak_virtual_ptr(upcasts, copy or move), or astd::shared_ptr/std::weak_ptr(acquires the vptr; expired ⇒ null vptr, control block kept);lock(),expired(),use_count(),owner_before()(against a weak or a sharedvirtual_ptr),swap(),reset(),pointer(),vptr(); moves arenoexcept.owner_beforeis what an owner-keyed container's comparator calls; note thatstd::owner_less<void>is generic only in libstdc++ — MSVC's and libc++'s acceptstd::shared_ptrandstd::weak_ptralone — so such a container is best given a comparator of its own;get(),operator*,operator->,cast<>(), comparison operators ormake_weak_virtual;observe(virtual_ptr<Animal>, weak_virtual_ptr<Animal>)), not as a virtual one:virtual_<std::weak_ptr<T>>andvirtual_<weak_virtual_ptr<T>>, in all four forms (value,&,const&,&&), are rejected with "a weak pointer cannot be a virtual parameter; call lock() first";virtual_ptr<std::weak_ptr<T>>— which is whatfinal_virtual_ptr(std::weak_ptr)would build — is rejected with "a std::weak_ptr cannot be wrapped in a virtual_ptr; use weak_virtual_ptr", unlessvirtual_traitsis specialized forstd::weak_ptr, in which case the specialization steps aside.The one change in
core.hppWhat the weak class needs from a
shared_virtual_ptrhas no public route: the boxed v-table pointer (underindirect_vptr, the address of the cell thatinitialize()rewrites, whichvptr()unboxes away) and the private constructor that takes a v-table pointer. Rather than have core befriend a class from an interop header,core.hppgainsdetail::virtual_ptr_access<VirtualPtr>— a generic door to those two things (boxed_vptr_type,boxed_vptr(ptr),make(obj, vptr)), befriended by bothvirtual_ptrspecializations in the same wayregistrybefriendsdetail::use_class_aux.std_weak_ptr.hppis an ordinary client of it; core names no client class, and the comingboost::weak_ptradaptor uses the same door.final_virtual_ptris unchanged.Rebased on
develop28494b6(#96): like the smart pointer aliases,weak_virtual_ptrdefaults its registry to the affinity its class declares, so it agrees with theshared_virtual_ptrit converts to and from.test/test_adl_registry_smart_ptr.cppcovers it.Tests and docs
test/test_weak_virtual_ptr.cpp: 10 cases over the 4 direct/indirect × vector/map registries (including dispatch with a weak non-virtual parameter, an owner-keyedstd::set,swap, an expired source), plusstatic_asserts on the allowed and rejected conversions and on nothrow moves.virtual_<std::weak_ptr<T>>,virtual_<weak_virtual_ptr<T>>,final_virtual_ptr(std::weak_ptr). Theirexpected-errormarkers no longer contain a;—PASS_REGULAR_EXPRESSIONis a CMake list, so a;split each regex into two alternatives and a test passed on either half;test/CMakeLists.txtnow refuses such a marker.smart_pointers.adoc(after the AST example, describing a class rather than an alias),ref_headers.adocentries,weak_lock/weak_pointertags in thesmart_pointers.cppsnippet, referenced by the doc comments.Verified locally: gcc 13.3 Debug
-Werror, full suite 189/189 (rebased ondevelop28494b6, so including #96's tests); clang 22 on the new test, the compile-fail tests and a set of ad-hoc probes; all compile-fail tests report the intendedstatic_assertas the first error on both compilers; Antora build clean (no MrDocs leakage,weak_virtual_ptrhas its own reference page, all@ref/cpp:targets resolve). CI on the fork: https://github.com/jll63/openmethod/actions/runs/34971218600.🤖 Generated with Claude Code
https://claude.ai/code/session_018FzFtVWNk6d4Wut5SboCZC