/* * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once #include #include #include #include namespace nostalgia::core { class AssetManager; template class AssetRef; template struct AssetContainer { friend AssetManager; friend AssetRef; private: T m_obj; mutable int m_references = 0; public: template explicit constexpr AssetContainer(Args&&... args): m_obj(ox::forward(args)...) { } constexpr T *get() noexcept { return &m_obj; } constexpr const T *get() const noexcept { return &m_obj; } protected: constexpr void incRefs() const noexcept { ++m_references; } constexpr void decRefs() const noexcept { --m_references; } constexpr int references() const noexcept { return m_references; } }; template class AssetRef { private: const AssetContainer *m_ctr = nullptr; public: explicit constexpr AssetRef(const AssetContainer *c = nullptr) noexcept: m_ctr(c) { } constexpr AssetRef(const AssetRef &h) noexcept { m_ctr = h.m_ctr; if (m_ctr) { m_ctr->incRefs(); } } constexpr AssetRef(AssetRef &&h) noexcept { m_ctr = h.m_ctr; h.m_ctr = nullptr; } ~AssetRef() noexcept { if (m_ctr) { m_ctr->decRefs(); } } constexpr const T *get() const noexcept { return m_ctr->get(); } constexpr const T &operator*() const & noexcept { return *m_ctr->get(); } constexpr const T &&operator*() const && noexcept { return *m_ctr->get(); } constexpr const T *operator->() const noexcept { return m_ctr->get(); } AssetRef &operator=(const AssetRef &h) noexcept { if (this == &h) { return *this; } if (m_ctr) { m_ctr->decRefs(); } m_ctr = h.m_ctr; if (m_ctr) { m_ctr->incRefs(); } return *this; } AssetRef &operator=(AssetRef &&h) noexcept { if (this == &h) { return *this; } m_ctr = h.m_ctr; h.m_ctr = nullptr; return *this; } explicit constexpr operator bool() const noexcept { return m_ctr; } }; class AssetManager { private: class AssetTypeManagerBase { public: virtual ~AssetTypeManagerBase() = default; virtual void gc() noexcept = 0; }; template class AssetTypeManager: public AssetTypeManagerBase { private: ox::HashMap>> m_cache; public: ox::Result> getAsset(const ox::String &path) const noexcept { auto out = m_cache.at(path); oxReturnError(out); return AssetRef(out.value.get()); } ox::Result> setAsset(const ox::String &path, const T &obj) noexcept { auto &p = m_cache[path] = ox::make_unique>(obj); return AssetRef(p.get()); } void gc() noexcept final { for (const auto &ack : m_cache.keys()) { auto &ac = m_cache[ack]; if (ac->references()) { m_cache.erase(ack); } } } }; ox::HashMap> m_assetTypeManagers; template AssetTypeManager *getTypeManager() noexcept { constexpr auto typeName = ox::requireModelTypeName(); static_assert(ox_strcmp(typeName, "") != 0, "Types must have TypeName to use AssetManager"); auto &am = m_assetTypeManagers[typeName]; if (!am) { am = ox::make_unique>(); } return dynamic_cast*>(am.get()); } public: template ox::Result> getAsset(const ox::String &path) noexcept { auto m = getTypeManager(); return m->getAsset(path); } template ox::Result> setAsset(const ox::String &path, const T &obj) noexcept { auto m = getTypeManager(); return m->setAsset(path, obj); } void gc() noexcept { for (const auto &amk : m_assetTypeManagers.keys()) { auto &am = m_assetTypeManagers[amk]; am->gc(); } } }; }