diff base/ById.cpp @ 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
children b92bdcd4954b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/ById.cpp	Fri Jun 28 17:36:30 2019 +0100
@@ -0,0 +1,122 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "ById.h"
+
+#include <unordered_map>
+#include <typeinfo>
+
+int IdAlloc::getNextId()
+{
+    static int nextId = 0;
+    static QMutex mutex;
+    QMutexLocker locker(&mutex);
+    int i = nextId;
+    if (nextId == INT_MAX) {
+        nextId = INT_MIN;
+    } else {
+        ++nextId;
+        if (nextId == 0 || nextId == NO_ID) {
+            throw std::runtime_error("Internal ID limit exceeded!");
+        }
+    }
+    return i;
+}
+
+class AnyById::Impl
+{
+public:    
+    ~Impl() {
+        QMutexLocker locker(&m_mutex);
+        bool empty = true;
+        for (const auto &p: m_items) {
+            if (p.second && p.second.use_count() > 0) {
+                empty = false;
+                break;
+            }
+        }
+        if (!empty) {
+            SVCERR << "WARNING: ById map is not empty at close; some items have not been released" << endl;
+            SVCERR << "         Unreleased items are:" << endl;
+            for (const auto &p: m_items) {
+                if (p.second && p.second.use_count() > 0) {
+                    SVCERR << "         - id #" << p.first
+                           << ": type " << typeid(*p.second.get()).name()
+                           << ", use count " << p.second.use_count() << endl;
+                }
+            }
+        }
+    }
+        
+    void add(int id, std::shared_ptr<WithId> item) {
+        QMutexLocker locker(&m_mutex);
+        if (m_items.find(id) != m_items.end()) {
+            SVCERR << "ById::add: item with id " << id
+                   << " is already recorded (existing item type is "
+                   << typeid(*m_items.find(id)->second.get()).name()
+                   << ", proposed is "
+                   << typeid(*item.get()).name() << ")" << endl;
+            throw std::logic_error("item id is already recorded in add");
+        }
+        m_items[id] = item;
+    }
+
+    void release(int id) {
+        QMutexLocker locker(&m_mutex);
+        if (m_items.find(id) == m_items.end()) {
+            SVCERR << "ById::release: unknown item id " << id << endl;
+            throw std::logic_error("unknown item id in release");
+        }
+        m_items.erase(id);
+    }
+    
+    std::shared_ptr<WithId> get(int id) const {
+        QMutexLocker locker(&m_mutex);
+        const auto &itr = m_items.find(id);
+        if (itr != m_items.end()) {
+            return itr->second;
+        } else {
+            return {};
+        }
+    }
+
+private:
+    mutable QMutex m_mutex;
+    std::unordered_map<int, std::shared_ptr<WithId>> m_items;
+};
+
+void
+AnyById::add(int id, std::shared_ptr<WithId> item)
+{
+    impl().add(id, item);
+}
+
+void
+AnyById::release(int id)
+{
+    impl().release(id);
+}
+
+std::shared_ptr<WithId>
+AnyById::get(int id)
+{
+    return impl().get(id);
+}
+
+AnyById::Impl &
+AnyById::impl()
+{
+    static Impl impl;
+    return impl;
+}