Skip to content

Commit

Permalink
Add enable_shared_from_sibling.
Browse files Browse the repository at this point in the history
  • Loading branch information
evoskuil committed Mar 2, 2024
1 parent ae82227 commit 652d6ef
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 4 deletions.
61 changes: 59 additions & 2 deletions include/bitcoin/network/async/enable_shared_from_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,18 @@
namespace libbitcoin {
namespace network {

/// Thread safe, base class.
/// Empty base optimization using CRTP.
// "The constructors of std::shared_ptr detect the presence of an unambiguous
// and accessible (i.e. public inheritance is mandatory)
// enable_shared_from_this base and assign the newly created std::shared_ptr
// to weak-this if not already owned by a live std::shared_ptr.
// Constructing a std::shared_ptr for an object that is already managed by
// another std::shared_ptr will not consult weak-this and is undefined behavior.
// It is permitted to call shared_from_this only on a previously shared object,
// i.e. on an object managed by std::shared_ptr<T>. Otherwise,
// std::bad_weak_ptr is thrown(by the shared_ptr constructor).."
// en.cppreference.com/w/cpp/memory/enable_shared_from_this

/// Base instance must be downcastable to Derived.
/// Because enable_shared_from_this does not support inheritance.
template <class Base>
class enable_shared_from_base
Expand All @@ -42,6 +52,53 @@ class enable_shared_from_base
std::shared_ptr<Derived> shared_from_base() NOEXCEPT;
};

/// Sibling instance must be castable to Derived:Base.
/// Because enable_shared_from_this/base do not support multiple inheritance.
/// Avoids diamond inheritance ambiguity by requiring a primary inheritance
/// linear path (sibling) from which classes in an independent path (Base)
/// may obtain a shared pointer within their own path (Derived).
template <class Base, class Sibling>
class enable_shared_from_sibling
{
public:
DELETE_COPY_MOVE(enable_shared_from_sibling);

enable_shared_from_sibling() NOEXCEPT;

/// Must be a polymorphic type (to use dynamic_cast).
virtual ~enable_shared_from_sibling() NOEXCEPT;

/// Simplifies capture of the shared pointer for a nop handler.
void nop() volatile NOEXCEPT;

protected:
/// Sibling (not Derived:Base) implements enable_shared_from...
/// Use in Derived to create shared instance of Derived from its Sibling.
/// Undefined behavior if cast from Sibling* to Derived* not well formed.
template <class Derived, bc::if_base_of<Base, Derived> = true>
std::shared_ptr<Derived> shared_from_sibling() NOEXCEPT;
};

// class foo__ : enabled_shared_from_base<foo__>
// class foo_ : foo__
// class foo: foo_
// auto fooptr = foo.shared_from_this()
// auto foo_ptr = foo.shared_from_base<foo_>()
// auto foo__ptr = foo.shared_from_base<foo__>()
//
// class bar__ : enabled_shared_from_sibling<bar__, foo>
// class bar_ : bar__
// class bar : bar_
// bar__/bar_/bar must be joined with foo.
//
// class foobar : public foo, public bar
// auto barptr = foobar.shared_from_sibling<bar>()
// auto bar_ptr = foobar.shared_from_sibling<bar_>()
// auto bar__ptr = foobar.shared_from_sibling<bar__>()
// auto fooptr = foobar.shared_from_base<foo>()
// auto foo_ptr = foobar.shared_from_base<foo_>()
// auto foo__ptr = foobar.shared_from_base<foo__>()

} // namespace network
} // namespace libbitcoin

Expand Down
44 changes: 42 additions & 2 deletions include/bitcoin/network/impl/async/enable_shared_from_base.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@

namespace libbitcoin {
namespace network {

// enable_shared_from_base : enable_shared_from_this
// ----------------------------------------------------------------------------

template <class Base>
void enable_shared_from_base<Base>::nop() volatile NOEXCEPT
void enable_shared_from_base<Base>::
nop() volatile NOEXCEPT
{
}

Expand All @@ -35,8 +39,44 @@ template <class Derived, bc::if_base_of<Base, Derived>>
std::shared_ptr<Derived> enable_shared_from_base<Base>::
shared_from_base() NOEXCEPT
{
// Instance must be downcastable to Derived.
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
return std::static_pointer_cast<Derived>(this->shared_from_this());
BC_POP_WARNING()
}

// enable_shared_from_sibling
// ----------------------------------------------------------------------------

template <class Base, class Sibling>
enable_shared_from_sibling<Base, Sibling>::
enable_shared_from_sibling() NOEXCEPT
{
}

template <class Base, class Sibling>
enable_shared_from_sibling<Base, Sibling>::
~enable_shared_from_sibling() NOEXCEPT
{
}

template <class Base, class Sibling>
void enable_shared_from_sibling<Base, Sibling>::
nop() volatile NOEXCEPT
{
}

template <class Base, class Sibling>
template <class Derived, bc::if_base_of<Base, Derived>>
std::shared_ptr<Derived> enable_shared_from_sibling<Base, Sibling>::
shared_from_sibling() NOEXCEPT
{
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
// Obtain shared pointer to object from Sibling class inheritance path.
const auto sibling = dynamic_cast<Sibling*>(this)->shared_from_this();
BC_POP_WARNING()

// Cast pointer back to Derived (from Base) class it was created from.
return std::dynamic_pointer_cast<Derived>(sibling);
}

} // namespace network
Expand Down

0 comments on commit 652d6ef

Please sign in to comment.