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