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@1731: #include Chris@1731: #include Chris@1742: #include Chris@1729: Chris@1729: #include Chris@1731: #include Chris@1729: Chris@1738: #include "XmlExportable.h" Chris@1738: Chris@1744: //!!! todo: docs Chris@1744: Chris@1750: //!!! further possibilities: Chris@1750: // Chris@1750: // - id can only be queried when an object is added to ById (maybe Chris@1750: // add() returns the id, and there is a ById::idOf, but the Chris@1750: // object has no public getId()) Chris@1750: // Chris@1750: // - get() returns a pointer wrapper that cannot be shared/copied Chris@1750: // again by the caller (except by the usual C++ trickery) Chris@1750: // Chris@1750: // also to do: review how often we are calling getAs<...> when we Chris@1750: // could just be using get Chris@1750: Chris@1742: struct IdAlloc { Chris@1742: Chris@1742: // The value NO_ID (-1) is never allocated Chris@1742: static const int NO_ID = -1; Chris@1742: Chris@1742: static int getNextId(); Chris@1742: }; Chris@1742: Chris@1731: template Chris@1742: struct TypedId { Chris@1735: Chris@1742: int untyped; Chris@1742: Chris@1742: TypedId() : untyped(IdAlloc::NO_ID) {} Chris@1729: Chris@1742: TypedId(const TypedId &) =default; Chris@1742: TypedId &operator=(const TypedId &) =default; Chris@1735: Chris@1742: bool operator==(const TypedId &other) const { Chris@1742: return untyped == other.untyped; Chris@1742: } Chris@1742: bool operator!=(const TypedId &other) const { Chris@1742: return untyped != other.untyped; Chris@1742: } Chris@1742: bool operator<(const TypedId &other) const { Chris@1742: return untyped < other.untyped; Chris@1742: } Chris@1742: bool isNone() const { Chris@1742: return untyped == IdAlloc::NO_ID; Chris@1731: } Chris@1731: }; Chris@1731: Chris@1731: template Chris@1739: std::ostream & Chris@1742: operator<<(std::ostream &ostr, const TypedId &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@1742: return (ostr << "#" << id.untyped); Chris@1739: } Chris@1739: } Chris@1739: Chris@1729: class WithId Chris@1729: { Chris@1729: public: Chris@1729: WithId() : Chris@1742: m_id(IdAlloc::getNextId()) { Chris@1742: } Chris@1742: virtual ~WithId() { Chris@1729: } Chris@1729: Chris@1752: protected: Chris@1752: friend class AnyById; Chris@1752: Chris@1731: /** Chris@1742: * Return an id for this object. The id is a unique number for Chris@1731: * this object among all objects that implement WithId within this Chris@1731: * single run of the application. Chris@1731: */ Chris@1742: int getUntypedId() const { Chris@1742: return m_id; Chris@1742: } Chris@1742: Chris@1742: private: Chris@1742: int m_id; Chris@1742: }; Chris@1742: Chris@1742: template Chris@1742: class WithTypedId : virtual public WithId Chris@1742: { Chris@1742: public: Chris@1742: typedef TypedId Id; Chris@1742: Chris@1742: WithTypedId() : WithId() { } Chris@1742: Chris@1752: protected: Chris@1752: template Chris@1752: friend class TypedById; Chris@1752: Chris@1742: /** Chris@1742: * Return an id for this object. The id is a unique value for this Chris@1742: * object among all objects that implement WithTypedId within this Chris@1742: * single run of the application. Chris@1742: */ Chris@1729: Id getId() const { Chris@1731: Id id; Chris@1742: id.untyped = getUntypedId(); Chris@1731: return id; Chris@1729: } Chris@1742: }; Chris@1742: Chris@1742: class AnyById Chris@1742: { Chris@1742: public: Chris@1752: static int add(std::shared_ptr); Chris@1742: static void release(int); Chris@1746: static std::shared_ptr get(int); Chris@1742: Chris@1742: template Chris@1746: static bool isa(int id) { Chris@1746: std::shared_ptr p = get(id); Chris@1746: return bool(std::dynamic_pointer_cast(p)); Chris@1746: } Chris@1746: Chris@1746: template Chris@1742: static std::shared_ptr getAs(int id) { Chris@1742: std::shared_ptr p = get(id); Chris@1742: return std::dynamic_pointer_cast(p); Chris@1742: } Chris@1729: Chris@1729: private: Chris@1742: class Impl; Chris@1742: static Impl &impl(); Chris@1729: }; Chris@1729: Chris@1731: template Chris@1742: class TypedById Chris@1729: { Chris@1729: public: Chris@1750: static Id add(std::shared_ptr item) { Chris@1752: Id id; Chris@1752: id.untyped = AnyById::add(item); Chris@1750: return id; Chris@1729: } Chris@1729: Chris@1742: static void release(Id id) { Chris@1742: AnyById::release(id.untyped); Chris@1729: } Chris@1742: static void release(std::shared_ptr item) { Chris@1742: release(item->getId()); Chris@1729: } Chris@1729: Chris@1729: template Chris@1746: static bool isa(Id id) { Chris@1746: return AnyById::isa(id.untyped); Chris@1746: } Chris@1746: Chris@1746: template Chris@1742: static std::shared_ptr getAs(Id id) { Chris@1742: return AnyById::getAs(id.untyped); Chris@1742: } Chris@1742: Chris@1742: static std::shared_ptr get(Id id) { Chris@1742: return getAs(id); Chris@1729: } Chris@1752: 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@1742: static int getExportId(Id id) { 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@1731: Chris@1729: #endif Chris@1729: