DLG4::VolumeBuilders
A fluent interface for Geant4 geometry definition.
Loading...
Searching...
No Matches
Linkable.hh
Go to the documentation of this file.
1#pragma once
52// Wrap an object in a linkable pointer.
53// Link to other reference wrappers.
54#ifndef LINKABLE_HH
55#define LINKABLE_HH
56
57#include <memory>
58#include <mutex>
59#include <stdexcept>
60#include <type_traits>
61#include <vector>
62#define SET_LINK_TYPE std::true_type
63#define SET_LINK std::true_type{}
64
65// Geant things need to not be deleted:
67 namespace PersistentObjectRegistry {
68 // For Geant, we need things left persistent.
69 // Yes, in header this is multiple "registries" but this is a black hole anyway.
70 inline std::vector<std::shared_ptr<void>> black_hole;
71 inline std::mutex s_registry_mutex;
72 }
73
74 using namespace PersistentObjectRegistry;
75
76 // enable a mocked version that helps code analysis, now incomplete though.
77 //#define DEBUG
78#ifdef DEBUG
79 // a mock version to help with IDE.
80 template <typename T>
81 class Linkable {
82 private:
83 T value_; // Store a plain object for simplicity
84
85 public:
86 Linkable() : value_() {
87 std::cout << "Mock Linkable default ctor\n";
88 }
89
90 Linkable(const Linkable<T> &other) : value_(other.value_) {
91 std::cout << "Mock Linkable copy ctor (deep copy)\n";
92 }
93
94 Linkable(const Linkable<T> &other, SET_LINK_TYPE) : value_(other.value_) {
95 std::cout << "Mock Linkable linking ctor\n";
96 }
97
98 void reset() {
99 std::cout << "Mock Linkable reset()\n";
100 }
101
102 T *get() { return &T; };
103
104 explicit Linkable(const T &other) : value_(other) {
105 std::cout << "Mock Linkable value ctor\n";
106 }
107
108 explicit operator bool() const noexcept {
109 return true;
110 }
111
112 T *operator->() {
113 return &value_;
114 }
115
116 T &operator*() {
117 return value_;
118 }
119
120 Linkable<T> &operator=(const Linkable<T> &other) {
121 std::cout << "Mock Linkable assignment\n";
122 value_ = other.value_;
123 return *this;
124 }
125
126 void Link(const Linkable<T> &other) {
127 std::cout << "Mock Linkable Link()\n";
128 value_ = other.value_;
129 }
130 };
131#else
132
133 template <typename T>
134 class Linkable {
135 private:
136 std::shared_ptr<T> ref_ = nullptr; // points to the source if any
137 mutable std::vector<Linkable<T> *> backlinks_; // points to things that linked to us.
138 mutable Linkable<T> *downlink_ = nullptr; // points to things we linked to.
139 mutable Linkable<T> *rootlink_ = nullptr;
140 static inline std::recursive_mutex s_link_mutex;
141
142 public:
145 : ref_() {
146 }
147
148 //destructor
150 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
151 // remove ourselves from the backlinks of the downlink
152 // this prevents corrupt backlink propagation after we are deleted.
153 if (downlink_) {
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());
158 }
159 }
160 // remove downlinks from our backlinks
161 // Does this orphan chains?:
162 // If we have a ref, link to will error anyway
163 // If we don't have a ref, we just become the root.
164 // No harm. Nothing linked, nothing lost.
165 // But backlinks become detatched from each other, have different views.
166 // So relink to the first backlink, making it the root.
167 Linkable<T> *first_link = nullptr;
168 for (auto *child : backlinks_) {
169 if (child->downlink_ == this) {
170 if (bool is_first_link = false; !is_first_link) {
171 first_link = child;
172 child->downlink_ = nullptr;
173 } else {
174 child->downlink_ = first_link;
175 }
176 }
177 }
178 backlinks_.clear();
179 //if this matters, something is already very wrong. But it can help unmask that.
180 }
181
184 Linkable(const Linkable<T> &other) :
185 ref_(std::make_shared<T>(*other.ref_)) {
186 }
187
190 ref_ = nullptr;
191 LinkTreeTo(other);
192 }
193
196 explicit Linkable(const T &other)
197 : ref_(std::make_shared<T>(other)) {
198 }
199
200 //
201 // const Forwarding methods:
202
204 template <typename U = T>
205 std::enable_if_t<!std::is_pointer_v<U>, U *>
206 operator->() const {
207 return ref_.get();
208 }
209
211 template <typename U = T>
212 std::enable_if_t<std::is_pointer_v<U>, std::remove_pointer_t<U> *>
213 operator->() const {
214 return *ref_;
215 }
216
218 template <typename U = T>
219 std::enable_if_t<!std::is_pointer_v<U>, U &>
220 operator*() const {
221 return *ref_;
222 }
223
225 template <typename U = T>
226 std::enable_if_t<std::is_pointer_v<U>, std::remove_pointer_t<U> &>
227 operator*() const {
228 return **ref_;
229 }
230
233 const Linkable<T> &operator=(const Linkable<T> &other) const {
234 *ref_ = *(other.ref_);
235 return *this; // return const ref to self.
236 }
237
238 Linkable &operator=(const T &other) const {
239 *ref_ = other;
240 return *this;
241 }
242
244 void LinkToRaw(T *ptr) {
245 auto data = std::shared_ptr<T>(ptr, [](T *)
246 {
247 /* do nothing */
248 });
249 LinkTreeTo(data);
250 }
251
252 // Non const link reseters:
253
255 void Link(Linkable<T> &other) {
256 LinkTreeTo(other);
257 }
258
259 template <typename X>
260 struct false_template: std::false_type {
261 };
262
263 //Accept shared ownership.
264 void ShareLink(std::shared_ptr<T> other) {
265 if (!other) {
266 throw std::invalid_argument("\n Error in Linkable::Link: null pointer passed.\n");
267 }
268 auto data = other;
269 LinkTreeTo(data);
270 }
271
275 template <typename... Args,
276 std::enable_if_t<
277 !(std::conjunction_v<std::is_same<std::decay_t<Args>, Linkable<T>>...>),
278 int> = 0>
279 void ConstructAndLink(Args &&... args) {
280 auto data = std::make_shared<T>(std::forward<Args>(args)...);
281 LinkTreeTo(data);
282 }
283
284 explicit operator bool() const noexcept {
285 return static_cast<bool>(ref_);
286 }
287
288 T *get_mutable() const {
289 return ref_.get();
290 };
291
292 const T *get() const {
293 return ref_.get();
294 };
295
297 std::lock_guard<std::mutex> lock(s_registry_mutex);
298 black_hole.push_back(ref_);
299 }
300
301 // operator T() const {
302 // if (!ref_) {
303 // throw std::runtime_error("Attempt to convert empty Linkable to T");
304 // }
305 // return *ref_; // Return a copy of the contained object
306 // }
307
308 operator T *() const {
309 if (!ref_) {
310 throw std::runtime_error("Attempt to convert empty Linkable to T*");
311 }
312 return ref_.get(); // Return a copy of the contained object
313 }
314
315 private
316 :
317 // private methods used to update the links network.
318
319 // called on a linkable to recrusive update all back-links to its data.
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.");
325 }
326 backlink->updateTreeRefs(target_ref);
327 }
328 }
329
330 void LinkTreeTo(Linkable<T> &other) {
331 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
332 if (!ref_) {
333 // if ref is set, root is already linked to a source
334 if (!downlink_) {
335 // we are the root, update links, not just data ref:
336 downlink_ = &other;
337 other.backlinks_.emplace_back(this);
338 if (other.ref_) {
339 other.updateTreeRefs(other);
340 }
341 } else {
342 // recurse toward root to apply the link there:
343 downlink_->LinkTreeTo(other);
344 }
345 } else {
346 throw std::logic_error(
347 "Linkable::Link() called on object already linked to data.\n");
348 }
349 }
350
351 void LinkTreeTo(std::shared_ptr<T> &other) {
352 std::lock_guard<std::recursive_mutex> lock(s_link_mutex);
353 if (!other.get()) {
354 throw std::logic_error("Attempting to link a Linkable<T> to null data.\n");
355 }
356 if (!ref_) {
357 if (!downlink_) {
358 // we are the root, update OUR data:
359 ref_ = other;
360 rootlink_ = this;
361 if (rootlink_->ref_) {
362 rootlink_->updateTreeRefs(*rootlink_);
363 }
364 } else {
365 // recurse toward root to apply the link there:
366 if (std::find(downlink_->backlinks_.begin(), downlink_->backlinks_.end(), this)
367 == downlink_->backlinks_.end()) {
368 downlink_->backlinks_.emplace_back(this);
369 }
370 downlink_->LinkTreeTo(other);
371 }
372 } else {
373 throw std::logic_error(
374 "Linkable::Link() called on object already linked to data.\n");
375 }
376 }
377 };
378
379
380#endif //DEBUG
381} // DLG4
382
383
384#endif //LINKABLE_HPP
#define SET_LINK_TYPE
Definition Linkable.hh:62
void Link(Linkable< T > &other)
Reset Link to another Linakble:
Definition Linkable.hh:255
std::enable_if_t<!std::is_pointer_v< U >, U * > operator->() const
-> foward For non-pointer T
Definition Linkable.hh:206
void ShareLink(std::shared_ptr< T > other)
Definition Linkable.hh:264
void LinkToRaw(T *ptr)
Link to an existing T object via pointer (non-owning)
Definition Linkable.hh:244
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
Definition Linkable.hh:227
Linkable(Linkable< T > &other, std::true_type)
The EXPLICIT linking copy ctor.
Definition Linkable.hh:189
Linkable & operator=(const T &other) const
Definition Linkable.hh:238
const Linkable< T > & operator=(const Linkable< T > &other) const
Allow CONTENT update (not reference) through assignment This is allowed even for const wrapper.
Definition Linkable.hh:233
Linkable(const Linkable< T > &other)
Copy constructor does deep copy, not link This is safest and default.
Definition Linkable.hh:184
std::enable_if_t< std::is_pointer_v< U >, std::remove_pointer_t< U > * > operator->() const
operator-> forward For pointer T
Definition Linkable.hh:213
std::enable_if_t<!std::is_pointer_v< U >, U & > operator*() const
operator* for non-pointer T: returns reference to T
Definition Linkable.hh:220
Linkable(const T &other)
Copy constructor does deep copy, not link This is safest and default.
Definition Linkable.hh:196
void ConstructAndLink(Args &&... args)
Link and own a T and link to it.
Definition Linkable.hh:279
std::vector< std::shared_ptr< void > > black_hole
Definition Linkable.hh:70