annotate base/ById.h @ 1752:6d09d68165a4 by-id

Further review of ById: make IDs only available when adding a model to the ById store, not by querying the item directly. This means any id encountered in the wild must have been added to the store at some point (even if later released), which simplifies reasoning about lifecycles
author Chris Cannam
date Fri, 05 Jul 2019 15:28:07 +0100
parents d0ef65d8dd89
children d954dfccd922
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@1731 21 #include <iostream>
Chris@1731 22 #include <climits>
Chris@1742 23 #include <stdexcept>
Chris@1729 24
Chris@1729 25 #include <QMutex>
Chris@1731 26 #include <QString>
Chris@1729 27
Chris@1738 28 #include "XmlExportable.h"
Chris@1738 29
Chris@1744 30 //!!! todo: docs
Chris@1744 31
Chris@1750 32 //!!! further possibilities:
Chris@1750 33 //
Chris@1750 34 // - id can only be queried when an object is added to ById (maybe
Chris@1750 35 // add() returns the id, and there is a ById::idOf, but the
Chris@1750 36 // object has no public getId())
Chris@1750 37 //
Chris@1750 38 // - get() returns a pointer wrapper that cannot be shared/copied
Chris@1750 39 // again by the caller (except by the usual C++ trickery)
Chris@1750 40 //
Chris@1750 41 // also to do: review how often we are calling getAs<...> when we
Chris@1750 42 // could just be using get
Chris@1750 43
Chris@1742 44 struct IdAlloc {
Chris@1742 45
Chris@1742 46 // The value NO_ID (-1) is never allocated
Chris@1742 47 static const int NO_ID = -1;
Chris@1742 48
Chris@1742 49 static int getNextId();
Chris@1742 50 };
Chris@1742 51
Chris@1731 52 template <typename T>
Chris@1742 53 struct TypedId {
Chris@1735 54
Chris@1742 55 int untyped;
Chris@1742 56
Chris@1742 57 TypedId() : untyped(IdAlloc::NO_ID) {}
Chris@1729 58
Chris@1742 59 TypedId(const TypedId &) =default;
Chris@1742 60 TypedId &operator=(const TypedId &) =default;
Chris@1735 61
Chris@1742 62 bool operator==(const TypedId &other) const {
Chris@1742 63 return untyped == other.untyped;
Chris@1742 64 }
Chris@1742 65 bool operator!=(const TypedId &other) const {
Chris@1742 66 return untyped != other.untyped;
Chris@1742 67 }
Chris@1742 68 bool operator<(const TypedId &other) const {
Chris@1742 69 return untyped < other.untyped;
Chris@1742 70 }
Chris@1742 71 bool isNone() const {
Chris@1742 72 return untyped == IdAlloc::NO_ID;
Chris@1731 73 }
Chris@1731 74 };
Chris@1731 75
Chris@1731 76 template <typename T>
Chris@1739 77 std::ostream &
Chris@1742 78 operator<<(std::ostream &ostr, const TypedId<T> &id)
Chris@1739 79 {
Chris@1739 80 // For diagnostic purposes only. Do not use these IDs for
Chris@1739 81 // serialisation - see XmlExportable instead.
Chris@1739 82 if (id.isNone()) {
Chris@1739 83 return (ostr << "<none>");
Chris@1739 84 } else {
Chris@1742 85 return (ostr << "#" << id.untyped);
Chris@1739 86 }
Chris@1739 87 }
Chris@1739 88
Chris@1729 89 class WithId
Chris@1729 90 {
Chris@1729 91 public:
Chris@1729 92 WithId() :
Chris@1742 93 m_id(IdAlloc::getNextId()) {
Chris@1742 94 }
Chris@1742 95 virtual ~WithId() {
Chris@1729 96 }
Chris@1729 97
Chris@1752 98 protected:
Chris@1752 99 friend class AnyById;
Chris@1752 100
Chris@1731 101 /**
Chris@1742 102 * Return an id for this object. The id is a unique number for
Chris@1731 103 * this object among all objects that implement WithId within this
Chris@1731 104 * single run of the application.
Chris@1731 105 */
Chris@1742 106 int getUntypedId() const {
Chris@1742 107 return m_id;
Chris@1742 108 }
Chris@1742 109
Chris@1742 110 private:
Chris@1742 111 int m_id;
Chris@1742 112 };
Chris@1742 113
Chris@1742 114 template <typename T>
Chris@1742 115 class WithTypedId : virtual public WithId
Chris@1742 116 {
Chris@1742 117 public:
Chris@1742 118 typedef TypedId<T> Id;
Chris@1742 119
Chris@1742 120 WithTypedId() : WithId() { }
Chris@1742 121
Chris@1752 122 protected:
Chris@1752 123 template <typename Item, typename Id>
Chris@1752 124 friend class TypedById;
Chris@1752 125
Chris@1742 126 /**
Chris@1742 127 * Return an id for this object. The id is a unique value for this
Chris@1742 128 * object among all objects that implement WithTypedId within this
Chris@1742 129 * single run of the application.
Chris@1742 130 */
Chris@1729 131 Id getId() const {
Chris@1731 132 Id id;
Chris@1742 133 id.untyped = getUntypedId();
Chris@1731 134 return id;
Chris@1729 135 }
Chris@1742 136 };
Chris@1742 137
Chris@1742 138 class AnyById
Chris@1742 139 {
Chris@1742 140 public:
Chris@1752 141 static int add(std::shared_ptr<WithId>);
Chris@1742 142 static void release(int);
Chris@1746 143 static std::shared_ptr<WithId> get(int);
Chris@1742 144
Chris@1742 145 template <typename Derived>
Chris@1746 146 static bool isa(int id) {
Chris@1746 147 std::shared_ptr<WithId> p = get(id);
Chris@1746 148 return bool(std::dynamic_pointer_cast<Derived>(p));
Chris@1746 149 }
Chris@1746 150
Chris@1746 151 template <typename Derived>
Chris@1742 152 static std::shared_ptr<Derived> getAs(int id) {
Chris@1742 153 std::shared_ptr<WithId> p = get(id);
Chris@1742 154 return std::dynamic_pointer_cast<Derived>(p);
Chris@1742 155 }
Chris@1729 156
Chris@1729 157 private:
Chris@1742 158 class Impl;
Chris@1742 159 static Impl &impl();
Chris@1729 160 };
Chris@1729 161
Chris@1731 162 template <typename Item, typename Id>
Chris@1742 163 class TypedById
Chris@1729 164 {
Chris@1729 165 public:
Chris@1750 166 static Id add(std::shared_ptr<Item> item) {
Chris@1752 167 Id id;
Chris@1752 168 id.untyped = AnyById::add(item);
Chris@1750 169 return id;
Chris@1729 170 }
Chris@1729 171
Chris@1742 172 static void release(Id id) {
Chris@1742 173 AnyById::release(id.untyped);
Chris@1729 174 }
Chris@1742 175 static void release(std::shared_ptr<Item> item) {
Chris@1742 176 release(item->getId());
Chris@1729 177 }
Chris@1729 178
Chris@1729 179 template <typename Derived>
Chris@1746 180 static bool isa(Id id) {
Chris@1746 181 return AnyById::isa<Derived>(id.untyped);
Chris@1746 182 }
Chris@1746 183
Chris@1746 184 template <typename Derived>
Chris@1742 185 static std::shared_ptr<Derived> getAs(Id id) {
Chris@1742 186 return AnyById::getAs<Derived>(id.untyped);
Chris@1742 187 }
Chris@1742 188
Chris@1742 189 static std::shared_ptr<Item> get(Id id) {
Chris@1742 190 return getAs<Item>(id);
Chris@1729 191 }
Chris@1752 192
Chris@1738 193 /**
Chris@1738 194 * If the Item type is an XmlExportable, return the export ID of
Chris@1739 195 * the given item ID. A call to this function will fail to compile
Chris@1739 196 * if the Item is not an XmlExportable.
Chris@1739 197 *
Chris@1739 198 * The export ID is a simple int, and is only allocated when first
Chris@1739 199 * requested, so objects that are never exported don't get one.
Chris@1738 200 */
Chris@1742 201 static int getExportId(Id id) {
Chris@1738 202 auto exportable = getAs<XmlExportable>(id);
Chris@1738 203 if (exportable) {
Chris@1738 204 return exportable->getExportId();
Chris@1738 205 } else {
Chris@1738 206 return XmlExportable::NO_ID;
Chris@1738 207 }
Chris@1738 208 }
Chris@1729 209 };
Chris@1731 210
Chris@1729 211 #endif
Chris@1729 212