annotate base/ById.h @ 1739:565575463752 by-id

Some work on models and transformers
author Chris Cannam
date Wed, 26 Jun 2019 14:59:09 +0100
parents 4abc0f08adf9
children 52705a328b34
rev   line source
Chris@1729 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1729 2
Chris@1729 3 /*
Chris@1729 4 Sonic Visualiser
Chris@1729 5 An audio file viewer and annotation editor.
Chris@1729 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1729 7
Chris@1729 8 This program is free software; you can redistribute it and/or
Chris@1729 9 modify it under the terms of the GNU General Public License as
Chris@1729 10 published by the Free Software Foundation; either version 2 of the
Chris@1729 11 License, or (at your option) any later version. See the file
Chris@1729 12 COPYING included with this distribution for more information.
Chris@1729 13 */
Chris@1729 14
Chris@1729 15 #ifndef SV_BY_ID_H
Chris@1729 16 #define SV_BY_ID_H
Chris@1729 17
Chris@1734 18 #include "Debug.h"
Chris@1734 19
Chris@1729 20 #include <memory>
Chris@1729 21 #include <map>
Chris@1731 22 #include <typeinfo>
Chris@1731 23 #include <iostream>
Chris@1731 24 #include <climits>
Chris@1729 25
Chris@1729 26 #include <QMutex>
Chris@1731 27 #include <QString>
Chris@1729 28
Chris@1738 29 #include "XmlExportable.h"
Chris@1738 30
Chris@1731 31 template <typename T>
Chris@1731 32 struct SvId {
Chris@1735 33
Chris@1731 34 int id;
Chris@1729 35
Chris@1735 36 enum {
Chris@1735 37 // The value NO_ID (-1) is never allocated by WithId
Chris@1735 38 NO_ID = -1
Chris@1735 39 };
Chris@1735 40
Chris@1735 41 SvId() : id(NO_ID) {}
Chris@1735 42
Chris@1735 43 SvId(const SvId &) =default;
Chris@1735 44 SvId &operator=(const SvId &) =default;
Chris@1735 45
Chris@1735 46 bool operator==(const SvId &other) const { return id == other.id; }
Chris@1731 47 bool operator<(const SvId &other) const { return id < other.id; }
Chris@1731 48
Chris@1735 49 bool isNone() const { return id == NO_ID; }
Chris@1735 50
Chris@1731 51 QString toString() const {
Chris@1731 52 return QString("%1").arg(id);
Chris@1731 53 }
Chris@1731 54 };
Chris@1731 55
Chris@1731 56 template <typename T>
Chris@1739 57 std::ostream &
Chris@1739 58 operator<<(std::ostream &ostr, const SvId<T> &id)
Chris@1739 59 {
Chris@1739 60 // For diagnostic purposes only. Do not use these IDs for
Chris@1739 61 // serialisation - see XmlExportable instead.
Chris@1739 62 if (id.isNone()) {
Chris@1739 63 return (ostr << "<none>");
Chris@1739 64 } else {
Chris@1739 65 return (ostr << "#" << id.id);
Chris@1739 66 }
Chris@1739 67 }
Chris@1739 68
Chris@1739 69 template <typename T>
Chris@1729 70 class WithId
Chris@1729 71 {
Chris@1729 72 public:
Chris@1731 73 typedef SvId<T> Id;
Chris@1731 74
Chris@1729 75 WithId() :
Chris@1729 76 m_id(getNextId()) {
Chris@1729 77 }
Chris@1729 78
Chris@1731 79 /**
Chris@1731 80 * Return an id for this object. The id is a unique identifier for
Chris@1731 81 * this object among all objects that implement WithId within this
Chris@1731 82 * single run of the application.
Chris@1731 83 */
Chris@1729 84 Id getId() const {
Chris@1731 85 Id id;
Chris@1731 86 id.id = m_id;
Chris@1731 87 return id;
Chris@1729 88 }
Chris@1729 89
Chris@1729 90 private:
Chris@1731 91 int m_id;
Chris@1731 92
Chris@1731 93 static int getNextId() {
Chris@1731 94 static int nextId = 0;
Chris@1731 95 static QMutex mutex;
Chris@1731 96 QMutexLocker locker(&mutex);
Chris@1731 97 int i = nextId;
Chris@1731 98 if (nextId == INT_MAX) {
Chris@1731 99 nextId = INT_MIN;
Chris@1735 100 } else {
Chris@1735 101 ++nextId;
Chris@1735 102 if (nextId == 0 || nextId == Id::NO_ID) {
Chris@1735 103 throw std::runtime_error("Internal ID limit exceeded!");
Chris@1735 104 }
Chris@1731 105 }
Chris@1731 106 return i;
Chris@1731 107 }
Chris@1729 108 };
Chris@1729 109
Chris@1731 110 template <typename Item, typename Id>
Chris@1729 111 class ById
Chris@1729 112 {
Chris@1729 113 public:
Chris@1731 114 ~ById() {
Chris@1731 115 QMutexLocker locker(&m_mutex);
Chris@1731 116 for (const auto &p: m_items) {
Chris@1731 117 if (p.second && p.second.use_count() > 0) {
Chris@1734 118 SVCERR << "WARNING: ById map destroyed with use count of "
Chris@1734 119 << p.second.use_count() << " for item with type "
Chris@1734 120 << typeid(*p.second.get()).name()
Chris@1734 121 << " and id " << p.first.id << endl;
Chris@1731 122 }
Chris@1731 123 }
Chris@1731 124 }
Chris@1731 125
Chris@1729 126 void add(std::shared_ptr<Item> item) {
Chris@1729 127 QMutexLocker locker(&m_mutex);
Chris@1734 128 auto id = item->getId();
Chris@1739 129 if (id.isNone()) {
Chris@1739 130 throw std::logic_error("item id should never be None");
Chris@1739 131 }
Chris@1734 132 if (m_items.find(id) != m_items.end()) {
Chris@1734 133 SVCERR << "WARNING: ById::add: item with id " << id
Chris@1734 134 << " is already recorded, replacing it (item type is "
Chris@1734 135 << typeid(*item.get()).name() << ")" << endl;
Chris@1734 136 }
Chris@1734 137 m_items[id] = item;
Chris@1729 138 }
Chris@1729 139
Chris@1729 140 void
Chris@1729 141 release(Id id) {
Chris@1729 142 QMutexLocker locker(&m_mutex);
Chris@1729 143 m_items.erase(id);
Chris@1729 144 }
Chris@1729 145
Chris@1729 146 std::shared_ptr<Item> get(Id id) const {
Chris@1739 147 if (id.isNone()) return {}; // this id is never issued: avoid locking
Chris@1729 148 QMutexLocker locker(&m_mutex);
Chris@1729 149 const auto &itr = m_items.find(id);
Chris@1729 150 if (itr != m_items.end()) {
Chris@1729 151 return itr->second;
Chris@1729 152 } else {
Chris@1739 153 return {};
Chris@1729 154 }
Chris@1729 155 }
Chris@1729 156
Chris@1729 157 template <typename Derived>
Chris@1729 158 std::shared_ptr<Derived> getAs(Id id) const {
Chris@1729 159 return std::dynamic_pointer_cast<Derived>(get(id));
Chris@1729 160 }
Chris@1738 161
Chris@1738 162 /**
Chris@1738 163 * If the Item type is an XmlExportable, return the export ID of
Chris@1739 164 * the given item ID. A call to this function will fail to compile
Chris@1739 165 * if the Item is not an XmlExportable.
Chris@1739 166 *
Chris@1739 167 * The export ID is a simple int, and is only allocated when first
Chris@1739 168 * requested, so objects that are never exported don't get one.
Chris@1738 169 */
Chris@1738 170 int getExportId(Id id) const {
Chris@1738 171 auto exportable = getAs<XmlExportable>(id);
Chris@1738 172 if (exportable) {
Chris@1738 173 return exportable->getExportId();
Chris@1738 174 } else {
Chris@1738 175 return XmlExportable::NO_ID;
Chris@1738 176 }
Chris@1738 177 }
Chris@1729 178
Chris@1729 179 private:
Chris@1729 180 mutable QMutex m_mutex;
Chris@1729 181 std::map<Id, std::shared_ptr<Item>> m_items;
Chris@1729 182 };
Chris@1729 183
Chris@1731 184 template <typename Item, typename Id>
Chris@1731 185 class StaticById
Chris@1729 186 {
Chris@1729 187 public:
Chris@1731 188 static void add(std::shared_ptr<Item> imagined) {
Chris@1731 189 byId().add(imagined);
Chris@1729 190 }
Chris@1729 191
Chris@1729 192 static void release(Id id) {
Chris@1731 193 byId().release(id);
Chris@1729 194 }
Chris@1729 195
Chris@1731 196 static std::shared_ptr<Item> get(Id id) {
Chris@1731 197 return byId().get(id);
Chris@1729 198 }
Chris@1729 199
Chris@1729 200 template <typename Derived>
Chris@1729 201 static
Chris@1729 202 std::shared_ptr<Derived> getAs(Id id) {
Chris@1731 203 return std::dynamic_pointer_cast<Derived>(get(id));
Chris@1729 204 }
Chris@1729 205
Chris@1738 206 static int getExportId(Id id) {
Chris@1738 207 return byId().getExportId(id);
Chris@1738 208 }
Chris@1738 209
Chris@1729 210 private:
Chris@1731 211 static
Chris@1731 212 ById<Item, Id> &byId() {
Chris@1731 213 static ById<Item, Id> b;
Chris@1731 214 return b;
Chris@1731 215 }
Chris@1729 216 };
Chris@1731 217
Chris@1729 218 #endif
Chris@1729 219