annotate 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
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@1742 30 struct IdAlloc {
Chris@1742 31
Chris@1742 32 // The value NO_ID (-1) is never allocated
Chris@1742 33 static const int NO_ID = -1;
Chris@1742 34
Chris@1742 35 static int getNextId();
Chris@1742 36 };
Chris@1742 37
Chris@1731 38 template <typename T>
Chris@1742 39 struct TypedId {
Chris@1735 40
Chris@1742 41 int untyped;
Chris@1742 42
Chris@1742 43 TypedId() : untyped(IdAlloc::NO_ID) {}
Chris@1729 44
Chris@1742 45 TypedId(const TypedId &) =default;
Chris@1742 46 TypedId &operator=(const TypedId &) =default;
Chris@1735 47
Chris@1742 48 bool operator==(const TypedId &other) const {
Chris@1742 49 return untyped == other.untyped;
Chris@1742 50 }
Chris@1742 51 bool operator!=(const TypedId &other) const {
Chris@1742 52 return untyped != other.untyped;
Chris@1742 53 }
Chris@1742 54 bool operator<(const TypedId &other) const {
Chris@1742 55 return untyped < other.untyped;
Chris@1742 56 }
Chris@1742 57 bool isNone() const {
Chris@1742 58 return untyped == IdAlloc::NO_ID;
Chris@1731 59 }
Chris@1731 60 };
Chris@1731 61
Chris@1731 62 template <typename T>
Chris@1739 63 std::ostream &
Chris@1742 64 operator<<(std::ostream &ostr, const TypedId<T> &id)
Chris@1739 65 {
Chris@1739 66 // For diagnostic purposes only. Do not use these IDs for
Chris@1739 67 // serialisation - see XmlExportable instead.
Chris@1739 68 if (id.isNone()) {
Chris@1739 69 return (ostr << "<none>");
Chris@1739 70 } else {
Chris@1742 71 return (ostr << "#" << id.untyped);
Chris@1739 72 }
Chris@1739 73 }
Chris@1739 74
Chris@1729 75 class WithId
Chris@1729 76 {
Chris@1729 77 public:
Chris@1729 78 WithId() :
Chris@1742 79 m_id(IdAlloc::getNextId()) {
Chris@1742 80 }
Chris@1742 81 virtual ~WithId() {
Chris@1729 82 }
Chris@1729 83
Chris@1731 84 /**
Chris@1742 85 * Return an id for this object. The id is a unique number for
Chris@1731 86 * this object among all objects that implement WithId within this
Chris@1731 87 * single run of the application.
Chris@1731 88 */
Chris@1742 89 int getUntypedId() const {
Chris@1742 90 return m_id;
Chris@1742 91 }
Chris@1742 92
Chris@1742 93 private:
Chris@1742 94 int m_id;
Chris@1742 95 };
Chris@1742 96
Chris@1742 97 template <typename T>
Chris@1742 98 class WithTypedId : virtual public WithId
Chris@1742 99 {
Chris@1742 100 public:
Chris@1742 101 typedef TypedId<T> Id;
Chris@1742 102
Chris@1742 103 WithTypedId() : WithId() { }
Chris@1742 104
Chris@1742 105 /**
Chris@1742 106 * Return an id for this object. The id is a unique value for this
Chris@1742 107 * object among all objects that implement WithTypedId within this
Chris@1742 108 * single run of the application.
Chris@1742 109 */
Chris@1729 110 Id getId() const {
Chris@1731 111 Id id;
Chris@1742 112 id.untyped = getUntypedId();
Chris@1731 113 return id;
Chris@1729 114 }
Chris@1742 115 };
Chris@1742 116
Chris@1742 117 class AnyById
Chris@1742 118 {
Chris@1742 119 public:
Chris@1742 120 static void add(int, std::shared_ptr<WithId>);
Chris@1742 121 static void release(int);
Chris@1742 122 static std::shared_ptr<WithId> get(int);
Chris@1742 123
Chris@1742 124 template <typename Derived>
Chris@1742 125 static std::shared_ptr<Derived> getAs(int id) {
Chris@1742 126 std::shared_ptr<WithId> p = get(id);
Chris@1742 127 return std::dynamic_pointer_cast<Derived>(p);
Chris@1742 128 }
Chris@1729 129
Chris@1729 130 private:
Chris@1742 131 class Impl;
Chris@1742 132 static Impl &impl();
Chris@1729 133 };
Chris@1729 134
Chris@1731 135 template <typename Item, typename Id>
Chris@1742 136 class TypedById
Chris@1729 137 {
Chris@1729 138 public:
Chris@1742 139 static void add(std::shared_ptr<Item> item) {
Chris@1734 140 auto id = item->getId();
Chris@1739 141 if (id.isNone()) {
Chris@1739 142 throw std::logic_error("item id should never be None");
Chris@1739 143 }
Chris@1742 144 AnyById::add(id.untyped, item);
Chris@1729 145 }
Chris@1729 146
Chris@1742 147 static void release(Id id) {
Chris@1742 148 AnyById::release(id.untyped);
Chris@1729 149 }
Chris@1742 150 static void release(std::shared_ptr<Item> item) {
Chris@1742 151 release(item->getId());
Chris@1729 152 }
Chris@1729 153
Chris@1729 154 template <typename Derived>
Chris@1742 155 static std::shared_ptr<Derived> getAs(Id id) {
Chris@1742 156 if (id.isNone()) return {}; // this id is never issued: avoid locking
Chris@1742 157 return AnyById::getAs<Derived>(id.untyped);
Chris@1742 158 }
Chris@1742 159
Chris@1742 160 static std::shared_ptr<Item> get(Id id) {
Chris@1742 161 return getAs<Item>(id);
Chris@1729 162 }
Chris@1738 163
Chris@1738 164 /**
Chris@1738 165 * If the Item type is an XmlExportable, return the export ID of
Chris@1739 166 * the given item ID. A call to this function will fail to compile
Chris@1739 167 * if the Item is not an XmlExportable.
Chris@1739 168 *
Chris@1739 169 * The export ID is a simple int, and is only allocated when first
Chris@1739 170 * requested, so objects that are never exported don't get one.
Chris@1738 171 */
Chris@1742 172 static int getExportId(Id id) {
Chris@1738 173 auto exportable = getAs<XmlExportable>(id);
Chris@1738 174 if (exportable) {
Chris@1738 175 return exportable->getExportId();
Chris@1738 176 } else {
Chris@1738 177 return XmlExportable::NO_ID;
Chris@1738 178 }
Chris@1738 179 }
Chris@1729 180 };
Chris@1731 181
Chris@1729 182 #endif
Chris@1729 183