annotate base/ById.h @ 1833:21c792334c2e sensible-delimited-data-strings

Rewrite all the DelimitedDataString stuff so as to return vectors of individual cell strings rather than having the classes add the delimiters themselves. Rename accordingly to names based on StringExport. Take advantage of this in the CSV writer code so as to properly quote cells that contain delimiter characters.
author Chris Cannam
date Fri, 03 Apr 2020 17:11:05 +0100
parents 0678bf772f82
children 7faa08747f5e
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@1764 30 /*
Chris@1764 31 * ById - central pool of objects to be retrieved by persistent id.
Chris@1764 32 *
Chris@1764 33 * This is a pretty simple mechanism for obtaining safe "borrowed"
Chris@1764 34 * references to shared objects, including across threads, based on an
Chris@1764 35 * object ID.
Chris@1764 36 *
Chris@1764 37 * A class (call it C) inherits WithTypedId<C>. This produces a type
Chris@1764 38 * C::Id containing a numerical id. Each instance of C (or subclass
Chris@1764 39 * thereof) has an internal id of type C::Id whose value is unique
Chris@1764 40 * among all ids ever possessed by any instances of all classes that
Chris@1764 41 * use this id mechanism (within a single run of the program).
Chris@1764 42 *
Chris@1764 43 * Then we have a static store of type TypedById<C, C::Id>. This holds
Chris@1764 44 * a set of heap-allocated C objects (or subclass thereof) and hands
Chris@1764 45 * out shared_ptr references to them when queried by id. The
Chris@1764 46 * application calls add() to pass an object to the store (which takes
Chris@1764 47 * ownership of it), and the application calls release() when it
Chris@1764 48 * thinks it has finished with an object, to request the store to
Chris@1764 49 * delete it.
Chris@1764 50 *
Chris@1764 51 * Note that an object's id can't (without shenanigans) be queried
Chris@1764 52 * directly from that object - it is returned when the object is added
Chris@1764 53 * to a ById store. So if you have an object id, you know that the
Chris@1764 54 * object must have been added to a store at some point.
Chris@1764 55 *
Chris@1764 56 * The goal is to improve code that would previously have retained a
Chris@1764 57 * bare pointer to a heap-allocated object that it did not own. For
Chris@1764 58 * example, in Sonic Visualiser we have a Model hierarchy of complex
Chris@1764 59 * mutable objects, and any given model may be referred to by many
Chris@1764 60 * different layers, transforms (as both source and target) etc. Using
Chris@1764 61 * bare pointers for those references means that we need everything to
Chris@1764 62 * be notified (and act properly on the notification) if a model is
Chris@1764 63 * about to be deleted. Using a Model::Id instead gives the code a
Chris@1764 64 * guarantee: if the model has been deleted since you last looked at
Chris@1764 65 * it, then the ById store will return a null shared_ptr from its
Chris@1764 66 * get() function for that id; but if it returns a non-null
Chris@1764 67 * shared_ptr, then the object being pointed to can't be deleted while
Chris@1764 68 * that shared_ptr is in scope.
Chris@1764 69 *
Chris@1764 70 * Example:
Chris@1764 71 *
Chris@1764 72 * class Thing : public WithTypedId<Thing> { Thing(int x) { } };
Chris@1764 73 * typedef TypedById<Thing, Thing::Id> ThingById;
Chris@1764 74 *
Chris@1764 75 * // application creates a new Thing
Chris@1764 76 * // ...
Chris@1764 77 * auto thing = std::make_shared<Thing>(10);
Chris@1764 78 * auto thingId = ThingById::add(thing);
Chris@1764 79 *
Chris@1764 80 * // application then passes thingId to something else, without
Chris@1764 81 * // storing the shared_ptr anywhere - the ById store manages that
Chris@1764 82 *
Chris@1764 83 * // code elsewhere now has the thingId, and needs to use the Thing
Chris@1764 84 * // ...
Chris@1764 85 * void doSomething() {
Chris@1764 86 * auto thing = ThingById::get(m_thingId);
Chris@1764 87 * if (!thing) { // the Thing has been deleted, stop acting on it
Chris@1764 88 * return; // (this may be an error or it may be unexceptional)
Chris@1764 89 * }
Chris@1764 90 * // now we have a guarantee that the thing ptr will be valid
Chris@1764 91 * // until it goes out of scope when doSomething returns
Chris@1764 92 * }
Chris@1764 93 *
Chris@1764 94 * // application wants to be rid of the Thing
Chris@1764 95 * ThingById::release(thingId);
Chris@1764 96 */
Chris@1744 97
Chris@1764 98 //!!! to do: review how often we are calling getAs<...> when we could
Chris@1764 99 // just be using get
Chris@1753 100
Chris@1742 101 struct IdAlloc {
Chris@1742 102
Chris@1742 103 // The value NO_ID (-1) is never allocated
Chris@1742 104 static const int NO_ID = -1;
Chris@1742 105
Chris@1742 106 static int getNextId();
Chris@1742 107 };
Chris@1742 108
Chris@1731 109 template <typename T>
Chris@1742 110 struct TypedId {
Chris@1735 111
Chris@1742 112 int untyped;
Chris@1742 113
Chris@1742 114 TypedId() : untyped(IdAlloc::NO_ID) {}
Chris@1729 115
Chris@1742 116 TypedId(const TypedId &) =default;
Chris@1742 117 TypedId &operator=(const TypedId &) =default;
Chris@1735 118
Chris@1742 119 bool operator==(const TypedId &other) const {
Chris@1742 120 return untyped == other.untyped;
Chris@1742 121 }
Chris@1742 122 bool operator!=(const TypedId &other) const {
Chris@1742 123 return untyped != other.untyped;
Chris@1742 124 }
Chris@1742 125 bool operator<(const TypedId &other) const {
Chris@1742 126 return untyped < other.untyped;
Chris@1742 127 }
Chris@1742 128 bool isNone() const {
Chris@1742 129 return untyped == IdAlloc::NO_ID;
Chris@1731 130 }
Chris@1731 131 };
Chris@1731 132
Chris@1731 133 template <typename T>
Chris@1739 134 std::ostream &
Chris@1742 135 operator<<(std::ostream &ostr, const TypedId<T> &id)
Chris@1739 136 {
Chris@1739 137 // For diagnostic purposes only. Do not use these IDs for
Chris@1739 138 // serialisation - see XmlExportable instead.
Chris@1739 139 if (id.isNone()) {
Chris@1739 140 return (ostr << "<none>");
Chris@1739 141 } else {
Chris@1742 142 return (ostr << "#" << id.untyped);
Chris@1739 143 }
Chris@1739 144 }
Chris@1739 145
Chris@1729 146 class WithId
Chris@1729 147 {
Chris@1729 148 public:
Chris@1729 149 WithId() :
Chris@1742 150 m_id(IdAlloc::getNextId()) {
Chris@1742 151 }
Chris@1742 152 virtual ~WithId() {
Chris@1729 153 }
Chris@1729 154
Chris@1752 155 protected:
Chris@1752 156 friend class AnyById;
Chris@1752 157
Chris@1731 158 /**
Chris@1742 159 * Return an id for this object. The id is a unique number for
Chris@1731 160 * this object among all objects that implement WithId within this
Chris@1731 161 * single run of the application.
Chris@1731 162 */
Chris@1742 163 int getUntypedId() const {
Chris@1742 164 return m_id;
Chris@1742 165 }
Chris@1742 166
Chris@1742 167 private:
Chris@1742 168 int m_id;
Chris@1742 169 };
Chris@1742 170
Chris@1742 171 template <typename T>
Chris@1742 172 class WithTypedId : virtual public WithId
Chris@1742 173 {
Chris@1742 174 public:
Chris@1742 175 typedef TypedId<T> Id;
Chris@1742 176
Chris@1742 177 WithTypedId() : WithId() { }
Chris@1742 178
Chris@1752 179 protected:
Chris@1752 180 template <typename Item, typename Id>
Chris@1752 181 friend class TypedById;
Chris@1752 182
Chris@1742 183 /**
Chris@1742 184 * Return an id for this object. The id is a unique value for this
Chris@1742 185 * object among all objects that implement WithTypedId within this
Chris@1742 186 * single run of the application.
Chris@1742 187 */
Chris@1729 188 Id getId() const {
Chris@1731 189 Id id;
Chris@1742 190 id.untyped = getUntypedId();
Chris@1731 191 return id;
Chris@1729 192 }
Chris@1742 193 };
Chris@1742 194
Chris@1742 195 class AnyById
Chris@1742 196 {
Chris@1742 197 public:
Chris@1752 198 static int add(std::shared_ptr<WithId>);
Chris@1742 199 static void release(int);
Chris@1746 200 static std::shared_ptr<WithId> get(int);
Chris@1742 201
Chris@1742 202 template <typename Derived>
Chris@1746 203 static bool isa(int id) {
Chris@1746 204 std::shared_ptr<WithId> p = get(id);
Chris@1746 205 return bool(std::dynamic_pointer_cast<Derived>(p));
Chris@1746 206 }
Chris@1746 207
Chris@1746 208 template <typename Derived>
Chris@1742 209 static std::shared_ptr<Derived> getAs(int id) {
Chris@1742 210 std::shared_ptr<WithId> p = get(id);
Chris@1742 211 return std::dynamic_pointer_cast<Derived>(p);
Chris@1742 212 }
Chris@1729 213
Chris@1729 214 private:
Chris@1742 215 class Impl;
Chris@1742 216 static Impl &impl();
Chris@1729 217 };
Chris@1729 218
Chris@1731 219 template <typename Item, typename Id>
Chris@1742 220 class TypedById
Chris@1729 221 {
Chris@1729 222 public:
Chris@1750 223 static Id add(std::shared_ptr<Item> item) {
Chris@1752 224 Id id;
Chris@1752 225 id.untyped = AnyById::add(item);
Chris@1750 226 return id;
Chris@1729 227 }
Chris@1729 228
Chris@1742 229 static void release(Id id) {
Chris@1742 230 AnyById::release(id.untyped);
Chris@1729 231 }
Chris@1742 232 static void release(std::shared_ptr<Item> item) {
Chris@1742 233 release(item->getId());
Chris@1729 234 }
Chris@1729 235
Chris@1729 236 template <typename Derived>
Chris@1746 237 static bool isa(Id id) {
Chris@1746 238 return AnyById::isa<Derived>(id.untyped);
Chris@1746 239 }
Chris@1746 240
Chris@1746 241 template <typename Derived>
Chris@1742 242 static std::shared_ptr<Derived> getAs(Id id) {
Chris@1742 243 return AnyById::getAs<Derived>(id.untyped);
Chris@1742 244 }
Chris@1742 245
Chris@1742 246 static std::shared_ptr<Item> get(Id id) {
Chris@1742 247 return getAs<Item>(id);
Chris@1729 248 }
Chris@1752 249
Chris@1738 250 /**
Chris@1738 251 * If the Item type is an XmlExportable, return the export ID of
Chris@1739 252 * the given item ID. A call to this function will fail to compile
Chris@1739 253 * if the Item is not an XmlExportable.
Chris@1739 254 *
Chris@1739 255 * The export ID is a simple int, and is only allocated when first
Chris@1739 256 * requested, so objects that are never exported don't get one.
Chris@1738 257 */
Chris@1742 258 static int getExportId(Id id) {
Chris@1738 259 auto exportable = getAs<XmlExportable>(id);
Chris@1738 260 if (exportable) {
Chris@1738 261 return exportable->getExportId();
Chris@1738 262 } else {
Chris@1738 263 return XmlExportable::NO_ID;
Chris@1738 264 }
Chris@1738 265 }
Chris@1729 266 };
Chris@1731 267
Chris@1729 268 #endif
Chris@1729 269