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
11 changes: 11 additions & 0 deletions doc/modules/ROOT/pages/ref_headers.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ 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 `virtual_ptr` that tracks an object with a
`std::weak_ptr` and remembers its v-table pointer. It 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
41 changes: 41 additions & 0 deletions doc/modules/ROOT/pages/smart_pointers.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,47 @@ in `<boost/openmethod/interop/std_unique_ptr.hpp>`, and support for
`<boost/openmethod/interop/boost_intrusive_ptr.hpp>`. `<boost/openmethod.hpp>`
does not include smart pointer headers, so they must be included explicitly.

[#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>[] is an alias for `virtual_ptr<std::weak_ptr<Class>>`

A `weak_virtual_ptr` is a storage facility. 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 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.

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`. Support for `std::weak_ptr` is provided in
`<boost/openmethod/interop/std_weak_ptr.hpp>`, which also includes the
`std::shared_ptr` header.

Here is a variation of the AST example that uses dynamic allocation and unique
pointers:

Expand Down
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_virtual_ptr_alias[]
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_virtual_ptr_alias[]
}
}
20 changes: 16 additions & 4 deletions include/boost/openmethod/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,13 @@ struct same_smart_ptr_aux<
typename virtual_traits<Class, Registry>::template rebind<
typename Other::element_type>> {};

template<class T, typename = void>
constexpr bool has_get = false;

template<class T>
constexpr bool
has_get<T, std::void_t<decltype(std::declval<const T&>().get())>> = true;

} // namespace detail

BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS
Expand Down Expand Up @@ -1007,8 +1014,10 @@ class virtual_ptr {
//! @li @c Other's object pointer must be assignable to a @c Class*.
template<
class Other,
typename = std::enable_if_t<std::is_constructible_v<
Class*, typename virtual_ptr<Other, Registry>::element_type*>>>
typename = std::enable_if_t<
std::is_constructible_v<
Class*, typename virtual_ptr<Other, Registry>::element_type*> &&
detail::has_get<virtual_ptr<Other, Registry>>>>
virtual_ptr(const virtual_ptr<Other, Registry>& other) :
vp(other.vp), obj(other.get()) {
}
Expand Down Expand Up @@ -1117,8 +1126,11 @@ class virtual_ptr {
//! @li @c Other's object pointer must be assignable to a @c Class*.
template<
class Other,
typename = std::enable_if_t<std::is_assignable_v<
Class*&, typename virtual_ptr<Other, Registry>::element_type*>>>
typename = std::enable_if_t<
std::is_assignable_v<
Class*&,
typename virtual_ptr<Other, Registry>::element_type*> &&
detail::has_get<virtual_ptr<Other, Registry>>>>
virtual_ptr& operator=(const virtual_ptr<Other, Registry>& other) {
obj = other.get();
vp = other.vp;
Expand Down
Loading
Loading