Chris@1729: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@1729: Chris@1729: /* Chris@1729: Sonic Visualiser Chris@1729: An audio file viewer and annotation editor. Chris@1729: Centre for Digital Music, Queen Mary, University of London. Chris@1729: Chris@1729: This program is free software; you can redistribute it and/or Chris@1729: modify it under the terms of the GNU General Public License as Chris@1729: published by the Free Software Foundation; either version 2 of the Chris@1729: License, or (at your option) any later version. See the file Chris@1729: COPYING included with this distribution for more information. Chris@1729: */ Chris@1729: Chris@1729: #ifndef SV_BY_ID_H Chris@1729: #define SV_BY_ID_H Chris@1729: Chris@1734: #include "Debug.h" Chris@1734: Chris@1729: #include Chris@1729: #include Chris@1731: #include Chris@1731: #include Chris@1731: #include Chris@1729: Chris@1729: #include Chris@1731: #include Chris@1729: Chris@1738: #include "XmlExportable.h" Chris@1738: Chris@1731: template Chris@1731: struct SvId { Chris@1735: Chris@1731: int id; Chris@1729: Chris@1735: enum { Chris@1735: // The value NO_ID (-1) is never allocated by WithId Chris@1735: NO_ID = -1 Chris@1735: }; Chris@1735: Chris@1735: SvId() : id(NO_ID) {} Chris@1735: Chris@1735: SvId(const SvId &) =default; Chris@1735: SvId &operator=(const SvId &) =default; Chris@1735: Chris@1735: bool operator==(const SvId &other) const { return id == other.id; } Chris@1731: bool operator<(const SvId &other) const { return id < other.id; } Chris@1731: Chris@1735: bool isNone() const { return id == NO_ID; } Chris@1735: Chris@1731: QString toString() const { Chris@1731: return QString("%1").arg(id); Chris@1731: } Chris@1731: }; Chris@1731: Chris@1731: template Chris@1739: std::ostream & Chris@1739: operator<<(std::ostream &ostr, const SvId &id) Chris@1739: { Chris@1739: // For diagnostic purposes only. Do not use these IDs for Chris@1739: // serialisation - see XmlExportable instead. Chris@1739: if (id.isNone()) { Chris@1739: return (ostr << ""); Chris@1739: } else { Chris@1739: return (ostr << "#" << id.id); Chris@1739: } Chris@1739: } Chris@1739: Chris@1739: template Chris@1729: class WithId Chris@1729: { Chris@1729: public: Chris@1731: typedef SvId Id; Chris@1731: Chris@1729: WithId() : Chris@1729: m_id(getNextId()) { Chris@1729: } Chris@1729: Chris@1731: /** Chris@1731: * Return an id for this object. The id is a unique identifier for Chris@1731: * this object among all objects that implement WithId within this Chris@1731: * single run of the application. Chris@1731: */ Chris@1729: Id getId() const { Chris@1731: Id id; Chris@1731: id.id = m_id; Chris@1731: return id; Chris@1729: } Chris@1729: Chris@1729: private: Chris@1731: int m_id; Chris@1731: Chris@1731: static int getNextId() { Chris@1731: static int nextId = 0; Chris@1731: static QMutex mutex; Chris@1731: QMutexLocker locker(&mutex); Chris@1731: int i = nextId; Chris@1731: if (nextId == INT_MAX) { Chris@1731: nextId = INT_MIN; Chris@1735: } else { Chris@1735: ++nextId; Chris@1735: if (nextId == 0 || nextId == Id::NO_ID) { Chris@1735: throw std::runtime_error("Internal ID limit exceeded!"); Chris@1735: } Chris@1731: } Chris@1731: return i; Chris@1731: } Chris@1729: }; Chris@1729: Chris@1731: template Chris@1729: class ById Chris@1729: { Chris@1729: public: Chris@1731: ~ById() { Chris@1731: QMutexLocker locker(&m_mutex); Chris@1731: for (const auto &p: m_items) { Chris@1731: if (p.second && p.second.use_count() > 0) { Chris@1734: SVCERR << "WARNING: ById map destroyed with use count of " Chris@1734: << p.second.use_count() << " for item with type " Chris@1734: << typeid(*p.second.get()).name() Chris@1734: << " and id " << p.first.id << endl; Chris@1731: } Chris@1731: } Chris@1731: } Chris@1731: Chris@1729: void add(std::shared_ptr item) { Chris@1729: QMutexLocker locker(&m_mutex); Chris@1734: auto id = item->getId(); Chris@1739: if (id.isNone()) { Chris@1739: throw std::logic_error("item id should never be None"); Chris@1739: } Chris@1734: if (m_items.find(id) != m_items.end()) { Chris@1734: SVCERR << "WARNING: ById::add: item with id " << id Chris@1734: << " is already recorded, replacing it (item type is " Chris@1734: << typeid(*item.get()).name() << ")" << endl; Chris@1734: } Chris@1734: m_items[id] = item; Chris@1729: } Chris@1729: Chris@1729: void Chris@1729: release(Id id) { Chris@1729: QMutexLocker locker(&m_mutex); Chris@1729: m_items.erase(id); Chris@1729: } Chris@1729: Chris@1729: std::shared_ptr get(Id id) const { Chris@1739: if (id.isNone()) return {}; // this id is never issued: avoid locking Chris@1729: QMutexLocker locker(&m_mutex); Chris@1729: const auto &itr = m_items.find(id); Chris@1729: if (itr != m_items.end()) { Chris@1729: return itr->second; Chris@1729: } else { Chris@1739: return {}; Chris@1729: } Chris@1729: } Chris@1729: Chris@1729: template Chris@1729: std::shared_ptr getAs(Id id) const { Chris@1729: return std::dynamic_pointer_cast(get(id)); Chris@1729: } Chris@1738: Chris@1738: /** Chris@1738: * If the Item type is an XmlExportable, return the export ID of Chris@1739: * the given item ID. A call to this function will fail to compile Chris@1739: * if the Item is not an XmlExportable. Chris@1739: * Chris@1739: * The export ID is a simple int, and is only allocated when first Chris@1739: * requested, so objects that are never exported don't get one. Chris@1738: */ Chris@1738: int getExportId(Id id) const { Chris@1738: auto exportable = getAs(id); Chris@1738: if (exportable) { Chris@1738: return exportable->getExportId(); Chris@1738: } else { Chris@1738: return XmlExportable::NO_ID; Chris@1738: } Chris@1738: } Chris@1729: Chris@1729: private: Chris@1729: mutable QMutex m_mutex; Chris@1729: std::map> m_items; Chris@1729: }; Chris@1729: Chris@1731: template Chris@1731: class StaticById Chris@1729: { Chris@1729: public: Chris@1731: static void add(std::shared_ptr imagined) { Chris@1731: byId().add(imagined); Chris@1729: } Chris@1729: Chris@1729: static void release(Id id) { Chris@1731: byId().release(id); Chris@1729: } Chris@1729: Chris@1731: static std::shared_ptr get(Id id) { Chris@1731: return byId().get(id); Chris@1729: } Chris@1729: Chris@1729: template Chris@1729: static Chris@1729: std::shared_ptr getAs(Id id) { Chris@1731: return std::dynamic_pointer_cast(get(id)); Chris@1729: } Chris@1729: Chris@1738: static int getExportId(Id id) { Chris@1738: return byId().getExportId(id); Chris@1738: } Chris@1738: Chris@1729: private: Chris@1731: static Chris@1731: ById &byId() { Chris@1731: static ById b; Chris@1731: return b; Chris@1731: } Chris@1729: }; Chris@1731: Chris@1729: #endif Chris@1729: