diff --git a/Control/AthContainers/AthContainers/DataVector.h b/Control/AthContainers/AthContainers/DataVector.h index 37e4ae0a045e8a372374d405281ada661ec12643..9bc8775a4384e2825b0e7c10f5bff94442adf561 100644 --- a/Control/AthContainers/AthContainers/DataVector.h +++ b/Control/AthContainers/AthContainers/DataVector.h @@ -837,6 +837,9 @@ public: typedef DataVector base_data_vector; + using Deleter = typename BASE::Deleter; + + //======================================================================== /** @name Constructors, destructors, assignment. */ //@{ @@ -1710,6 +1713,20 @@ public: SG::IndexTrackingPolicy trackIndices); + /** + * @brief Erase all the elements in the collection, and change + * how elements are to be deleted. + * @param deleter Object to be used to delete object. + * Passing nullptr will change back to the default. + * + * If the container owns its elements, then the removed elements + * will be deleted. Any duplicates will be removed in this process, + * but don't rely on this. + * After the current elements are deleted, the Deleter object is changed. + */ + void clear (std::unique_ptr<Deleter> deleter); + + /** * @brief Return the DV/DL info struct for this class. * @@ -2046,6 +2063,22 @@ public: typedef DataVector base_data_vector; + + /** + * @brief Interface to allow customizing how elements are to be deleted. + */ + class Deleter + { + public: + using value_type = DataVector::value_type; + using PtrVector = DataVector::PtrVector; + virtual ~Deleter() = default; + virtual void doDelete (value_type p) = 0; + virtual void doDelete (typename PtrVector::iterator first, + typename PtrVector::iterator last) = 0; + }; + + //======================================================================== /** @name Constructors, destructors, assignment. */ //@{ @@ -2920,6 +2953,20 @@ public: SG::IndexTrackingPolicy trackIndices); + /** + * @brief Erase all the elements in the collection, and change + * how elements are to be deleted. + * @param deleter Object to be used to delete object. + * Passing nullptr will change back to the default. + * + * If the container owns its elements, then the removed elements + * will be deleted. Any duplicates will be removed in this process, + * but don't rely on this. + * After the current elements are deleted, the Deleter object is changed. + */ + void clear (std::unique_ptr<Deleter> deleter); + + /** * @brief Return the DV/DL info struct for this class. * @@ -3146,6 +3193,22 @@ protected: typename PtrVector::iterator last); + /** + * @brief Delete an element + * @param p The element to delete. + */ + void doDelete (value_type p); + + + /** + * @brief Delete a range of elements + * @param first Start of range to delete. + * @param last End of range to delete. + */ + void doDelete (typename PtrVector::iterator first, + typename PtrVector::iterator last); + + protected: /// The ownership policy of this container --- /// either SG::OWNS_ELEMENTS or SG::VIEW_ELEMENTS. @@ -3154,6 +3217,12 @@ protected: /// This actually holds the elements. PtrVector m_pCont; + /// Interface telling us how to delete objects. + /// If null, just use the C++ default. + // This should really be a unique_ptr --- but that causes problems + // with ROOT persistency (even though this is tagged as transient). + Deleter* m_deleter = nullptr; + /** * @brief Clear @c m_isMostDerived for this instance and for all bases. @@ -3261,6 +3330,7 @@ public: #endif /// Declare the automatically created variable transient ROOT_SELECTION_NS::MemberAttributes< kTransient > m_isMostDerived; + ROOT_SELECTION_NS::MemberAttributes< kTransient > m_deleter; }; diff --git a/Control/AthContainers/AthContainers/DataVector.icc b/Control/AthContainers/AthContainers/DataVector.icc index 59db0982ae16e0781d50d213b2a8975c7385c78b..b5518a0624ad0644573cae4daa56949190a96d21 100644 --- a/Control/AthContainers/AthContainers/DataVector.icc +++ b/Control/AthContainers/AthContainers/DataVector.icc @@ -77,6 +77,7 @@ struct VirtBases<B1, DataModel_detail::NoBase, DataModel_detail::NoBase> typedef typename DataVector<B1>::size_type size_type; typedef typename DataVector<B1>::difference_type difference_type; typedef typename DataVector<B1>::allocator_type allocator_type; + typedef typename DataVector<B1>::Deleter Deleter; // We're using virtual derivation. static const bool has_virtual = true; @@ -154,6 +155,7 @@ struct VirtBases<B1, B2, DataModel_detail::NoBase> typedef typename DataVector<B1>::size_type size_type; typedef typename DataVector<B1>::difference_type difference_type; typedef typename DataVector<B1>::allocator_type allocator_type; + typedef typename DataVector<B1>::Deleter Deleter; // We're using virtual derivation. static const bool has_virtual = true; @@ -240,6 +242,7 @@ struct VirtBases typedef typename DataVector<B1>::size_type size_type; typedef typename DataVector<B1>::difference_type difference_type; typedef typename DataVector<B1>::allocator_type allocator_type; + typedef typename DataVector<B1>::Deleter Deleter; // We're using virtual derivation. static const bool has_virtual = true; @@ -409,6 +412,8 @@ DataVector<T, BASE>::DataVector (DataVector&& rhs) noexcept SG::AuxVectorBase::operator= (std::move (rhs)); this->m_ownPolicy = rhs.m_ownPolicy; this->m_pCont = std::move (rhs.m_pCont); + this->m_deleter = std::move(rhs.m_deleter); + rhs.m_deleter = nullptr; // Need to reset the container pointer on elements. this->setIndices (this->begin(), this->end()); @@ -551,6 +556,10 @@ DataVector<T, BASE>::operator= (DataVector<T, BASE>&& rhs) noexcept this->m_ownPolicy = rhs.m_ownPolicy; this->m_pCont = std::move (rhs.m_pCont); + delete this->m_deleter; + this->m_deleter = std::move(rhs.m_deleter); + rhs.m_deleter = nullptr; + // Need to reset the container pointer on elements. this->setIndices (this->begin(), this->end()); } @@ -1369,7 +1378,7 @@ void DataVector<T, BASE>::pop_back() { if (!this->m_pCont.empty()) { if (this->m_ownPolicy == SG::OWN_ELEMENTS) - delete this->m_pCont.back(); + this->doDelete (this->m_pCont.back()); else this->clearIndex (iterator (this->m_pCont.end() - 1, this)); this->m_pCont.pop_back(); @@ -1420,6 +1429,7 @@ void DataVector<T, BASE>::swap(DataVector& rhs) std::swap(this->m_ownPolicy, rhs.m_ownPolicy); SG::AuxVectorBase::swap (rhs); this->m_pCont.swap(rhs.m_pCont); + std::swap (this->m_deleter, rhs.m_deleter); this->setIndices (this->begin(), this->end()); rhs.setIndices (rhs.begin(), rhs.end()); } @@ -1670,6 +1680,26 @@ const DataModel_detail::DVLInfoBase& DataVector<T, BASE>::dvlinfo() } +/** + * @brief Erase all the elements in the collection, and change + * how elements are to be deleted. + * @param deleter Object to be used to delete object. + * Passing nullptr will change back to the default. + * + * If the container owns its elements, then the removed elements + * will be deleted. Any duplicates will be removed in this process, + * but don't rely on this. + * After the current elements are deleted, the Deleter object is changed. + */ +template <class T, class BASE> +void DataVector<T, BASE>::clear (std::unique_ptr<Deleter> deleter) +{ + this->clear(); + delete this->m_deleter; + this->m_deleter = deleter.release(); +} + + /** * @brief Return the DV/DL info struct for this class. * @@ -1815,7 +1845,7 @@ void DataVector<T, BASE>::assignElement (typename BaseContainer::iterator pos, { testInsert ("assignElement"); if (this->m_ownPolicy == SG::OWN_ELEMENTS) - delete *pos; + this->doDelete (*pos); else this->clearIndex (iterator (pos, this)); *pos = newElem; @@ -1841,7 +1871,7 @@ DataVector<T, BASE>::assignElement (typename BaseContainer::iterator pos, SG::throwExcNonowningContainer(); testInsert ("assignElement"); - delete *pos; + this->doDelete (*pos); value_type ptr = newElem.release(); *pos = ptr; this->moveAux (pos - this->m_pCont.begin(), ptr); @@ -1863,7 +1893,7 @@ DataVector<T, BASE>::assignBaseElement (typename BaseContainer::iterator pos, { testInsert ("assignBaseElement"); if (this->m_ownPolicy == SG::OWN_ELEMENTS) - delete *pos; + this->doDelete (*pos); else this->clearIndex (iterator (pos, this)); *pos = newElem; @@ -2058,6 +2088,9 @@ DATAVECTOR::DataVector(DataVector&& rhs) noexcept m_ownPolicy(rhs.m_ownPolicy), m_pCont(std::move (rhs.m_pCont)) { + m_deleter = std::move(rhs.m_deleter); + rhs.m_deleter = nullptr; + // Need to reset the container pointer on elements. this->setIndices (this->begin(), this->end()); @@ -2172,6 +2205,10 @@ DATAVECTOR& DATAVECTOR::operator= (DATAVECTOR&& rhs) noexcept this->m_ownPolicy = rhs.m_ownPolicy; this->m_pCont = std::move (rhs.m_pCont); + delete this->m_deleter; + this->m_deleter = std::move(rhs.m_deleter); + rhs.m_deleter = nullptr; + // Need to reset the container pointer on elements. this->setIndices (this->begin(), this->end()); } @@ -2250,10 +2287,9 @@ DATAVECTOR::~DataVector() if (m_ownPolicy == SG::OWN_ELEMENTS) { typename PtrVector::iterator new_end = DataVector_detail::remove_duplicates(m_pCont.begin(), m_pCont.end()); - typename PtrVector::iterator iter = m_pCont.begin(); - while (iter != new_end) - delete *(iter++); - } + this->doDelete (m_pCont.begin(), new_end); + } + delete m_deleter; } @@ -3063,7 +3099,7 @@ void DATAVECTOR::pop_back() { if (!m_pCont.empty()) { if (m_ownPolicy == SG::OWN_ELEMENTS) - delete m_pCont.back(); + this->doDelete (m_pCont.back()); else this->clearIndex (m_pCont.end() - 1); m_pCont.pop_back(); @@ -3114,6 +3150,7 @@ void DATAVECTOR::swap(DataVector& rhs) std::swap(m_ownPolicy, rhs.m_ownPolicy); SG::AuxVectorBase::swap (rhs); m_pCont.swap(rhs.m_pCont); + std::swap (this->m_deleter, rhs.m_deleter); this->setIndices (this->begin(), this->end()); rhs.setIndices (rhs.begin(), rhs.end()); } @@ -3388,6 +3425,27 @@ const DataModel_detail::DVLInfoBase& DATAVECTOR::dvlinfo() } +/** + * @brief Erase all the elements in the collection, and change + * how elements are to be deleted. + * @param deleter Object to be used to delete object. + * (The DataVector does not take ownership.) + * Passing nullptr will change back to the default. + * + * If the container owns its elements, then the removed elements + * will be deleted. Any duplicates will be removed in this process, + * but don't rely on this. + * After the current elements are deleted, the Deleter object is changed. + */ +template <class T> +void DATAVECTOR::clear (std::unique_ptr<Deleter> deleter) +{ + this->clear(); + delete this->m_deleter; + this->m_deleter = deleter.release(); +} + + /** * @brief Return the DV/DL info struct for this class. * @@ -3492,7 +3550,7 @@ void DATAVECTOR::assignElement (typename BaseContainer::iterator pos, { testInsert ("assignElement"); if (this->m_ownPolicy == SG::OWN_ELEMENTS) - delete *pos; + this->doDelete (*pos); else this->clearIndex (iterator (pos, this)); *pos = newElem; @@ -3518,7 +3576,7 @@ DATAVECTOR::assignElement (typename BaseContainer::iterator pos, SG::throwExcNonowningContainer(); testInsert ("assignElement"); - delete *pos; + this->doDelete (*pos); value_type ptr = newElem.release(); *pos = ptr; this->moveAux (pos - this->m_pCont.begin(), ptr); @@ -3539,7 +3597,7 @@ void DATAVECTOR::assignBaseElement (typename BaseContainer::iterator pos, { testInsert ("assignBaseElement"); if (this->m_ownPolicy == SG::OWN_ELEMENTS) - delete *pos; + this->doDelete (*pos); else this->clearIndex (iterator (pos, this)); *pos = newElem; @@ -3624,7 +3682,7 @@ typename DATAVECTOR::PtrVector::iterator DATAVECTOR::erase_base(typename PtrVector::iterator position) { if (m_ownPolicy == SG::OWN_ELEMENTS && position != m_pCont.end()) - delete *position; + this->doDelete (*position); return m_pCont.erase(position); } @@ -3649,13 +3707,50 @@ DATAVECTOR::erase_base(typename PtrVector::iterator first, if (m_ownPolicy == SG::OWN_ELEMENTS) { typename PtrVector::iterator new_end = DataVector_detail::remove_duplicates(first, last); - typename PtrVector::iterator iter = first; - while (iter != new_end) delete *(iter++); + this->doDelete (first, new_end); } return m_pCont.erase(first, last); } +/** + * @brief Delete an element + * @param p The element to delete. + */ +template <class T> +inline +void DATAVECTOR::doDelete (value_type p) +{ + if (m_deleter) { + m_deleter->doDelete (p); + } + else { + delete p; + } +} + + +/** + * @brief Delete a range of elements + * @param first Start of range to delete. + * @param last End of range to delete. + */ +template <class T> +inline +void DATAVECTOR::doDelete (typename PtrVector::iterator first, + typename PtrVector::iterator last) +{ + if (m_deleter) { + m_deleter->doDelete (first, last); + } + else { + for (; first != last; ++first) { + delete *first; + } + } +} + + /** * @brief Test if we can insert; raise an exception if not. * @param op Description of the attempted operation. diff --git a/Control/AthContainers/CMakeLists.txt b/Control/AthContainers/CMakeLists.txt index 20179e3c0a41bc056f93bdb54397898a121aa33f..503a7a5e0e0d92d5467058b34d1e4d6091a079cc 100644 --- a/Control/AthContainers/CMakeLists.txt +++ b/Control/AthContainers/CMakeLists.txt @@ -7,7 +7,7 @@ atlas_subdir( AthContainers ) # Extra dependencies, based on the environment we are in: if( NOT XAOD_STANDALONE ) - set( extra_libs AthenaKernel SGTools GaudiKernel ) + set( extra_libs AthenaKernel AthAllocators SGTools GaudiKernel ) set( extra_sources src/*.cxx ) endif() diff --git a/Control/AthContainers/test/DataVector_test.icc b/Control/AthContainers/test/DataVector_test.icc index 1c6b223e41367874ae3cc240bb915f5bd3f5fa78..17acce3150d85b6df9dc5253dde4229ba4eb04fe 100644 --- a/Control/AthContainers/test/DataVector_test.icc +++ b/Control/AthContainers/test/DataVector_test.icc @@ -6022,6 +6022,93 @@ void test2_elconversions() } +template <class DV> +class TestDeleter + : public DV::Deleter +{ +public: + using value_type = typename DV::Deleter::value_type; + using PtrVector = typename DV::Deleter::PtrVector; + + TestDeleter (std::vector<int>& the_v) : v(the_v) {} + + virtual void doDelete (value_type p) override + { + v.push_back (p->x); + delete p; + } + virtual void doDelete (typename PtrVector::iterator first, + typename PtrVector::iterator last) override + { + for (; first != last; ++first) { + v.push_back ((*first)->x); + delete *first; + } + } + + std::vector<int>& v; +}; +template <class T> +void test2_deleter1() +{ + std::vector<int> v; + DataVector<T> dv; + dv.clear (std::make_unique<TestDeleter<DataVector<T> > > (v)); + dv.push_back (new T(1)); + dv.push_back (new T(2)); + dv.push_back (new T(3)); + assert (v.empty()); + dv.pop_back(); + assert (v == std::vector<int>{3}); + v.clear(); + dv.clear(); + assert (v == (std::vector<int>{1, 2})); +} + + +template <class T> +void test2_deleter2() +{ + std::vector<int> v; + DataVector<T> dv1; + dv1.clear (std::make_unique<TestDeleter<DataVector<T> > > (v)); + dv1.push_back (new T(1)); + dv1.push_back (new T(2)); + dv1.push_back (new T(3)); + + DataVector<T> dv2 (std::move (dv1)); + assert (v.empty()); + assert (dv1.empty()); + assert (dv2.size() == 3); + dv2.pop_back(); + assert (v == std::vector<int>{3}); + v.clear(); + + dv1 = std::move (dv2); + assert (v.empty()); + assert (dv1.size() == 2); + assert (dv2.empty()); + dv1.pop_back(); + assert (v == std::vector<int>{2}); + v.clear(); + + dv1.swap (dv2); + assert (v.empty()); + assert (dv1.empty()); + assert (dv2.size() == 1); + dv2.pop_back(); + assert (v == std::vector<int>{1}); +} + + +template <class B, class D> +void test2_deleter() +{ + test2_deleter1<B>(); + test2_deleter1<D>(); +} + + template <class B, class D> void do_test2() { @@ -6084,6 +6171,7 @@ void do_test2() test2_move<B,D> (); test2_offset<B,D> (); test2_elconversions<B,D>(); + test2_deleter<B,D>(); }