Mercurial > hg > svcore
diff base/ById.h @ 1742:52705a328b34 by-id
Rejig ById so as to put everything in a single pool, so that at the core you can go from numeric id (untyped) to anything the object can be dynamic_cast to. Useful for building other abstractions like PlayParameter-type registrations that don't know about e.g. Models. Probably some more tweaking needed. Also add tests
author | Chris Cannam |
---|---|
date | Fri, 28 Jun 2019 17:36:30 +0100 |
parents | 565575463752 |
children | b92bdcd4954b |
line wrap: on
line diff
--- a/base/ById.h Thu Jun 27 13:08:10 2019 +0100 +++ b/base/ById.h Fri Jun 28 17:36:30 2019 +0100 @@ -18,145 +18,147 @@ #include "Debug.h" #include <memory> -#include <map> -#include <typeinfo> #include <iostream> #include <climits> +#include <stdexcept> #include <QMutex> #include <QString> #include "XmlExportable.h" +struct IdAlloc { + + // The value NO_ID (-1) is never allocated + static const int NO_ID = -1; + + static int getNextId(); +}; + template <typename T> -struct SvId { +struct TypedId { - int id; + int untyped; + + TypedId() : untyped(IdAlloc::NO_ID) {} - enum { - // The value NO_ID (-1) is never allocated by WithId - NO_ID = -1 - }; - - SvId() : id(NO_ID) {} + TypedId(const TypedId &) =default; + TypedId &operator=(const TypedId &) =default; - SvId(const SvId &) =default; - SvId &operator=(const SvId &) =default; - - bool operator==(const SvId &other) const { return id == other.id; } - bool operator<(const SvId &other) const { return id < other.id; } - - bool isNone() const { return id == NO_ID; } - - QString toString() const { - return QString("%1").arg(id); + bool operator==(const TypedId &other) const { + return untyped == other.untyped; + } + bool operator!=(const TypedId &other) const { + return untyped != other.untyped; + } + bool operator<(const TypedId &other) const { + return untyped < other.untyped; + } + bool isNone() const { + return untyped == IdAlloc::NO_ID; } }; template <typename T> std::ostream & -operator<<(std::ostream &ostr, const SvId<T> &id) +operator<<(std::ostream &ostr, const TypedId<T> &id) { // For diagnostic purposes only. Do not use these IDs for // serialisation - see XmlExportable instead. if (id.isNone()) { return (ostr << "<none>"); } else { - return (ostr << "#" << id.id); + return (ostr << "#" << id.untyped); } } -template <typename T> class WithId { public: - typedef SvId<T> Id; - WithId() : - m_id(getNextId()) { + m_id(IdAlloc::getNextId()) { + } + virtual ~WithId() { } /** - * Return an id for this object. The id is a unique identifier for + * Return an id for this object. The id is a unique number for * this object among all objects that implement WithId within this * single run of the application. */ + int getUntypedId() const { + return m_id; + } + +private: + int m_id; +}; + +template <typename T> +class WithTypedId : virtual public WithId +{ +public: + typedef TypedId<T> Id; + + WithTypedId() : WithId() { } + + /** + * Return an id for this object. The id is a unique value for this + * object among all objects that implement WithTypedId within this + * single run of the application. + */ Id getId() const { Id id; - id.id = m_id; + id.untyped = getUntypedId(); return id; } +}; + +class AnyById +{ +public: + static void add(int, std::shared_ptr<WithId>); + static void release(int); + static std::shared_ptr<WithId> get(int); + + template <typename Derived> + static std::shared_ptr<Derived> getAs(int id) { + std::shared_ptr<WithId> p = get(id); + return std::dynamic_pointer_cast<Derived>(p); + } private: - int m_id; - - static int getNextId() { - static int nextId = 0; - static QMutex mutex; - QMutexLocker locker(&mutex); - int i = nextId; - if (nextId == INT_MAX) { - nextId = INT_MIN; - } else { - ++nextId; - if (nextId == 0 || nextId == Id::NO_ID) { - throw std::runtime_error("Internal ID limit exceeded!"); - } - } - return i; - } + class Impl; + static Impl &impl(); }; template <typename Item, typename Id> -class ById +class TypedById { public: - ~ById() { - QMutexLocker locker(&m_mutex); - for (const auto &p: m_items) { - if (p.second && p.second.use_count() > 0) { - SVCERR << "WARNING: ById map destroyed with use count of " - << p.second.use_count() << " for item with type " - << typeid(*p.second.get()).name() - << " and id " << p.first.id << endl; - } - } - } - - void add(std::shared_ptr<Item> item) { - QMutexLocker locker(&m_mutex); + static void add(std::shared_ptr<Item> item) { auto id = item->getId(); if (id.isNone()) { throw std::logic_error("item id should never be None"); } - if (m_items.find(id) != m_items.end()) { - SVCERR << "WARNING: ById::add: item with id " << id - << " is already recorded, replacing it (item type is " - << typeid(*item.get()).name() << ")" << endl; - } - m_items[id] = item; + AnyById::add(id.untyped, item); } - void - release(Id id) { - QMutexLocker locker(&m_mutex); - m_items.erase(id); + static void release(Id id) { + AnyById::release(id.untyped); } - - std::shared_ptr<Item> get(Id id) const { - if (id.isNone()) return {}; // this id is never issued: avoid locking - QMutexLocker locker(&m_mutex); - const auto &itr = m_items.find(id); - if (itr != m_items.end()) { - return itr->second; - } else { - return {}; - } + static void release(std::shared_ptr<Item> item) { + release(item->getId()); } template <typename Derived> - std::shared_ptr<Derived> getAs(Id id) const { - return std::dynamic_pointer_cast<Derived>(get(id)); + static std::shared_ptr<Derived> getAs(Id id) { + if (id.isNone()) return {}; // this id is never issued: avoid locking + return AnyById::getAs<Derived>(id.untyped); + } + + static std::shared_ptr<Item> get(Id id) { + return getAs<Item>(id); } /** @@ -167,7 +169,7 @@ * The export ID is a simple int, and is only allocated when first * requested, so objects that are never exported don't get one. */ - int getExportId(Id id) const { + static int getExportId(Id id) { auto exportable = getAs<XmlExportable>(id); if (exportable) { return exportable->getExportId(); @@ -175,44 +177,6 @@ return XmlExportable::NO_ID; } } - -private: - mutable QMutex m_mutex; - std::map<Id, std::shared_ptr<Item>> m_items; -}; - -template <typename Item, typename Id> -class StaticById -{ -public: - static void add(std::shared_ptr<Item> imagined) { - byId().add(imagined); - } - - static void release(Id id) { - byId().release(id); - } - - static std::shared_ptr<Item> get(Id id) { - return byId().get(id); - } - - template <typename Derived> - static - std::shared_ptr<Derived> getAs(Id id) { - return std::dynamic_pointer_cast<Derived>(get(id)); - } - - static int getExportId(Id id) { - return byId().getExportId(id); - } - -private: - static - ById<Item, Id> &byId() { - static ById<Item, Id> b; - return b; - } }; #endif