62#define SET_LINK_TYPE std::true_type
63#define SET_LINK std::true_type{}
87 std::cout <<
"Mock Linkable default ctor\n";
90 Linkable(
const Linkable<T> &other) : value_(other.value_) {
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; };
103 explicit Linkable(
const T &other) : value_(other) {
104 std::cout <<
"Mock Linkable value ctor\n";
107 explicit operator bool()
const noexcept {
119 Linkable<T> &
operator=(
const Linkable<T> &other) {
120 std::cout <<
"Mock Linkable assignment\n";
121 value_ = other.value_;
125 void Link(
const Linkable<T> &other) {
126 std::cout <<
"Mock Linkable Link()\n";
127 value_ = other.value_;
132 template <
typename T>
135 std::shared_ptr<T> ref_ =
nullptr;
136 mutable std::vector<Linkable<T> *> backlinks_;
139 static inline std::recursive_mutex s_link_mutex;
149 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
153 auto &parent_backlinks = downlink_->backlinks_;
154 auto it = std::remove(parent_backlinks.begin(), parent_backlinks.end(),
this);
155 if (it != parent_backlinks.end()) {
156 parent_backlinks.erase(it, parent_backlinks.end());
167 for (
auto *child : backlinks_) {
168 if (child->downlink_ ==
this) {
169 if (
bool is_first_link =
false; !is_first_link) {
171 child->downlink_ =
nullptr;
173 child->downlink_ = first_link;
184 ref_(std::make_shared<T>(*other.ref_)) {
196 : ref_(std::make_shared<T>(other)) {
203 template <
typename U = T>
204 std::enable_if_t<!std::is_pointer_v<U>, U *>
210 template <
typename U = T>
211 std::enable_if_t<std::is_pointer_v<U>, std::remove_pointer_t<U> *>
217 template <
typename U = T>
218 std::enable_if_t<!std::is_pointer_v<U>, U &>
224 template <
typename U = T>
225 std::enable_if_t<std::is_pointer_v<U>, std::remove_pointer_t<U> &>
233 *ref_ = *(other.ref_);
244 auto data = std::shared_ptr<T>(ptr, [](T *)
258 template <
typename X>
265 throw std::invalid_argument(
"\n Error in Linkable::Link: null pointer passed.\n");
274 template <
typename... Args,
276 !(std::conjunction_v<std::is_same<std::decay_t<Args>,
Linkable<T>>...>),
279 auto data = std::make_shared<T>(std::forward<Args>(args)...);
283 explicit operator bool() const noexcept {
284 return static_cast<bool>(ref_);
307 operator T *()
const {
309 throw std::runtime_error(
"Attempt to convert empty Linkable to T*");
319 void updateTreeRefs(
const Linkable<T> &target_ref) {
320 for (
auto backlink : backlinks_) {
321 backlink->ref_ = this->ref_;
322 if (backlink == &target_ref) {
323 throw std::logic_error(
"Fatal error: A loop occurred when linking Linkables.");
325 backlink->updateTreeRefs(target_ref);
329 void LinkTreeTo(Linkable<T> &other) {
330 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
336 other.backlinks_.emplace_back(
this);
338 other.updateTreeRefs(other);
342 downlink_->LinkTreeTo(other);
345 throw std::logic_error(
346 "Linkable::Link() called on object already linked to data.\n");
350 void LinkTreeTo(std::shared_ptr<T> &other) {
351 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
353 throw std::logic_error(
"Attempting to link a Linkable<T> to null data.\n");
360 if (rootlink_->ref_) {
361 rootlink_->updateTreeRefs(*rootlink_);
365 if (std::find(downlink_->backlinks_.begin(), downlink_->backlinks_.end(),
this)
366 == downlink_->backlinks_.end()) {
367 downlink_->backlinks_.emplace_back(
this);
369 downlink_->LinkTreeTo(other);
372 throw std::logic_error(
373 "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