2016-04-25 3 views
2

Ich versuche, einen generischen Ressourcenmanager zu implementieren, der sicherstellen würde, dass jede Ressource nur einmal mit C++ 11 geladen wird.Mit CRTP als Alternative zu abstrakten statischen Methoden in C++ 11

Mein erster Versuch:

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; 
} 

Da jedoch abstrakte statische Methoden sind offenbar schwarze Magie verboten Ich habe versucht, zu verwenden, 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); 
} 

Das sieht aus wie sollte es tun, was ich will. Jedoch, wenn ich versuche, es zu verwenden, scheitert es an der Verknüpfungsstufe:

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; 
} 

Wenn ich versuche, dies mit g++ -std=c++11 -o bin -Wall main.cpp managedstring.cpp resourcemanager.cpp zu kompilieren (mit gcc Version 5.3.0) ich diese Fehlermeldung:

/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> >)' 

sollte diese Arbeit? Ist das ein Compiler-Mangel? Oder versuche ich etwas zu tun, was ich nicht tun sollte.

Ich dachte auch über die Änderung meines Designs nach, aber ich denke, es ist nicht so schlimm. Fühlen Sie sich frei, mir diesbezüglich zu widersprechen.

Antwort

1

In resourcemanager.h, diese Zeile:

#include "resourcemanager.h" 

Sollte sein:

#include "resourcemanager.cpp" 

Dies gilt scheint nur für die ersten Beispiel, aber das gleiche gilt für alle anderen auch.
Andernfalls können Sie auch Deklarationen und Definitionen von Vorlagenklassen in dieselbe Datei einfügen.

+0

Wow, ich vermutete, dass es etwas Einfaches sein könnte. Aber ich hätte nicht gedacht, dass es so dumm wäre. Vielen Dank! – Faerbit

+0

@Faerbit Gern geschehen, ich habe noch nicht einmal die volle Antwort gelesen und probiert. Schön, ich bin froh, dass ich hilfreich war. – skypjack

+0

Um zu klären: Das Problem war, dass ich meine Funktionen in der Header-Datei nicht deklarierte. Soweit ich weiß, müssen bei der Verwendung von Templating alle Funktionen in der Header-Datei deklariert werden. – Faerbit

Verwandte Themen