Loading

Paste #pwj2ixurx

  1. #ifndef SAFE_REF_H
  2. #define SAFE_REF_H
  3.  
  4. #include <cassert>
  5. #include <vector>
  6.  
  7. template<typename T> class SafeReference;
  8.  
  9. /**
  10.  * Registration object of references to some object that might disappear at any time.
  11.  * @tparam T Type of the object that may disappear.
  12.  * @note It's useful to connect lifetime of this class with the object being protected,
  13.  *      for example by embedding the registration into the object to protect.
  14.  */
  15. template<typename T>
  16. class ReferencedObject {
  17.     friend class SafeReference<T>;
  18. public:
  19.     ReferencedObject() = delete;
  20.     ReferencedObject(ReferencedObject &) = delete;
  21.     ReferencedObject(ReferencedObject &&) = delete;
  22.  
  23.     ReferencedObject(T &object) : object(object) { }
  24.  
  25.     ~ReferencedObject()
  26.     {
  27.         unsigned int index = 0;
  28.         for (SafeReference<T> *sref : this->references) {
  29.             sref->Disconnect(index);
  30.             index++;
  31.         }
  32.     }
  33.  
  34.     /**
  35.      * Construct a new reference object to access the protected object.
  36.      * @return New reference object.
  37.      */
  38.     SafeReference<T> MakeReference()
  39.     {
  40.         return SafeReference<T>(this);
  41.     }
  42.  
  43. #ifdef DBG_SAFE_REF
  44.     /**
  45.      * For debugging, dump the reference objects that currently exist for this referenced object.
  46.      * @param indent If specified, the indentation text to prepend to each line.
  47.      */
  48.     void DumpReferences(const char *indent = "")
  49.     {
  50.         printf("%sReferencedObject points at %p (%lu references)\n", indent, &this->object, this->references.size());
  51.         for (SafeReference<T> *ref : this->references) {
  52.             printf("%s- Index %2u: Reference at %p\n", indent, ref->ref_index, ref);
  53.         }
  54.         printf("\n");
  55.     }
  56. #endif
  57.  
  58. private:
  59.     /**
  60.      * Register a new reference pointing to this object.
  61.      * @param reference New reference to register.
  62.      * @return Index of the reference.
  63.      */
  64.     unsigned int Register(SafeReference<T> *reference)
  65.     {
  66.         assert(this->references.size() < UINT_MAX);
  67.         unsigned int new_index = this->references.size();
  68.         this->references.push_back(reference);
  69.         return new_index;
  70.     }
  71.  
  72.     /**
  73.      * Change the registrered reference at a given index.
  74.      * @param ref_index Index to change.
  75.      * @param old_ref Old reference at the given index.
  76.      * @param new_ref New reference to use instead.
  77.      */
  78.     void ChangeRegistered(unsigned int ref_index, SafeReference<T> *old_ref, SafeReference<T> *new_ref)
  79.     {
  80.         assert(ref_index < this->references.size());
  81.         assert(this->references[ref_index] == old_ref);
  82.         this->references[ref_index] = new_ref;
  83.     }
  84.  
  85.     /**
  86.      * Drop a reference to the object.
  87.      * @param ref_index Index of the reference to remove.
  88.      * @param reference Address of the reference, for verification.
  89.      */
  90.     void Unregister(unsigned int ref_index, SafeReference<T> *reference)
  91.     {
  92.         size_t count = this->references.size();
  93.         assert(ref_index < count);
  94.         assert(this->references[ref_index] == reference);
  95.  
  96.         SafeReference<T> *last_ref = this->references.back();
  97.         if (reference != last_ref) { // Reference is not the last element.
  98.             // Move last_ref into 'ref_index' to fill the hole.
  99.             last_ref->MoveIndex(count - 1, ref_index);
  100.             this->references[ref_index] = last_ref;
  101.         }
  102.         this->references.pop_back();
  103.     }
  104.  
  105.     std::vector<SafeReference<T> *> references; ///< References to the object. Vector doesn't
  106.                                                 ///< have holes, last entry gets moved as needed.
  107.     T& object;                                  ///< The object pointed at.
  108. };
  109.  
  110. /**
  111.  * Reference object to refer to another object safely.
  112.  * @tparam T Type of the referenced object.
  113.  */
  114. template<typename T>
  115. class SafeReference {
  116.     friend class ReferencedObject<T>;
  117. public:
  118.     SafeReference() : ref_object(nullptr)
  119.     {
  120.     }
  121.  
  122.     SafeReference(ReferencedObject<T> *ref_object) : ref_object(ref_object)
  123.     {
  124.         assert(this->ref_object != nullptr);
  125.         this->ref_index = this->ref_object->Register(this);
  126.     }
  127.  
  128.     SafeReference(SafeReference<T> &sref)
  129.     {
  130.         if (sref.IsValid()) {
  131.             this->ref_object = sref.ref_object;
  132.             this->ref_index = this->ref_object->Register(this);
  133.         } else {
  134.             this->ref_object = nullptr;
  135.         }
  136.     }
  137.  
  138.     SafeReference(SafeReference<T> &&sref)
  139.     {
  140.         if (sref.IsValid()) {
  141.             this->ref_object = sref.ref_object;
  142.             this->ref_index = sref.ref_index;
  143.             this->ref_object->ChangeRegistered(this->ref_index, &sref, this);
  144.             sref.ref_object = nullptr;
  145.         } else {
  146.             this->ref_object = nullptr;
  147.         }
  148.     }
  149.  
  150.     SafeReference &operator=(SafeReference<T> &sref)
  151.     {
  152.         if (this == &sref) return *this;
  153.  
  154.         if (this->ref_object != nullptr) this->ref_object->Unregister(this->ref_index, this);
  155.  
  156.         if (sref.IsValid()) {
  157.             this->ref_object = sref.ref_object;
  158.             this->ref_index = this->ref_object->Register(this);
  159.         } else {
  160.             this->ref_object = nullptr;
  161.         }
  162.         return *this;
  163.     }
  164.  
  165.     SafeReference &operator=(SafeReference<T> &&sref)
  166.     {
  167.         if (this == &sref) return *this;
  168.  
  169.         if (this->ref_object != nullptr) this->ref_object->Unregister(this->ref_index, this);
  170.  
  171.         if (sref.IsValid()) {
  172.             this->ref_object = sref.ref_object;
  173.             this->ref_index = sref.ref_index;
  174.             this->ref_object->ChangeRegistered(this->ref_index, &sref, this);
  175.             sref.ref_object = nullptr;
  176.         } else {
  177.             this->ref_object = nullptr;
  178.         }
  179.         return *this;
  180.     }
  181.  
  182.     ~SafeReference()
  183.     {
  184.         if (this->ref_object != nullptr) this->ref_object->Unregister(this->ref_index, this);
  185.     }
  186.  
  187.     /**
  188.      * Return whether the referenced object is still available.
  189.      * @return Whether the referenced object still exists.
  190.      */
  191.     bool IsValid() const
  192.     {
  193.         return this->ref_object != nullptr;
  194.     }
  195.  
  196.     /**
  197.      * Get a pointer to the referenced object.
  198.      * @return Pointer to the referenced object, or \c nullptr if the referenced object does not
  199.      *      exist any more.
  200.      * @note The pointer is not protected, and may become dangling. To avoid that, don't keep the
  201.      *      bare pointer around, instead, use #IsValid and #GetObject for every access.
  202.      */
  203.     T* GetObject()
  204.     {
  205.         return (this->ref_object == nullptr) ? nullptr : &this->ref_object->object;
  206.     }
  207.  
  208. private:
  209.     /**
  210.      * Move the index of this reference from \a old_index to \a new_index.
  211.      * @param old_index Index of the reference object that it currently uses.
  212.      * @param new_index New index to use instead.
  213.      */
  214.     void MoveIndex(unsigned int old_index, unsigned int new_index)
  215.     {
  216.         assert(ref_object != nullptr);
  217.         assert(old_index == this->ref_index);
  218.         this->ref_index = new_index;
  219.     }
  220.  
  221.     /**
  222.      * The referenced object disappeared, do not access the referenced object any more.
  223.      * @param old_index Index of the reference object that it currently uses.
  224.      */
  225.     void Disconnect(unsigned int old_index)
  226.     {
  227.         assert(ref_object != nullptr);
  228.         assert(old_index == this->ref_index);
  229.         this->ref_object = nullptr;
  230.     }
  231.  
  232.     ReferencedObject<T> *ref_object; ///< Referenced object pointing to, if not \c nullptr.
  233.     unsigned int ref_index;          ///< Index number of this reference in the referenced object vector,
  234. };
  235.  
  236. #endif

Comments