Undefined Reference bei statischer Methode / Template



  • Einen schönen guten Abend miteinander.

    Ich erhalte die genannte Fehlermeldung bei folgendem Code an den Markierten Stellen.
    Was ist das Problem, und wie kann ich es beheben?

    undefined reference to `sf::ResourceManager<sf::Image>::resourcemap'
    
    template <class ResourceType>
    class ResourceManager
    {
    	typedef std::pair<std::string, ResourceType*> Resource;
    	typedef std::unordered_map<std::string, ResourceType*> ResourceMap;
    public:
    	static ResourceType &getResource(std::string path)
    	{
    		if(resourcemap.find(path) == resourcemap.end()) // <---
    			loadResource(path);
    		return *resourcemap[path]; // <---
    	}
    	static std::string loadResource(std::string &path)
    	{
    		ResourceType *resource = new ResourceType();
    		if(!resource->LoadFromFile(path))
    			throw std::runtime_error("Unable to load file: '"+path+"'");
    		addResource(path, resource);
    		return path;
    	}
    	static void unloadResource(std::string &path)
    	{
    		resourcemap.erase(path);
    	}
    	static void addResource(std::string &path, ResourceType *resource)
    	{
    		resourcemap.insert(Resource(path, resource)); // <---
    	}
    	virtual ~ResourceManager();
    private:
    	static ResourceMap resourcemap;
    	static int a;
    	ResourceManager();
    };
    typedef ResourceManager<sf::Image> ImageManager;
    

    Der Aufruf erfolgt über ImageManager::getResource();

    Danke im Voraus,
    Adam S



  • Du musst die statische Variable ausserhalb der Klasse (normalerweise in der .cpp-Datei, bei Templates im Header) definieren.

    Und ich würde für eigene Klassen eher einen anderen Namensraum als sf (für SFML) nehmen, um eine klare Abgrenzung zu haben 😉



  • Ich kann den Lösungsvorschlag leider gerade beim besten Willen nicht umsetzen.
    Wäre es möglich ein Beispiel zu erstellen?



  • Adam S schrieb:

    template <class ResourceType>
    class ResourceManager
    {
    	typedef std::pair<std::string, ResourceType*> Resource;
    	typedef std::unordered_map<std::string, ResourceType*> ResourceMap;
    public:
    	static ResourceType &getResource(std::string path)
    	{
    		if(resourcemap.find(path) == resourcemap.end()) // <---
    			loadResource(path);
    		return *resourcemap[path]; // <---
    	}
    	static std::string loadResource(std::string &path)
    	{
    		ResourceType *resource = new ResourceType();
    		if(!resource->LoadFromFile(path))
    			throw std::runtime_error("Unable to load file: '"+path+"'");
    		addResource(path, resource);
    		return path;
    	}
    	static void unloadResource(std::string &path)
    	{
    		resourcemap.erase(path);
    	}
    	static void addResource(std::string &path, ResourceType *resource)
    	{
    		resourcemap.insert(Resource(path, resource)); // <---
    	}
    	virtual ~ResourceManager();
    private:
    	static ResourceMap resourcemap;
    	static int a;
    	ResourceManager();
    };
    typedef ResourceManager<sf::Image> ImageManager;
    
    // das gehört dazu(genau heir hin):
    ResourceManager::resourcemap = ResourceMap();
    

    (mein problem ist, dass ich die konstruktoren von unordered map nicht kenne, aber du musst da mehr oder weniger einen aufrufen)



  • Ja, und im Fall hier müssen noch die Templateparameter angegeben werden. Also mit template <...> und ResourceManager<...> .



  • Müsste ich also für jede Templatevariante eine eigene Initialisierung erstellen? Das Template ResourceType ist nicht mehr bekannt, wenn ich

    ResourceManager<ResourceType>::resourcemap
    

    angebe. Und auch bei

    template <class ResourceType>
    ResourceManager<ResourceType>::resourcemap = ResourceManager<ResourceType>::ResourceMap();
    

    Erhalte ich folgenden Fehler.

    error: need 'typename' before 'sf::ResourceManager<ResourceType>::resourcemap' because 'sf::ResourceManager<ResourceType>' is a dependent scope
    


  • template <class ResourceType>
    ResourceManager<ResourceType>::resourcemap = ResourceManager<ResourceType>::ResourceMap();
    

    Nach genauerem hinschauen fiel mir auf, dass der TYP von resourcemap bei der Definition fehlt!

    Korrekt müsste es also lauten:

    template <class ResourceType>
    ResourceManager<ResourceType>::ResourceMap ResourceManager<ResourceType>::resourcemap = ResourceManager<ResourceType>::ResourceMap();
    

    Wobei ich mir jetzt nicht 100% sicher bin, ob da noch ein typename davor gehört, schließlich ist es ResourceMap ein abhängiger Typ, aber an bestimmten Stellen darf man dann doch wieder kein typename spezifizieren oder so oder auch nicht und überhaupt. Strg+F7 war da immer viel komfortabler, als mir ein für alle Mal Klarheit zu verschaffen 😉

    Edit #2001: Wobei ich mich aber natürlich frage, warum du dem ein Standard-Konstruiertes Objekt zuweist, kannst du dir doch sparen.



  • Decimad schrieb:

    Wobei ich mir jetzt nicht 100% sicher bin, ob da noch ein typename davor gehört, schließlich ist es ResourceMap ein abhängiger Typ, aber an bestimmten Stellen darf man dann doch wieder kein typename spezifizieren oder so oder auch nicht und überhaupt.

    Ist es nicht einfach so, dass typename genau dann angegeben werden muss, wenn der Typ Member eines Templates ist, das noch nicht vollständig instanziiert wurde (sprich: das von Template-Parametern abhängt)? Sorry, ich kann es gerade nicht besser formulieren.

    // T = Template-Parameter
    typename std::vector<T>::iterator i;
    
    // int = festgelegter, bekannter Typ
    std::vector<int>::iterator i;
    

    Die Begründung macht eigentlich auch Sinn: Solange für T kein konkreter Typ eingesetzt wird, ist nicht klar, ob es sich beim abhängigen Member um einen Typ handelt. Daher typename zur Anweisung an den Compiler.



  • Ja, so wie du das da schreibst, ergibt es auf diabolische Art und Weise Sinn!

    Wobei ich mich jetzt hier nun gerade frage... Wie macht man eigentlich static's in eine template-Klasse? Die könnte ja in tausend Modulen instanziiert werden, mit Typen, die nur diese Module kennen... Warum habe ich mir darüber nie Gedanken gemacht?



  • Decimad schrieb:

    Die könnte ja in tausend Modulen instanziiert werden, mit Typen, die nur diese Module kennen...

    Ja, aber das trifft ja auf alle Templates zu, oder inwiefern sind static -Variablen hier speziell?



  • Naja, static-variablen brauchen ja einen Speicherplatz im ganzen Projekt sozusagen. Den Code für die Templates kann man ja in jedes Übersetzungseinheit basteln und darauf vertrauen, dass der Compiler in jeder Einheit kompatiblen Code erzeugt, sozusagen (da wäre also die Duplizierung nicht schlimm).



  • Also ich habe mal so ein bissl gegoogelt und dann einen Beitrag von jemandem gefunden, der aussagte, dass der Compiler im Falle von statischen Datenmembern in Klassentemplates (welche dann ja im Header auch definiert werden müssen), darauf aufpasst, dass für jede Kombination von Template-Parametern in einem Projekt nur eine Definition erzeugt wird. Da stellt sich mir die Frage, warum tut er das nicht auch bei Nicht-Template-Klassen?^^



  • Decimad schrieb:

    Da stellt sich mir die Frage, warum tut er das nicht auch bei Nicht-Template-Klassen?^^

    Wohl weil der Standard dagegen ist. Vieles wäre wahrscheinlich technisch realisierbar.

    Erlaubt C++0x nicht die Definition beliebiger statischer Member direkt innerhalb der Klasse? (Wobei das auch Nachteile hat, wenn man dadurch Abhängigkeiten in den Header verfrachtet.)



  • Passt jetzt vielleicht nicht 100% hier her, aber...: wie sieht es in C++0x mit inline initialisierten "static const" in einer Klasse aus?
    Laut dem aktuellen Standard muss man die ja nochmal ausserhalb der Klasse definieren. Was aber irgendwie keiner macht, weil es doof ist.
    Also so

    struct foo {
    	static const int c = 1234;
    };
    
    const int foo::c; // <- die Zeile die keiner schreibt, weil sie sowas von umsonst ist
    


  • Funtioniert wie gewohnt.



  • 314159265358979 schrieb:

    Funtioniert wie gewohnt.

    Sinnlose Antwort, wie gewohnt.



  • Wieder mal Löschviecher unterwegs.
    Danke!



  • Macht doch mal Bussi ihr beiden! Hilft doch nix 😉



  • Decimad schrieb:

    Macht doch mal Bussi ihr beiden! Hilft doch nix 😉

    Ich bin ruhig, solange ich nicht provoziert werde.



  • 314159265358979 schrieb:

    Wieder mal Löschviecher unterwegs.
    Danke!

    Ja, du lagst mit Deiner Antwort weit abseits.
    Du hättest analysieren sollen, inwiefern ein static const überhaupt ein Objekt ist, ob es Speicher hat, ob man damit alles machen kann, Funktionen aufrufen, Adressen oder Referenzen ziehen und so.


Log in to reply