62#define SET_LINK_TYPE std::true_type
63#define SET_LINK std::true_type{}
67 namespace PersistentObjectRegistry {
74 using namespace PersistentObjectRegistry;
87 std::cout <<
"Mock Linkable default ctor\n";
91 std::cout <<
"Mock Linkable copy ctor (deep copy)\n";
95 std::cout <<
"Mock Linkable linking ctor\n";
99 std::cout <<
"Mock Linkable reset()\n";
102 T *
get() {
return &T; };
104 explicit Linkable(
const T &other) : value_(other) {
105 std::cout <<
"Mock Linkable value ctor\n";
108 explicit operator bool() const noexcept {
121 std::cout <<
"Mock Linkable assignment\n";
122 value_ = other.value_;
127 std::cout <<
"Mock Linkable Link()\n";
128 value_ = other.value_;
133 template <
typename T>
136 std::shared_ptr<T> ref_ =
nullptr;
137 mutable std::vector<Linkable<T> *> backlinks_;
140 static inline std::recursive_mutex s_link_mutex;
150 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
154 auto &parent_backlinks = downlink_->backlinks_;
155 auto it = std::remove(parent_backlinks.begin(), parent_backlinks.end(),
this);
156 if (it != parent_backlinks.end()) {
157 parent_backlinks.erase(it, parent_backlinks.end());
168 for (
auto *child : backlinks_) {
169 if (child->downlink_ ==
this) {
170 if (
bool is_first_link =
false; !is_first_link) {
172 child->downlink_ =
nullptr;
174 child->downlink_ = first_link;
185 ref_(std::make_shared<T>(*other.ref_)) {
197 : ref_(std::make_shared<T>(other)) {
204 template <
typename U = T>
205 std::enable_if_t<!std::is_pointer_v<U>, U *>
211 template <
typename U = T>
212 std::enable_if_t<std::is_pointer_v<U>, std::remove_pointer_t<U> *>
218 template <
typename U = T>
219 std::enable_if_t<!std::is_pointer_v<U>, U &>
225 template <
typename U = T>
226 std::enable_if_t<std::is_pointer_v<U>, std::remove_pointer_t<U> &>
234 *ref_ = *(other.ref_);
245 auto data = std::shared_ptr<T>(ptr, [](T *)
259 template <
typename X>
266 throw std::invalid_argument(
"\n Error in Linkable::Link: null pointer passed.\n");
275 template <
typename... Args,
277 !(std::conjunction_v<std::is_same<std::decay_t<Args>,
Linkable<T>>...>),
280 auto data = std::make_shared<T>(std::forward<Args>(args)...);
284 explicit operator bool() const noexcept {
285 return static_cast<bool>(ref_);
308 operator T *()
const {
310 throw std::runtime_error(
"Attempt to convert empty Linkable to T*");
320 void updateTreeRefs(
const Linkable<T> &target_ref) {
321 for (
auto backlink : backlinks_) {
322 backlink->ref_ = this->ref_;
323 if (backlink == &target_ref) {
324 throw std::logic_error(
"Fatal error: A loop occurred when linking Linkables.");
326 backlink->updateTreeRefs(target_ref);
331 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
337 other.backlinks_.emplace_back(
this);
339 other.updateTreeRefs(other);
343 downlink_->LinkTreeTo(other);
346 throw std::logic_error(
347 "Linkable::Link() called on object already linked to data.\n");
351 void LinkTreeTo(std::shared_ptr<T> &other) {
352 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
354 throw std::logic_error(
"Attempting to link a Linkable<T> to null data.\n");
361 if (rootlink_->ref_) {
362 rootlink_->updateTreeRefs(*rootlink_);
366 if (std::find(downlink_->backlinks_.begin(), downlink_->backlinks_.end(),
this)
367 == downlink_->backlinks_.end()) {
368 downlink_->backlinks_.emplace_back(
this);
370 downlink_->LinkTreeTo(other);
373 throw std::logic_error(
374 "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.
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::vector< std::shared_ptr< void > > black_hole
std::mutex s_registry_mutex