62#include <sanitizer/lsan_interface.h>
63#define SET_LINK_TYPE std::true_type
64#define SET_LINK std::true_type{}
71 namespace PersistentObjectRegistry {
75 auto* bh =
new std::vector<std::shared_ptr<void>>();
76#ifdef __SANITIZE_ADDRESS__
77 __lsan_ignore_object(bh);
84 using namespace PersistentObjectRegistry;
97 std::cout <<
"Mock Linkable default ctor\n";
101 std::cout <<
"Mock Linkable copy ctor (deep copy)\n";
105 std::cout <<
"Mock Linkable linking ctor\n";
109 std::cout <<
"Mock Linkable reset()\n";
112 T *
get() {
return &T; };
114 explicit Linkable(
const T &other) : value_(other) {
115 std::cout <<
"Mock Linkable value ctor\n";
118 explicit operator bool() const noexcept {
131 std::cout <<
"Mock Linkable assignment\n";
132 value_ = other.value_;
137 std::cout <<
"Mock Linkable Link()\n";
138 value_ = other.value_;
143 template <
typename T>
146 std::shared_ptr<T> ref_ =
nullptr;
147 mutable std::vector<Linkable<T> *> backlinks_;
150 static inline std::recursive_mutex s_link_mutex;
160 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
164 auto &parent_backlinks = downlink_->backlinks_;
165 auto it = std::remove(parent_backlinks.begin(), parent_backlinks.end(),
this);
166 if (it != parent_backlinks.end()) {
167 parent_backlinks.erase(it, parent_backlinks.end());
178 for (
auto *child : backlinks_) {
179 if (child->downlink_ ==
this) {
180 if (
bool is_first_link =
false; !is_first_link) {
182 child->downlink_ =
nullptr;
184 child->downlink_ = first_link;
195 ref_(std::make_shared<T>(*other.ref_)) {
207 : ref_(std::make_shared<T>(other)) {
214 template <
typename U = T>
215 std::enable_if_t<!std::is_pointer_v<U>, U *>
221 template <
typename U = T>
222 std::enable_if_t<std::is_pointer_v<U>, std::remove_pointer_t<U> *>
228 template <
typename U = T>
229 std::enable_if_t<!std::is_pointer_v<U>, U &>
235 template <
typename U = T>
236 std::enable_if_t<std::is_pointer_v<U>, std::remove_pointer_t<U> &>
244 *ref_ = *(other.ref_);
255 auto data = std::shared_ptr<T>(ptr, [](T *)
269 template <
typename X>
276 throw std::invalid_argument(
"\n Error in Linkable::Link: null pointer passed.\n");
285 template <
typename... Args,
287 !(std::conjunction_v<std::is_same<std::decay_t<Args>,
Linkable<T>>...>),
290 auto data = std::make_shared<T>(std::forward<Args>(args)...);
295 template <
typename... Args,
297 !(std::conjunction_v<std::is_same<std::decay_t<Args>,
Linkable<T>>...>),
300 auto raw_data =
new T(std::forward<Args>(args)...);
305 explicit operator bool() const noexcept {
306 return static_cast<bool>(ref_);
329 operator T *()
const {
331 throw std::runtime_error(
"Attempt to convert empty Linkable to T*");
341 void updateTreeRefs(
const Linkable<T> &target_ref) {
342 for (
auto backlink : backlinks_) {
343 backlink->ref_ = this->ref_;
344 if (backlink == &target_ref) {
345 throw std::logic_error(
"Fatal error: A loop occurred when linking Linkables.");
347 backlink->updateTreeRefs(target_ref);
352 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
358 other.backlinks_.emplace_back(
this);
360 other.updateTreeRefs(other);
364 downlink_->LinkTreeTo(other);
367 throw std::logic_error(
368 "Linkable::Link() called on object already linked to data.\n");
372 void LinkTreeTo(std::shared_ptr<T> &other) {
373 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
375 throw std::logic_error(
"Attempting to link a Linkable<T> to null data.\n");
382 if (rootlink_->ref_) {
383 rootlink_->updateTreeRefs(*rootlink_);
387 if (std::find(downlink_->backlinks_.begin(), downlink_->backlinks_.end(),
this)
388 == downlink_->backlinks_.end()) {
389 downlink_->backlinks_.emplace_back(
this);
391 downlink_->LinkTreeTo(other);
394 throw std::logic_error(
395 "Linkable::Link() called on object already linked to data.\n");
void Link(Linkable< T > &other)
Reset Link to another Linakble:
std::enable_if_t<!std::is_pointer_v< U >, U * > operator->() const
-> foward For non-pointer T
Linkable()
Default constructor.
void ShareLink(std::shared_ptr< T > other)
void LinkToRaw(T *ptr)
Link to an existing T object via pointer (non-owning)
std::enable_if_t< std::is_pointer_v< U >, std::remove_pointer_t< U > & > operator*() const
operator* for pointer T: returns reference to the pointed-to object
Linkable(Linkable< T > &other, std::true_type)
The EXPLICIT linking copy ctor.
Linkable & operator=(const T &other) const
const Linkable< T > & operator=(const Linkable< T > &other) const
Allow CONTENT update (not reference) through assignment This is allowed even for const wrapper.
Linkable(const Linkable< T > &other)
Copy constructor does deep copy, not link This is safest and default.
void ConstructAndRawLink(Args &&... args)
Non owning construct and link.
std::enable_if_t< std::is_pointer_v< U >, std::remove_pointer_t< U > * > operator->() const
operator-> forward For pointer T
std::enable_if_t<!std::is_pointer_v< U >, U & > operator*() const
operator* for non-pointer T: returns reference to T
Linkable(const T &other)
Copy constructor does deep copy, not link This is safest and default.
void ConstructAndLink(Args &&... args)
Link and own a T and link to it.
std::mutex s_registry_mutex