Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/podio/utilities/MaybeSharedPtr.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 #ifndef PODIO_UTILITIES_MAYBESHAREDPTR_H
0002 #define PODIO_UTILITIES_MAYBESHAREDPTR_H
0003 
0004 #include <atomic>
0005 
0006 namespace podio::utils {
0007 
0008 namespace detail {
0009   /// Tag struct to create MaybeSharedPtr instances that initially own their
0010   /// managed pointer and hence will be created with a control block (ownership of
0011   /// the managed pointer may still change later!)
0012   struct MarkOwnedTag {};
0013 } // namespace detail
0014 
0015 constexpr static auto MarkOwned [[maybe_unused]] = detail::MarkOwnedTag{};
0016 
0017 /// "Semi-smart" pointer class for pointers that at some point during their
0018 /// lifetime might hand over management to another entity. E.g. Objects that
0019 /// are added to a collection will hand over the management of their Obj* to
0020 /// collection. In such a case two things need to be considered:
0021 /// - Other Objects with the same Obj* instance should not delete the managed
0022 ///   Obj*, even if the last Object goes out of scope
0023 /// - Even if the managed Obj* is gone (e.g. collection has gone out of scope or
0024 ///   was cleared), the remaining Object instances should still be able to
0025 ///   gracefully destruct, even if they are at this point merely an "empty husk"
0026 /// The MaybeSharedPtr achieves this by having an optional control block that
0027 /// controls the lifetime of itself and potentially the managed Obj*.
0028 template <typename T>
0029 class MaybeSharedPtr {
0030 public:
0031   /// There are no empty MaybeSharedPtrs
0032   MaybeSharedPtr() = delete;
0033 
0034   /// Constructor from raw pointer. Assumes someone else manages the pointer
0035   /// already
0036   explicit MaybeSharedPtr(T* p) : m_ptr(p) {
0037   }
0038 
0039   /// Constructor from a raw pointer assuming ownership in the process
0040   explicit MaybeSharedPtr(T* p, detail::MarkOwnedTag) : m_ptr(p), m_ctrlBlock(new ControlBlock()) {
0041   }
0042 
0043   /// Copy constructor
0044   MaybeSharedPtr(const MaybeSharedPtr& other) : m_ptr(other.m_ptr), m_ctrlBlock(other.m_ctrlBlock) {
0045     // Increase the reference count if there is a control block
0046     m_ctrlBlock && m_ctrlBlock->count++;
0047   }
0048 
0049   /// Assignment operator
0050   MaybeSharedPtr& operator=(MaybeSharedPtr other) {
0051     swap(*this, other);
0052     return *this;
0053   }
0054 
0055   /// Move constructor
0056   MaybeSharedPtr(MaybeSharedPtr&& other) : m_ptr(other.m_ptr), m_ctrlBlock(other.m_ctrlBlock) {
0057     other.m_ptr = nullptr;
0058     other.m_ctrlBlock = nullptr;
0059   }
0060 
0061   /// Destructor
0062   ~MaybeSharedPtr() {
0063     // Only if we have a control block, do we assume that we have any
0064     // responsibility in cleaning things up
0065     if (m_ctrlBlock && --m_ctrlBlock->count == 0) {
0066       // When the reference count reaches 0 we have to clean up control block in
0067       // any case, but first we have to find out whether we also need to clean
0068       // up the "managed" pointer
0069       if (m_ctrlBlock->owned) {
0070         delete m_ptr;
0071       }
0072       delete m_ctrlBlock;
0073     }
0074   }
0075 
0076   /// Get a raw pointer to the managed pointer. Do not change anything
0077   /// concerning the management of the pointer
0078   T* get() const {
0079     return m_ptr;
0080   }
0081 
0082   /// Get a raw pointer to the managed pointer and assume ownership.
0083   T* release() {
0084     if (m_ctrlBlock) {
0085       // From now on we only need to keep track of the control block
0086       m_ctrlBlock->owned = false;
0087     }
0088     return m_ptr;
0089   }
0090 
0091   operator bool() const {
0092     return m_ptr;
0093   }
0094 
0095   T* operator->() {
0096     return m_ptr;
0097   }
0098   const T* operator->() const {
0099     return m_ptr;
0100   }
0101 
0102   T& operator*() {
0103     return *m_ptr;
0104   }
0105   const T& operator*() const {
0106     return *m_ptr;
0107   }
0108 
0109   template <typename U>
0110   friend void swap(MaybeSharedPtr<U>& a, MaybeSharedPtr<U>& b);
0111 
0112   // avoid a bit of typing for having all the necessary combinations of
0113   // comparison operators
0114 #define DECLARE_COMPARISON_OPERATOR(op)                                                                                \
0115   template <typename U>                                                                                                \
0116   friend bool operator op(const MaybeSharedPtr<U>& lhs, const MaybeSharedPtr<U>& rhs);                                 \
0117   template <typename U>                                                                                                \
0118   friend bool operator op(const MaybeSharedPtr<U>& lhs, const U* rhs);                                                 \
0119   template <typename U>                                                                                                \
0120   friend bool operator op(const U* lhs, const MaybeSharedPtr<U>& rhs);
0121 
0122   DECLARE_COMPARISON_OPERATOR(==)
0123   DECLARE_COMPARISON_OPERATOR(!=)
0124   DECLARE_COMPARISON_OPERATOR(<)
0125 #undef DECLARE_COMPARISON_OPERATOR
0126 
0127 private:
0128   /// Simple control structure that controls the behavior of the
0129   /// MaybeSharedPtr destructor. Keeps track of how many references of the
0130   /// ControlBlock are currently still alive and whether the managed pointer
0131   /// should be destructed alongside the ControlBlock, once the reference count
0132   /// reaches 0.
0133   struct ControlBlock {
0134     std::atomic<unsigned> count{1}; ///< reference count
0135     std::atomic<bool> owned{true};  ///< ownership flag for the managed pointer. true == we manage the pointer
0136   };
0137 
0138   T* m_ptr{nullptr};
0139   ControlBlock* m_ctrlBlock{nullptr};
0140 };
0141 
0142 template <typename T>
0143 void swap(MaybeSharedPtr<T>& a, MaybeSharedPtr<T>& b) {
0144   using std::swap;
0145   swap(a.m_ptr, b.m_ptr);
0146   swap(a.m_ctrlBlock, b.m_ctrlBlock);
0147 }
0148 
0149 // helper macro for avoiding a bit of typing/repetition
0150 #define DEFINE_COMPARISON_OPERATOR(op)                                                                                 \
0151   template <typename U>                                                                                                \
0152   bool operator op(const MaybeSharedPtr<U>& lhs, const MaybeSharedPtr<U>& rhs) {                                       \
0153     return lhs.m_ptr op rhs.m_ptr;                                                                                     \
0154   }
0155 
0156 DEFINE_COMPARISON_OPERATOR(==)
0157 DEFINE_COMPARISON_OPERATOR(!=)
0158 DEFINE_COMPARISON_OPERATOR(<)
0159 #undef DEFINE_COMPARISON_OPERATOR
0160 
0161 } // namespace podio::utils
0162 
0163 #endif // PODIO_UTILITIES_MAYBESHAREDPTR_H