Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ the license header:
`MATCHES "//[ \t]*expected-error:[ \t]*([^\r\n]+)"`, and hands it to
`openmethod_compile_fail_test` as the test's `PASS_REGULAR_EXPRESSION`. Adding a test is dropping
in a file - no build-file edit. A file with no marker is a configure-time `FATAL_ERROR`, so a
silently unchecked test cannot slip through. The glob has no `CONFIGURE_DEPENDS` (matching the
silently unchecked test cannot slip through. So is a marker containing a `;`:
`PASS_REGULAR_EXPRESSION` is a CMake list, so the `;` would split the regex into two alternatives
and the test would pass on either half. Write `.*` in its place. The glob has no `CONFIGURE_DEPENDS` (matching the
`test_*.cpp` glob above it), so a new file needs a manual re-run of `cmake`.

Where the expected wording differs across compilers, match the common substring and say why in a
Expand Down
15 changes: 13 additions & 2 deletions doc/modules/ROOT/pages/ref_headers.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@ convenient macros.
* xref:#initialize[`<boost/openmethod/initialize.hpp>`] to initialize the library.
Typically only included in the translation unit containing `main`.

The following headers make it possible to use standard smart pointers in virtual
parameters:
The following headers make it possible to use standard smart pointers with
`virtual_ptr`:

* xref:#std_shared_ptr[`<boost/openmethod/interop/std_shared_ptr.hpp>`] to use
`std::shared_ptr` in virtual parameters.

* xref:#std_unique_ptr[`<boost/openmethod/interop/std_unique_ptr.hpp>`] to use
`std::unique_ptr` in virtual parameters.

* xref:#std_weak_ptr[`<boost/openmethod/interop/std_weak_ptr.hpp>`] to track
objects with `std::weak_ptr` without losing their v-table pointer.

## High-level Headers

[#core]
Expand Down Expand Up @@ -72,6 +75,14 @@ Provides a `virtual_traits` specialization that makes it possible to use a
Provides a `virtual_traits` specialization that makes it possible to use a
`std::unique_ptr` in place of a raw pointer or reference in virtual parameters.

[#std_weak_ptr]
### link:{headers-url}/boost/openmethod/interop/std_weak_ptr.hpp[<boost/openmethod/interop/std_weak_ptr.hpp>]

Provides cpp:weak_virtual_ptr[], a class that tracks an object with a
`std::weak_ptr` and remembers its v-table pointer. It is not a `virtual_ptr`,
and cannot be used in virtual parameters; its `lock` function returns a
cpp:shared_virtual_ptr[], without a hash table lookup.

[#boost_intrusive_ptr]
### link:{headers-url}/boost/openmethod/interop/boost_intrusive_ptr.hpp[<boost/openmethod/interop/boost_intrusive_ptr.hpp>]

Expand Down
47 changes: 47 additions & 0 deletions doc/modules/ROOT/pages/smart_pointers.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,50 @@ pointers:
----
include::example$ast_unique_ptr.cpp[tag=content]
----

[#weak_pointers]
## Weak Pointers

A `std::weak_ptr` observes an object without keeping it alive. Since the object
may be gone, there is nothing to dispatch on: a weak pointer cannot be used in a
virtual parameter, and neither can a `virtual_ptr` to a weak pointer. Still, an
object that is tracked by weak pointers - in a cache, an observer list, or a
back pointer - is typically an object that methods will be called on, once a
weak pointer has been locked.

- cpp:weak_virtual_ptr<Class>[] tracks an object with a `std::weak_ptr`, and
remembers its v-table pointer

A `weak_virtual_ptr` is a storage facility, not a `virtual_ptr`. It is
constructed from a `shared_virtual_ptr` (or from a `std::shared_ptr` or a
`std::weak_ptr`), and it remembers the v-table pointer along with the weak
pointer. It cannot be dereferenced. Its `lock` function returns a
`shared_virtual_ptr`, which can be passed to methods. Since the v-table pointer
is copied, not looked up, `lock` costs no more than `std::weak_ptr::lock`. It
returns an empty `shared_virtual_ptr` if the object no longer exists.

[source,c++]
----
shared_virtual_ptr<Animal> animal = make_shared_virtual<Dog>();
weak_virtual_ptr<Animal> observer = animal;

std::cout << poke(observer.lock()) << "\n"; // bark

animal = nullptr;
std::cout << std::boolalpha << observer.expired() << "\n"; // true
----

Remembering the v-table pointer is safe with respect to the lifetime of the
object, because a `std::weak_ptr` keeps the control block alive: once the
object has been destroyed, the weak pointer stays expired, and the v-table
pointer can never be applied to another object. As for any `virtual_ptr`, the
v-table pointer is invalidated if `initialize` is called again, unless the
registry uses the cpp:indirect_vptr[] policy.

A `weak_virtual_ptr` converts to a `weak_virtual_ptr` to a base class, but not
to a plain or a shared `virtual_ptr`. A cast to a derived class requires the
object: use `lock`, then `cast`. Since it is not a `virtual_ptr`, a
`weak_virtual_ptr` cannot be used in a virtual parameter, but it can be passed
to a method as an ordinary parameter. Support for `std::weak_ptr` is provided
in `<boost/openmethod/interop/std_weak_ptr.hpp>`, which also includes the
`std::shared_ptr` header.
33 changes: 33 additions & 0 deletions doc/modules/ROOT/snippets/smart_pointers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <boost/openmethod/initialize.hpp>
#include <boost/openmethod/interop/std_shared_ptr.hpp>
#include <boost/openmethod/interop/std_unique_ptr.hpp>
#include <boost/openmethod/interop/std_weak_ptr.hpp>

#define BOOST_TEST_MODULE openmethod
#include <boost/test/unit_test.hpp>
Expand Down Expand Up @@ -194,3 +195,35 @@ BOOST_AUTO_TEST_CASE(unique_ptr_examples) {
BOOST_TEST(cout.str() == "bark\nhiss\n");
}
}

BOOST_AUTO_TEST_CASE(weak_ptr_examples) {
initialize();

{
using namespace shared_vptr;
capture_cout cout;

// tag::weak_lock[]
shared_virtual_ptr<Animal> animal = make_shared_virtual<Dog>();
weak_virtual_ptr<Animal> observer = animal;

std::cout << poke(observer.lock()) << "\n"; // bark

animal = nullptr;
std::cout << std::boolalpha << observer.expired() << "\n"; // true
// end::weak_lock[]

BOOST_TEST(cout.str() == "bark\ntrue\n");
}

{
// tag::weak_pointer[]
shared_virtual_ptr<Animal> animal = make_shared_virtual<Dog>();
weak_virtual_ptr<Animal> observer = animal;
std::weak_ptr<Animal> weak = observer.pointer();

BOOST_TEST(animal.pointer().use_count() == 1);
BOOST_TEST(weak.lock() == animal.pointer());
// end::weak_pointer[]
}
}
27 changes: 27 additions & 0 deletions include/boost/openmethod/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ using macro_default_registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY;
template<typename...>
constexpr bool false_t = false; // workaround before CWG2518/P2593R1

template<class>
struct virtual_ptr_access;

} // namespace detail

namespace detail {
Expand Down Expand Up @@ -955,6 +958,26 @@ inline auto unbox_vptr(const vptr_type* vpp) {

inline vptr_type null_vptr = nullptr;

// Access to the parts of a `virtual_ptr`, for the classes that carry a
// v-table pointer of their own and exchange it with one: copy it from a
// `virtual_ptr`, hand it back later. The pointer is the boxed one - under
// `indirect_vptr`, the address of the cell that `initialize()` rewrites, which
// the public `vptr()` unboxes away - and constructing with a given v-table
// pointer skips the lookup, which no public constructor does.
template<class VirtualPtr>
struct virtual_ptr_access {
using boxed_vptr_type = decltype(VirtualPtr::vp);

static auto boxed_vptr(const VirtualPtr& ptr) -> boxed_vptr_type {
return ptr.vp;
}

template<class Arg>
static auto make(Arg&& obj, boxed_vptr_type vp) -> VirtualPtr {
return VirtualPtr(std::forward<Arg>(obj), vp);
}
};

} // namespace detail

//! Create a `virtual_ptr` for an object of a known exact class.
Expand Down Expand Up @@ -1100,6 +1123,8 @@ class virtual_ptr {
#ifndef __MRDOCS__
template<class, class, typename>
friend class virtual_ptr;
template<class>
friend struct detail::virtual_ptr_access;
template<class, typename Arg>
friend auto final_virtual_ptr(Arg&& obj);
#endif
Expand Down Expand Up @@ -1458,6 +1483,8 @@ class virtual_ptr<
#ifndef __MRDOCS__
template<class, class, typename>
friend class virtual_ptr;
template<class>
friend struct detail::virtual_ptr_access;
template<class, typename Arg>
friend auto final_virtual_ptr(Arg&& obj);
#endif
Expand Down
Loading
Loading