Singletons are bad but here's one way you could clean up after them:
I split the AssetMapSingleton up, so that AssetMap<T> is a normal class, and then AssetMapSingleton<T> creates a global instance of that class.
AssetMap<T> inherits the IAssetMap interface, so that all AssetMaps can be iterated to have a Cleanup function called.
The AssetMap<T> constructor/destruction adds/removes itself from a list inside the global AssetManager -- this allows the asset manager to iterate through each asset map if it needs to. The AssetMap<T> destructor also does a last-minute Cleanup call, in case you forget to.
#include <map>
#include <string>
#include <vector>
class IAssetMap
{
public:
virtual void Cleanup() = 0;
};
template<class T> struct AssetLoader {};
struct Texture2D
{
void Release() { /*todo*/ }
};
template<> struct AssetLoader<Texture2D>
{
static Texture2D* Load(const std::string& name) { /*todo*/ return 0; }
};
struct AssetManager
{
template<class T> static T* GetAsset(const std::string& name)
{
return AssetMapSingleton<T>::instance.GetAsset(name);
}
static void CleanupAll()
{
if( maps )
{
for(auto i = maps->begin(), end=maps->end(); i!=end; ++i )
{
(*i)->Cleanup();
}
}
}
static void RegisterMap( IAssetMap* p )
{
if( !maps )
maps = new std::vector<IAssetMap*>;
maps->push_back(p);
}
static void UnregisterMap( IAssetMap* p )
{
assert( maps );
maps->erase(std::find(maps->begin(), maps->end(), p));
if( maps->empty() )
{
delete maps;
maps = 0;
}
}
private:
static std::vector<IAssetMap*>* maps;
};
template<class T> class AssetMap : public IAssetMap
{
public:
AssetMap() { AssetManager::RegisterMap(this); }
~AssetMap() { AssetManager::UnregisterMap(this); Cleanup(); }//prevent leaks -- on program shutdown, do a last-minute cleanup
void Cleanup()
{
for(auto i = assets.begin(), end=assets.end(); i!=end; ++i )
{
i->second->Release();
}
assets.clear();
}
T* GetAsset(const std::string& name)
{
auto i = assets.find(name);
if( i != assets.end() )
return i->second;
T* asset = AssetLoader<T>::Load(name);
assets.insert( std::make_pair(name, asset) );
return asset;
}
private:
std::map<std::string, T*> assets;
};
//Creates a global AssetMap<T> for each T that's used
template<class T> struct AssetMapSingleton
{
static AssetMap<T> instance;
};
void test()
{
Texture2D *texturePtr = AssetManager::GetAsset<Texture2D>("hero_character");
}