view base/ById.h @ 1735:d91ff235e69d by-id

Some messing with Model and AlignmentModel
author Chris Cannam
date Tue, 25 Jun 2019 15:29:34 +0100
parents 601851995f4b
children d9082ed16931
line wrap: on
line source
/* -*- 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.
*/

#ifndef SV_BY_ID_H
#define SV_BY_ID_H

#include <memory>
#include <map>
#include <typeinfo>
#include <iostream>
#include <climits>

#include <QMutex>
#include <QString>

template <typename T>
struct SvId {
    
    int id;

    enum {
        // The value NO_ID (-1) is never allocated by WithId
        NO_ID = -1
    };
    
    SvId() : id(NO_ID) {}

    SvId(const SvId &) =default;
    SvId &operator=(const SvId &) =default;

    bool operator==(const SvId &other) const { return id == other.id; }
    bool operator<(const SvId &other) const { return id < other.id; }

    bool isNone() const { return id == NO_ID; }

    QString toString() const {
        return QString("%1").arg(id);
    }
};

template <typename T>
class WithId
{
public:
    typedef SvId<T> Id;
    
    WithId() :
        m_id(getNextId()) {
    }

    /**
     * Return an id for this object. The id is a unique identifier for
     * this object among all objects that implement WithId within this
     * single run of the application.
     */
    Id getId() const {
        Id id;
        id.id = m_id;
        return id;
    }

private:
    int m_id;

    static int 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 == Id::NO_ID) {
                throw std::runtime_error("Internal ID limit exceeded!");
            }
        }
        return i;
    }
};

template <typename Item, typename Id>
class ById
{
public:
    ~ById() {
        QMutexLocker locker(&m_mutex);
        for (const auto &p: m_items) {
            if (p.second && p.second.use_count() > 0) {
                std::cerr << "WARNING: ById map destroyed with use count of "
                          << p.second.use_count() << " for item with type "
                          << typeid(*p.second.get()).name()
                          << " and id " << p.first.id << std::endl;
            }
        }
    }
    
    void add(std::shared_ptr<Item> item) {
        QMutexLocker locker(&m_mutex);
        m_items[item->getId()] = item;
    }

    void
    release(Id id) {
        QMutexLocker locker(&m_mutex);
        m_items.erase(id);
    }

    std::shared_ptr<Item> get(Id id) const {
        QMutexLocker locker(&m_mutex);
        const auto &itr = m_items.find(id);
        if (itr != m_items.end()) {
            return itr->second;
        } else {
            return std::shared_ptr<Item>();
        }
    }

    template <typename Derived>
    std::shared_ptr<Derived> getAs(Id id) const {
        return std::dynamic_pointer_cast<Derived>(get(id));
    }
    
private:
    mutable QMutex m_mutex;
    std::map<Id, std::shared_ptr<Item>> m_items;
};

template <typename Item, typename Id>
class StaticById
{
public:
    static void add(std::shared_ptr<Item> imagined) {
        byId().add(imagined);
    }

    static void release(Id id) {
        byId().release(id);
    }

    static std::shared_ptr<Item> get(Id id) {
        return byId().get(id);
    }

    template <typename Derived>
    static
    std::shared_ptr<Derived> getAs(Id id) {
        return std::dynamic_pointer_cast<Derived>(get(id));
    }

private:
    static
    ById<Item, Id> &byId() {
        static ById<Item, Id> b;
        return b;
    }
};

#endif