deal.II version GIT relicensing-6816-g8d70a4508a 2026-09-28 16:30:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
observer_pointer.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 1998 - 2025 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13#ifndef dealii_observer_pointer_h
14#define dealii_observer_pointer_h
15
16
17#include <deal.II/base/config.h>
18
21
22#include <atomic>
23#include <string>
24#include <typeinfo>
25
27
103template <typename T, typename P = void>
105{
106public:
112
117 template <class Q>
119
125
130
140 ObserverPointer(T *t, const std::string &id);
141
149
154
162 operator=(T *tt);
163
168 template <class Q>
171
178
184
196 void
198
202 operator T *() const;
203
208 T &
209 operator*() const;
210
215 T *
216 get() const;
217
222 T *
223 operator->() const;
224
234 template <class Q>
235 void
237
246 void
247 swap(T *&ptr);
248
254 std::size_t
256
257private:
262
266 const std::string id;
267
272 std::atomic<bool> pointed_to_object_is_alive;
273};
274
275
276/* --------------------- inline Template functions ------------------------- */
277
278
279template <typename T, typename P>
281 : pointer(nullptr)
282 , id(typeid(P).name())
283 , pointed_to_object_is_alive(false)
284{
285 static_assert(std::is_base_of_v<EnableObserverPointer, T>,
286 "This class can only be used if the first template argument "
287 "is a class derived from 'EnableObserverPointer'.");
288}
289
290
291
292template <typename T, typename P>
294 : pointer(t)
295 , id(typeid(P).name())
296 , pointed_to_object_is_alive(false)
297{
298 static_assert(std::is_base_of_v<EnableObserverPointer, T>,
299 "This class can only be used if the first template argument "
300 "is a class derived from 'EnableObserverPointer'.");
301
302 if (t != nullptr)
303 t->subscribe(&pointed_to_object_is_alive, id);
304}
305
306
307
308template <typename T, typename P>
309inline ObserverPointer<T, P>::ObserverPointer(T *t, const std::string &id)
310 : pointer(t)
311 , id(id)
312 , pointed_to_object_is_alive(false)
313{
314 static_assert(std::is_base_of_v<EnableObserverPointer, T>,
315 "This class can only be used if the first template argument "
316 "is a class derived from 'EnableObserverPointer'.");
317
318 if (pointer != nullptr)
319 pointer->subscribe(&pointed_to_object_is_alive, id);
320}
321
322
323
324template <typename T, typename P>
325template <class Q>
327 const ObserverPointer<T, Q> &other)
328 : pointer(other.pointer)
329 , id(other.id)
330 , pointed_to_object_is_alive(false)
331{
332 static_assert(std::is_base_of_v<EnableObserverPointer, T>,
333 "This class can only be used if the first template argument "
334 "is a class derived from 'EnableObserverPointer'.");
335
336 if (other != nullptr)
337 {
339 ExcMessage("You can't copy a smart pointer object that "
340 "is pointing to an object that is no longer alive."));
341 pointer->subscribe(&pointed_to_object_is_alive, id);
342 }
343}
344
345
346
347template <typename T, typename P>
349 const ObserverPointer<T, P> &other)
350 : pointer(other.pointer)
351 , id(other.id)
352 , pointed_to_object_is_alive(false)
353{
354 static_assert(std::is_base_of_v<EnableObserverPointer, T>,
355 "This class can only be used if the first template argument "
356 "is a class derived from 'EnableObserverPointer'.");
357
358 if (other != nullptr)
359 {
361 ExcMessage("You can't copy a smart pointer object that "
362 "is pointing to an object that is no longer alive."));
363 pointer->subscribe(&pointed_to_object_is_alive, id);
364 }
365}
366
367
368
369template <typename T, typename P>
371 ObserverPointer<T, P> &&other) noexcept
372 : pointer(other.pointer)
373 , id(other.id)
374 , pointed_to_object_is_alive(false)
375{
376 static_assert(std::is_base_of_v<EnableObserverPointer, T>,
377 "This class can only be used if the first template argument "
378 "is a class derived from 'EnableObserverPointer'.");
379
380 if (other != nullptr)
381 {
382 Assert(other.pointed_to_object_is_alive,
383 ExcMessage("You can't move a smart pointer object that "
384 "is pointing to an object that is no longer alive."));
385
386 try
387 {
388 pointer->subscribe(&pointed_to_object_is_alive, id);
389 }
390 catch (...)
391 {
392 Assert(false,
394 "Calling subscribe() failed with an exception, but we "
395 "are in a function that cannot throw exceptions. "
396 "Aborting the program here."));
397 }
398
399 // Release the rhs object as if we had moved all members away from
400 // it directly:
401 other = nullptr;
402 }
403}
404
405
406
407template <typename T, typename P>
409{
410 static_assert(std::is_base_of_v<EnableObserverPointer, T>,
411 "This class can only be used if the first template argument "
412 "is a class derived from 'EnableObserverPointer'.");
413
414 if (pointed_to_object_is_alive && pointer != nullptr)
415 pointer->unsubscribe(&pointed_to_object_is_alive, id);
416}
417
418
419
420template <typename T, typename P>
421inline void
423{
424 if (pointed_to_object_is_alive && pointer != nullptr)
425 {
426 pointer->unsubscribe(&pointed_to_object_is_alive, id);
427 delete pointer;
428 Assert(pointed_to_object_is_alive == false, ExcInternalError());
429 }
430 pointer = nullptr;
431}
432
433
434
435template <typename T, typename P>
438{
439 // optimize if no real action is requested
440 if (pointer == tt)
441 return *this;
442
443 // Let us unsubscribe from the current object
444 if (pointed_to_object_is_alive && pointer != nullptr)
445 pointer->unsubscribe(&pointed_to_object_is_alive, id);
446
447 // Then reset to the new object, and subscribe to it
448 pointer = tt;
449 if (tt != nullptr)
450 pointer->subscribe(&pointed_to_object_is_alive, id);
451
452 return *this;
453}
454
455
456
457template <typename T, typename P>
458template <class Q>
461{
462 // if objects on the left and right
463 // hand side of the operator= are
464 // the same, then this is a no-op
465 if (&other == this)
466 return *this;
467
468 // Let us unsubscribe from the current object
469 if (pointed_to_object_is_alive && pointer != nullptr)
470 pointer->unsubscribe(&pointed_to_object_is_alive, id);
471
472 // Then reset to the new object, and subscribe to it
473 pointer = (other != nullptr ? other.get() : nullptr);
474 if (other != nullptr)
475 {
477 ExcMessage("You can't copy a smart pointer object that "
478 "is pointing to an object that is no longer alive."));
479 pointer->subscribe(&pointed_to_object_is_alive, id);
480 }
481 return *this;
482}
483
484
485
486template <typename T, typename P>
489{
490 // if objects on the left and right
491 // hand side of the operator= are
492 // the same, then this is a no-op
493 if (&other == this)
494 return *this;
495
496 // Let us unsubscribe from the current object
497 if (pointed_to_object_is_alive && pointer != nullptr)
498 pointer->unsubscribe(&pointed_to_object_is_alive, id);
499
500 // Then reset to the new object, and subscribe to it
501 pointer = (other != nullptr ? other.get() : nullptr);
502 if (other != nullptr)
503 {
505 ExcMessage("You can't copy a smart pointer object that "
506 "is pointing to an object that is no longer alive."));
507 pointer->subscribe(&pointed_to_object_is_alive, id);
508 }
509 return *this;
510}
511
512
513
514template <typename T, typename P>
517{
518 if (other == nullptr)
519 {
520 *this = nullptr;
521 }
522 // if objects on the left and right hand side of the operator= are
523 // the same, then this is a no-op
524 else if (&other != this)
525 {
526 // Let us unsubscribe from the current object
527 if (pointer != nullptr && pointed_to_object_is_alive)
528 pointer->unsubscribe(&pointed_to_object_is_alive, id);
529
530 // Then reset to the new object, and subscribe to it:
531 Assert(other.pointed_to_object_is_alive,
532 ExcMessage("You can't move a smart pointer object that "
533 "is pointing to an object that is no longer alive."));
534 pointer = other.get();
535 try
536 {
537 pointer->subscribe(&pointed_to_object_is_alive, id);
538 }
539 catch (...)
540 {
541 Assert(false,
543 "Calling subscribe() failed with an exception, but we "
544 "are in a function that cannot throw exceptions. "
545 "Aborting the program here."));
546 }
547
548 // Finally release the rhs object since we moved its contents
549 other = nullptr;
550 }
551 return *this;
552}
553
554
555
556template <typename T, typename P>
558{
559 return pointer;
560}
561
562
563
564template <typename T, typename P>
565inline T &
567{
568 Assert(pointer != nullptr, ExcNotInitialized());
569 Assert(pointed_to_object_is_alive,
570 ExcMessage("The object pointed to is not valid anymore."));
571 return *pointer;
572}
573
574
575
576template <typename T, typename P>
577inline T *
579{
580 Assert(pointer != nullptr, ExcNotInitialized());
581 Assert(pointed_to_object_is_alive,
582 ExcMessage("The object pointed to is not valid anymore."));
583 return pointer;
584}
585
586
587
588template <typename T, typename P>
589inline T *
591{
592 return this->get();
593}
594
595
596
597template <typename T, typename P>
598template <class Q>
599inline void
601{
602 if constexpr (running_in_debug_mode())
603 {
605 *this = other;
606 other = aux;
607 }
608 else
609 {
610 std::swap(pointer, other.pointer);
611 }
612}
613
614
615
616template <typename T, typename P>
617inline void
619{
620 if (pointed_to_object_is_alive && pointer != nullptr)
621 pointer->unsubscribe(pointed_to_object_is_alive, id);
622
623 std::swap(pointer, ptr);
624
625 if (pointer != nullptr)
626 pointer->subscribe(pointed_to_object_is_alive, id);
627}
628
629
630
631template <typename T, typename P>
632inline std::size_t
637
638
639
640// The following function is not strictly necessary but is an optimization
641// for places where you call swap(p1,p2) with ObserverPointer objects p1, p2.
642// Unfortunately, MS Visual Studio (at least up to the 2013 edition) trips
643// over it when calling std::swap(v1,v2) where v1,v2 are std::vectors of
644// ObserverPointer objects: it can't determine whether it should call std::swap
645// or ::swap on the individual elements (see bug #184 on our Google Code
646// site. Consequently, just take this function out of the competition for this
647// compiler.
648#ifndef _MSC_VER
654template <typename T, typename P, class Q>
655inline void
657{
658 t1.swap(t2);
659}
660#endif
661
662
670template <typename T, typename P>
671inline void
673{
674 t1.swap(t2);
675}
676
677
678
686template <typename T, typename P>
687inline void
689{
690 t2.swap(t1);
691}
692
694
695#endif
T & operator*() const
void swap(T *&ptr)
std::size_t memory_consumption() const
T * operator->() const
void swap(ObserverPointer< T, Q > &tt)
std::atomic< bool > pointed_to_object_is_alive
ObserverPointer< T, P > & operator=(const ObserverPointer< T, P > &other)
ObserverPointer< T, P > & operator=(const ObserverPointer< T, Q > &other)
ObserverPointer(const ObserverPointer< T, P > &other)
const std::string id
ObserverPointer< T, P > & operator=(T *tt)
ObserverPointer< T, P > & operator=(ObserverPointer< T, P > &&other) noexcept
ObserverPointer(const ObserverPointer< T, Q > &other)
ObserverPointer(ObserverPointer< T, P > &&other) noexcept
ObserverPointer(T *t, const std::string &id)
#define DEAL_II_DEPRECATED
Definition config.h:294
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define Assert(cond, exc)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcNotInitialized()
static ::ExceptionBase & ExcMessage(std::string arg1)
void swap(ObserverPointer< T, P > &t1, ObserverPointer< T, Q > &t2)