I'm trying to implement a generic resource manager which would ensure that every resource gets only loaded once with C++11.
My first attempt:
resourcemanager.h
#ifndef RESOURCEMANAGER_H
#define RESOURCEMANAGER_H
#include <map>
#include <memory>
template<typename T>
class ResourceManager {
public:
static std::shared_ptr<T> load(std::string filePath);
private:
static map<std::string, std::weak_ptr<T>> resources;
virtual static std::shared_ptr<T> loadResource(std::string filePath) = 0;
};
#endif // RESOURCEMANAGER_H
#include "resourcemanager.h"
resourcemanager.cpp
using namespace std;
template<typename T>
map<string, weak_ptr<T>> ResourceManager<T>::resources;
template<typename T>
shared_ptr<T> ResourceManager<T>::load(std::string filePath) {
auto search = resources.find(filePath);
if (search != resources.end()) {
auto ptr = search->second.lock();
if (ptr) {
return ptr;
}
}
auto ptr = loadResource(filePath);
resources[filePath] = ptr;
return ptr;
}
However since abstract static methods are apparently forbidden black magic I tried to use CRTP:
resourcemanager.h
#ifndef RESOURCEMANAGER_H
#define RESOURCEMANAGER_H
#include <map>
#include <memory>
template<typename T, class Derived>
class ResourceManager {
public:
static std::shared_ptr<T> load(std::string filePath);
private:
static std::map<std::string, std::weak_ptr<T>> resources;
static std::shared_ptr<T> loadResource(std::string filePath);
};
#endif // RESOURCEMANAGER_H
resourcemanager.cpp
#include "resourcemanager.h"
using namespace std;
template<typename T, class Derived>
map<string, weak_ptr<T>> ResourceManager<T, Derived>::resources;
template<typename T, class Derived>
shared_ptr<T> ResourceManager<T, Derived>::load(string filePath) {
auto search = resources.find(filePath);
if (search != resources.end()) {
auto ptr = search->second.lock();
if (ptr) {
return ptr;
}
}
auto ptr = ResourceManager::loadResource(filePath);
resources[filePath] = ptr;
return ptr;
}
template<typename T, class Derived>
shared_ptr<T> ResourceManager<T, Derived>::loadResource(string filePath) {
return Derived::loadResource(filePath);
}
This looks like it should do what I want. However when I try to use it, it fails at the linking stage:
managedstring.h
#ifndef MANAGEDSTRING_H
#define MANAGEDSTRING_H
#include "resourcemanager.h"
class ManagedString {
public:
ManagedString(std::string filePath);
std::string get();
private:
std::shared_ptr<std::string> ptr;
class StringManager : public ResourceManager<std::string, StringManager> {
private:
static std::shared_ptr<std::string> loadResource(std::string filePath);
};
};
#endif // MANAGEDSTRING_H
managedstring.cpp
#include "managedstring.h"
using namespace std;
ManagedString::ManagedString(string filePath) {
ptr = StringManager::load(filePath);
}
string ManagedString::get() {
return *ptr;
}
shared_ptr<string> ManagedString::StringManager::loadResource(string filePath) {
// dummy implementation
return make_shared<string>("foo");
}
main.cpp
#include <iostream>
#include "managedstring.h"
using namespace std;
int main() {
ManagedString string1 = ManagedString("bar");
ManagedString string2 = ManagedString("foobar");
cout << string1.get() << endl;
cout << string2.get() << endl;
}
When I try to compile this with g++ -std=c++11 -o bin -Wall main.cpp managedstring.cpp resourcemanager.cpp (using gcc version 5.3.0) I get this error message:
/tmp/ccgqljOQ.o: In function `ManagedString::ManagedString(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
managedstring.cpp:(.text+0xdd): undefined reference to `ResourceManager<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >,
ManagedString::StringManager>::load(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
Should this work? Is this a compiler shortcoming? Or am I trying to do something I shouldn't do.
I also thought about altering my design, however I think it's not that bad. Feel free to disagree with me on this.
Aucun commentaire:
Enregistrer un commentaire