mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-04-01 21:38:44 +00:00
Add weak self make_shared variant (#1299)
This extends the CTL version of make_shared with functionality not found in the STL, with inspiration taken from Rust's Rc class.
This commit is contained in:
parent
aaed879ec7
commit
1bfb348403
2 changed files with 97 additions and 8 deletions
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "exception.h"
|
#include "exception.h"
|
||||||
#include "is_base_of.h"
|
#include "is_base_of.h"
|
||||||
|
#include "is_constructible.h"
|
||||||
#include "is_convertible.h"
|
#include "is_convertible.h"
|
||||||
#include "remove_extent.h"
|
#include "remove_extent.h"
|
||||||
#include "unique_ptr.h"
|
#include "unique_ptr.h"
|
||||||
|
@ -437,6 +438,9 @@ class weak_ptr
|
||||||
template<typename U>
|
template<typename U>
|
||||||
friend class shared_ptr;
|
friend class shared_ptr;
|
||||||
|
|
||||||
|
template<typename U, typename... Args>
|
||||||
|
friend shared_ptr<U> make_shared(Args&&...);
|
||||||
|
|
||||||
element_type* p = nullptr;
|
element_type* p = nullptr;
|
||||||
__::shared_ref* rc = nullptr;
|
__::shared_ref* rc = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -497,11 +501,66 @@ shared_ptr<T>::shared_ptr(U* const p, D d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Our make_shared supports passing a weak self reference as the first parameter
|
||||||
|
// to your constructor, e.g.:
|
||||||
|
//
|
||||||
|
// struct Tree : ctl::weak_self_base
|
||||||
|
// {
|
||||||
|
// ctl::shared_ptr<Tree> l, r;
|
||||||
|
// ctl::weak_ptr<Tree> parent;
|
||||||
|
// Tree(weak_ptr<Tree> const& self, auto&& l2, auto&& r2)
|
||||||
|
// : l(ctl::forward<decltype(l2)>(l2)),
|
||||||
|
// r(ctl::forward<decltype(r2)>(r2))
|
||||||
|
// {
|
||||||
|
// if (l) l->parent = self;
|
||||||
|
// if (r) r->parent = self;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// int main() {
|
||||||
|
// auto t = ctl::make_shared<Tree>(
|
||||||
|
// ctl::make_shared<Tree>(nullptr, nullptr), nullptr);
|
||||||
|
// return t->l->parent.lock().get() == t.get() ? 0 : 1;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// As shown, passing the parameter at object construction time lets you complete
|
||||||
|
// object construction without needing a separate Init method. But because we go
|
||||||
|
// off spec as far as the STL is concerned, there is a potential ambiguity where
|
||||||
|
// you might have a constructor with a weak_ptr first parameter that is intended
|
||||||
|
// to be something other than a self-reference. So this feature is opt-in by way
|
||||||
|
// of inheriting from the following struct.
|
||||||
|
struct weak_self_base
|
||||||
|
{};
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
shared_ptr<T>
|
shared_ptr<T>
|
||||||
make_shared(Args&&... args)
|
make_shared(Args&&... args)
|
||||||
{
|
{
|
||||||
auto rc = __::shared_emplace<T>::make();
|
unique_ptr rc = __::shared_emplace<T>::make();
|
||||||
|
if constexpr (is_base_of_v<weak_self_base, T> &&
|
||||||
|
is_constructible_v<T, const weak_ptr<T>&, Args...>) {
|
||||||
|
// A __::shared_ref has a virtual weak reference that is owned by all of
|
||||||
|
// the shared references. We can avoid some unnecessary refcount changes
|
||||||
|
// by "borrowing" that reference and passing it to the constructor, then
|
||||||
|
// promoting it to a shared reference by swapping it with the shared_ptr
|
||||||
|
// that we return.
|
||||||
|
weak_ptr<T> w;
|
||||||
|
w.p = &rc->t;
|
||||||
|
w.rc = rc.get();
|
||||||
|
try {
|
||||||
|
rc->construct(const_cast<const weak_ptr<T>&>(w),
|
||||||
|
forward<Args>(args)...);
|
||||||
|
} catch (...) {
|
||||||
|
w.p = nullptr;
|
||||||
|
w.rc = nullptr;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
rc.release();
|
||||||
|
shared_ptr<T> r;
|
||||||
|
swap(r.p, w.p);
|
||||||
|
swap(r.rc, w.rc);
|
||||||
|
return r;
|
||||||
|
} else {
|
||||||
rc->construct(forward<Args>(args)...);
|
rc->construct(forward<Args>(args)...);
|
||||||
shared_ptr<T> r;
|
shared_ptr<T> r;
|
||||||
r.p = &rc->t;
|
r.p = &rc->t;
|
||||||
|
@ -510,6 +569,7 @@ make_shared(Args&&... args)
|
||||||
r->weak_this = r;
|
r->weak_this = r;
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ctl
|
} // namespace ctl
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#include "ctl/is_same.h"
|
||||||
#include "ctl/shared_ptr.h"
|
#include "ctl/shared_ptr.h"
|
||||||
#include "ctl/vector.h"
|
#include "ctl/vector.h"
|
||||||
#include "libc/mem/leaks.h"
|
#include "libc/mem/leaks.h"
|
||||||
|
@ -88,6 +89,21 @@ class SharedThis : public enable_shared_from_this<SharedThis>
|
||||||
class CanShareThis : public enable_shared_from_this<CanShareThis>
|
class CanShareThis : public enable_shared_from_this<CanShareThis>
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
// Sample class used to demonstrate the CTL shared_ptr's weak_self feature.
|
||||||
|
struct Tree : ctl::weak_self_base
|
||||||
|
{
|
||||||
|
ctl::shared_ptr<Tree> l, r;
|
||||||
|
ctl::weak_ptr<Tree> p;
|
||||||
|
Tree(ctl::weak_ptr<Tree> const& self, auto&& l2, auto&& r2)
|
||||||
|
: l(ctl::forward<decltype(l2)>(l2)), r(ctl::forward<decltype(r2)>(r2))
|
||||||
|
{
|
||||||
|
if (l)
|
||||||
|
l->p = self;
|
||||||
|
if (r)
|
||||||
|
r->p = self;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
|
@ -276,6 +292,19 @@ main()
|
||||||
return 25;
|
return 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if constexpr (ctl::is_same_v<shared_ptr<Tree>, ctl::shared_ptr<Tree>>) {
|
||||||
|
// Exercise our off-STL make_shared with weak self support.
|
||||||
|
auto t = ctl::make_shared<Tree>(
|
||||||
|
ctl::make_shared<Tree>(ctl::make_shared<Tree>(nullptr, nullptr),
|
||||||
|
nullptr),
|
||||||
|
ctl::make_shared<Tree>(nullptr, nullptr));
|
||||||
|
auto t2 = t->l->l->p.lock()->p.lock();
|
||||||
|
if (t.owner_before(t2) || t2.owner_before(t))
|
||||||
|
return 26;
|
||||||
|
if (!t.owner_before(t->l) && !t->l.owner_before(t))
|
||||||
|
return 27;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(mrdomino): exercise threads / races. The reference count should be
|
// TODO(mrdomino): exercise threads / races. The reference count should be
|
||||||
// atomically maintained.
|
// atomically maintained.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue