Edit: The example below makes a dangerous use of shared_ptr
, please refer tosee the edit belowat the bottom.
Edit: I have found that the use ofit is dangerous to transfer a raw pointer that's already owned by a shared_ptr
in my example is erroneous and can lead to multiple deletions ofanother shared_ptr
. The second shared_ptr
will have a different reference counting from the resourcefirst and it is 100% certain that the object will be destroyed at least twice. Please refer
Instead of returning void*
or BaseClass*
, prefer to return shared_ptr<void>
or shared_ptr<BaseClass>
and remember to use the aliasing constructor of shared_ptr
before returning shared_ptr<T>
in your equivalent of the followingResourceManager::load<T>
method from the example which, while also untested, doesn't showcase a dangerous practice:.
Something like:
struct ResourceManagerTextureFactory {
typedef std::function<std::shared_ptr<void> ()> TFactoryFunctor;
std::map<std::string, TFactoryFunctor> m_factories;
void subscribe (std::string key, TFactoryFunctor factory)shared_ptr<Texture> {texturePtr;
m_factories[key] = factory;
}
/* ... */
template <class T>
std::shared_ptr<T>shared_ptr<ResourceBase> load (std::string key) {
auto& factoryFunctor =if m_factories.at(key!texturePtr);
{
texturePtr = std::shared_ptr<void> ptr = factoryFunctormake_shared<Texture>();
// Here we use the "aliasing" constructor of shared_ptr, which will allow us to use the same reference counting of
/texturePtr->loadFromFile(/ the shared_ptr within the* factory.
//
// Back in the previous example, this returned shared_ptr would have had a different referece counting from the
// shared_ptr within the factory and this would have lead to the pointed resource getting destroyed at least twice
// once all shared_ptr's stopped point to it.
return std::shared_ptr<T>(ptr, static_cast<T*>(ptr.get()) */);
}
};
struct ResourceFactory {
std::string m_path;
void setFilePath (std::string path) {
m_path = path;
}
std::string getFilePath() const {
return m_path;texturePtr;
}
virtual void* load() =0;
void* operator() () {
return this->load()};
}
/* ... };*/
struct TextureFactory : ResourceFactoryResourceManager {
// Now, only one shared_ptr is enough. We don't need to keep a rawtemplate pointer<class around.T>
std::shared_ptr<Texture> m_texPtr;
TextureFactoryshared_ptr<T> load(std::string fileNamekey) {
setFilePath(TEXTURES_FOLDER + fileName);
}
std::shared_ptr<void> load() override {
auto& factoryFunctor if= m_factories.at(!m_texPtrkey) {;
m_texPtr = std::make_shared<Texture>();
shared_ptr<ResourceBase> ptr = m_texPtr->loadFromFile(getFilePathfactoryFunctor());
}
// A shared_ptr<void> is essentially a void*, we're not changing much here.
return m_texPtr;
}
};
// ---
ResourceManager daMan;
auto castRawPtr void= subscribeTexturesdynamic_cast<ResourceBase*>() {
daManptr.subscribe("hero", TextureFactoryget("hero.png"));
daMan.subscribe("foe", TextureFactory("foe.png"));
daMan.subscribe("tileset", TextureFactoryassert("tileset.png")castRawPtr);
}
void loadHeroTexture (Hero& hero) {
auto texPtr = daMan.load<Texture>("hero");
return hero.claimOwnershipstd::shared_ptr<T>(texPtrptr, castRawPtr); // Maybe you want
to keep track of who's using the resource?}
hero.sprite.setTexture(*texPtr);
};