diff options
-rw-r--r-- | indra/llcommon/CMakeLists.txt | 1 | ||||
-rw-r--r-- | indra/llcommon/llinstancetracker.h | 97 | ||||
-rw-r--r-- | indra/llcommon/llinstancetrackersubclass.h | 98 |
3 files changed, 160 insertions, 36 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 21998f0b78..0fe61108ff 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -189,6 +189,7 @@ set(llcommon_HEADER_FILES llinitdestroyclass.h llinitparam.h llinstancetracker.h + llinstancetrackersubclass.h llkeybind.h llkeythrottle.h llleap.h diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 02535a59e7..97f7817e74 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -104,22 +104,26 @@ public: return LockStatic()->mMap.size(); } - // snapshot of std::pair<const KEY, std::shared_ptr<T>> pairs - class snapshot + // snapshot of std::pair<const KEY, std::shared_ptr<SUBCLASS>> pairs, for + // some SUBCLASS derived from T + template <typename SUBCLASS> + class snapshot_of { // It's very important that what we store in this snapshot are // weak_ptrs, NOT shared_ptrs. That's how we discover whether any // instance has been deleted during the lifespan of a snapshot. typedef std::vector<std::pair<const KEY, weak_t>> VectorType; - // Dereferencing our iterator produces a std::shared_ptr for each - // instance that still exists. Since we store weak_ptrs, that involves - // two chained transformations: + // Dereferencing the iterator we publish produces a + // std::shared_ptr<SUBCLASS> for each instance that still exists. + // Since we store weak_ptr<T>, that involves two chained + // transformations: // - a transform_iterator to lock the weak_ptr and return a shared_ptr - // - a filter_iterator to skip any shared_ptr that has become invalid. + // - a filter_iterator to skip any shared_ptr<T> that has become + // invalid or references any T instance that isn't SUBCLASS. // It is very important that we filter lazily, that is, during // traversal. Any one of our stored weak_ptrs might expire during // traversal. - typedef std::pair<const KEY, ptr_t> strong_pair; + typedef std::pair<const KEY, std::shared_ptr<SUBCLASS>> strong_pair; // Note for future reference: nat has not yet had any luck (up to // Boost 1.67) trying to use boost::transform_iterator with a hand- // coded functor, only with actual functions. In my experience, an @@ -127,7 +131,7 @@ public: // result_type typedef. But this works. static strong_pair strengthen(typename VectorType::value_type& pair) { - return { pair.first, pair.second.lock() }; + return { pair.first, std::dynamic_pointer_cast<SUBCLASS>(pair.second.lock()) }; } static bool dead_skipper(const strong_pair& pair) { @@ -135,7 +139,7 @@ public: } public: - snapshot(): + snapshot_of(): // populate our vector with a snapshot of (locked!) InstanceMap // note, this assigns pair<KEY, shared_ptr> to pair<KEY, weak_ptr> mData(mLock->mMap.begin(), mLock->mMap.end()) @@ -184,44 +188,51 @@ public: #endif // LL_WINDOWS VectorType mData; }; + using snapshot = snapshot_of<T>; - // iterate over this for references to each instance - class instance_snapshot: public snapshot + // iterate over this for references to each SUBCLASS instance + template <typename SUBCLASS> + class instance_snapshot_of: public snapshot_of<SUBCLASS> { private: - static T& instance_getter(typename snapshot::iterator::reference pair) + using super = snapshot_of<SUBCLASS>; + static T& instance_getter(typename super::iterator::reference pair) { return *pair.second; } public: typedef boost::transform_iterator<decltype(instance_getter)*, - typename snapshot::iterator> iterator; - iterator begin() { return iterator(snapshot::begin(), instance_getter); } - iterator end() { return iterator(snapshot::end(), instance_getter); } + typename super::iterator> iterator; + iterator begin() { return iterator(super::begin(), instance_getter); } + iterator end() { return iterator(super::end(), instance_getter); } void deleteAll() { - for (auto it(snapshot::begin()), end(snapshot::end()); it != end; ++it) + for (auto it(super::begin()), end(super::end()); it != end; ++it) { delete it->second.get(); } } - }; + }; + using instance_snapshot = instance_snapshot_of<T>; // iterate over this for each key - class key_snapshot: public snapshot + template <typename SUBCLASS> + class key_snapshot_of: public snapshot_of<SUBCLASS> { private: - static KEY key_getter(typename snapshot::iterator::reference pair) + using super = snapshot_of<SUBCLASS>; + static KEY key_getter(typename super::iterator::reference pair) { return pair.first; } public: typedef boost::transform_iterator<decltype(key_getter)*, - typename snapshot::iterator> iterator; - iterator begin() { return iterator(snapshot::begin(), key_getter); } - iterator end() { return iterator(snapshot::end(), key_getter); } + typename super::iterator> iterator; + iterator begin() { return iterator(super::begin(), key_getter); } + iterator end() { return iterator(super::end(), key_getter); } }; + using key_snapshot = key_snapshot_of<T>; static ptr_t getInstance(const KEY& k) { @@ -368,22 +379,25 @@ public: return LockStatic()->mSet.size(); } - // snapshot of std::shared_ptr<T> pointers - class snapshot + // snapshot of std::shared_ptr<SUBCLASS> pointers + template <typename SUBCLASS> + class snapshot_of { // It's very important that what we store in this snapshot are // weak_ptrs, NOT shared_ptrs. That's how we discover whether any // instance has been deleted during the lifespan of a snapshot. typedef std::vector<weak_t> VectorType; - // Dereferencing our iterator produces a std::shared_ptr for each - // instance that still exists. Since we store weak_ptrs, that involves - // two chained transformations: + // Dereferencing the iterator we publish produces a + // std::shared_ptr<SUBCLASS> for each instance that still exists. + // Since we store weak_ptrs, that involves two chained + // transformations: // - a transform_iterator to lock the weak_ptr and return a shared_ptr - // - a filter_iterator to skip any shared_ptr that has become invalid. - typedef std::shared_ptr<T> strong_ptr; + // - a filter_iterator to skip any shared_ptr that has become invalid + // or references any T instance that isn't SUBCLASS. + typedef std::shared_ptr<SUBCLASS> strong_ptr; static strong_ptr strengthen(typename VectorType::value_type& ptr) { - return ptr.lock(); + return std::dynamic_pointer_cast<SUBCLASS>(ptr.lock()); } static bool dead_skipper(const strong_ptr& ptr) { @@ -391,7 +405,7 @@ public: } public: - snapshot(): + snapshot_of(): // populate our vector with a snapshot of (locked!) InstanceSet // note, this assigns stored shared_ptrs to weak_ptrs for snapshot mData(mLock->mSet.begin(), mLock->mSet.end()) @@ -437,22 +451,33 @@ public: #endif // LL_WINDOWS VectorType mData; }; + using snapshot = snapshot_of<T>; // iterate over this for references to each instance - struct instance_snapshot: public snapshot + template <typename SUBCLASS> + class instance_snapshot_of: public snapshot_of<SUBCLASS> { - typedef boost::indirect_iterator<typename snapshot::iterator> iterator; - iterator begin() { return iterator(snapshot::begin()); } - iterator end() { return iterator(snapshot::end()); } + private: + using super = snapshot_of<SUBCLASS>; + + public: + typedef boost::indirect_iterator<typename super::iterator> iterator; + iterator begin() { return iterator(super::begin()); } + iterator end() { return iterator(super::end()); } void deleteAll() { - for (auto it(snapshot::begin()), end(snapshot::end()); it != end; ++it) + for (auto it(super::begin()), end(super::end()); it != end; ++it) { delete it->get(); } } }; + using instance_snapshot = instance_snapshot_of<T>; + // key_snapshot_of isn't really meaningful, but define it anyway to avoid + // requiring two different LLInstanceTrackerSubclass implementations. + template <typename SUBCLASS> + using key_snapshot_of = instance_snapshot_of<SUBCLASS>; protected: LLInstanceTracker() diff --git a/indra/llcommon/llinstancetrackersubclass.h b/indra/llcommon/llinstancetrackersubclass.h new file mode 100644 index 0000000000..ea9a38200f --- /dev/null +++ b/indra/llcommon/llinstancetrackersubclass.h @@ -0,0 +1,98 @@ +/** + * @file llinstancetrackersubclass.h + * @author Nat Goodspeed + * @date 2022-12-09 + * @brief Intermediate class to get subclass-specific types from + * LLInstanceTracker instance-retrieval methods. + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Copyright (c) 2022, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLINSTANCETRACKERSUBCLASS_H) +#define LL_LLINSTANCETRACKERSUBCLASS_H + +#include <memory> // std::shared_ptr, std::weak_ptr + +/** + * Derive your subclass S of a subclass T of LLInstanceTracker<T> from + * LLInstanceTrackerSubclass<S, T> to perform appropriate downcasting and + * filtering for LLInstanceTracker access methods. + * + * LLInstanceTracker<T> uses CRTP, so that getWeak(), getInstance(), snapshot + * and instance_snapshot return pointers and references to T. The trouble is + * that subclasses T0 and T1 derived from T also get pointers and references + * to their base class T, requiring explicit downcasting. Moreover, + * T0::getInstance() shouldn't find an instance of any T subclass other than + * T0. Nor should T0::snapshot. + * + * @code + * class Tracked: public LLInstanceTracker<Tracked, std::string> + * { + * private: + * using super = LLInstanceTracker<Tracked, std::string>; + * public: + * Tracked(const std::string& name): super(name) {} + * // All references to Tracked::ptr_t, Tracked::getInstance() etc. + * // appropriately use Tracked. + * // ... + * }; + * + * // But now we derive SubTracked from Tracked. We need SubTracked::ptr_t, + * // SubTracked::getInstance() etc. to use SubTracked, not Tracked. + * // This LLInstanceTrackerSubclass specialization is itself derived from + * // Tracked. + * class SubTracked: public LLInstanceTrackerSubclass<SubTracked, Tracked> + * { + * private: + * using super = LLInstanceTrackerSubclass<SubTracked, Tracked>; + * public: + * // LLInstanceTrackerSubclass's constructor forwards to Tracked's. + * SubTracked(const std::string& name): super(name) {} + * // SubTracked::getInstance() returns std::shared_ptr<SubTracked>, etc. + * // ... + * @endcode + */ +template <typename SUBCLASS, typename T> +class LLInstanceTrackerSubclass: public T +{ +public: + using ptr_t = std::shared_ptr<SUBCLASS>; + using weak_t = std::weak_ptr<SUBCLASS>; + + // forward any constructor call to the corresponding T ctor + template <typename... ARGS> + LLInstanceTrackerSubclass(ARGS&&... args): + T(std::forward<ARGS>(args)...) + {} + + weak_t getWeak() + { + // call base-class getWeak(), try to lock, downcast to SUBCLASS + return std::dynamic_pointer_cast<SUBCLASS>(T::getWeak().lock()); + } + + template <typename KEY> + static ptr_t getInstance(const KEY& k) + { + return std::dynamic_pointer_cast<SUBCLASS>(T::getInstance(k)); + } + + using snapshot = typename T::template snapshot_of<SUBCLASS>; + using instance_snapshot = typename T::template instance_snapshot_of<SUBCLASS>; + using key_snapshot = typename T::template key_snapshot_of<SUBCLASS>; + + static size_t instanceCount() + { + // T::instanceCount() lies because our snapshot, et al., won't + // necessarily return all the T instances -- only those that are also + // SUBCLASS instances. Count those. + size_t count = 0; + for (const auto& pair : snapshot()) + ++count; + return count; + } +}; + +#endif /* ! defined(LL_LLINSTANCETRACKERSUBCLASS_H) */ |