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