Forum de la communauté SFML

Aide => Général => Discussion démarrée par: jonatansan le Avril 29, 2014, 08:36:00 pm

Titre: SFML Game Development Book - Chapitre 2 - Error LNK2001 [RÉSOLU]
Posté par: jonatansan le Avril 29, 2014, 08:36:00 pm
Bonjour !

Je suis en plein apprentissage de la SFML grâce au livre qui lui est dédié, mais je rencontre un problème après avoir terminé le deuxième chapitre. En effet, je n'arrive pas à compiler le code donné sur github( https://github.com/SFML/SFML-Game-Development-Book/tree/master/02_Resources).

L'erreur est :
1>main.obj : error LNK2001: symbole externe non résolu "public: class sf::Texture & __thiscall ResourceHolder<class sf::Texture,enum Textures::ID>::get(enum Textures::ID)" (?get@?$ResourceHolder@VTexture@sf@@W4ID@Textures@@@@QAEAAVTexture@sf@@W4ID@Textures@@@Z)
1>main.obj : error LNK2001: symbole externe non résolu "public: void __thiscall ResourceHolder<class sf::Texture,enum Textures::ID>::load(enum Textures::ID,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?load@?$ResourceHolder@VTexture@sf@@W4ID@Textures@@@@QAEXW4ID@Textures@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z)
1>C:\...\Projects\SFML-Chap2\Release\Projet2.exe : fatal error LNK1120: 2 externes non résolus
========== Génération : 0 a réussi, 1 a échoué, 0 mis à jour, 0 a été ignoré ==========


Après recherche sur le net, il semblerait que mon compilateur considère que les fonctions "ResourceHolder::load(Identifier id, const std::string& filename)" et "ResourceHolder<Resource, Identifier>::get(Identifier id)" soient déclarées, mais non implémentées.. Or, elles le sont bel et bien dans mon code source.  :o

Voici mes trois fichiers, à savoir ResourceHolder.h, ResourceHolder.cpp et le main.cpp (la seule différence avec ceux sur github sont les #includes ) :

ResourceHolder.h
#ifndef BOOK_RESOURCEHOLDER_HPP
#define BOOK_RESOURCEHOLDER_HPP

#include <map>
#include <string>
#include <memory>
#include <stdexcept>
#include <cassert>

template <typename Resource, typename Identifier>
class ResourceHolder
{
        public:
                void                                            load(Identifier id, const std::string& filename);

                template <typename Parameter>
                void                                            load(Identifier id, const std::string& filename, const Parameter& secondParam);

                Resource&                                       get(Identifier id);
                const Resource&                         get(Identifier id) const;


        private:
                void                                            insertResource(Identifier id, std::unique_ptr<Resource> resource);


        private:
                std::map<Identifier, std::unique_ptr<Resource>> mResourceMap;
};

#endif // BOOK_RESOURCEHOLDER_HPP

ResourceHolder.cpp
#include "ResourceHolder.h"

template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string& filename)
{
        // Create and load resource
        std::unique_ptr<Resource> resource(new Resource());
        if (!resource->loadFromFile(filename))
                throw std::runtime_error("ResourceHolder::load - Failed to load " + filename);

        // If loading successful, insert resource to map
        insertResource(id, std::move(resource));
}

template <typename Resource, typename Identifier>
template <typename Parameter>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string& filename, const Parameter& secondParam)
{
        // Create and load resource
        std::unique_ptr<Resource> resource(new Resource());
        if (!resource->loadFromFile(filename, secondParam))
                throw std::runtime_error("ResourceHolder::load - Failed to load " + filename);

        // If loading successful, insert resource to map
        insertResource(id, std::move(resource));
}

template <typename Resource, typename Identifier>
Resource& ResourceHolder<Resource, Identifier>::get(Identifier id)
{
        auto found = mResourceMap.find(id);
        assert(found != mResourceMap.end());

        return *found->second;
}

template <typename Resource, typename Identifier>
const Resource& ResourceHolder<Resource, Identifier>::get(Identifier id) const
{
        auto found = mResourceMap.find(id);
        assert(found != mResourceMap.end());

        return *found->second;
}

template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::insertResource(Identifier id, std::unique_ptr<Resource> resource)
{
        // Insert and check success
        auto inserted = mResourceMap.insert(std::make_pair(id, std::move(resource)));
        assert(inserted.second);
}

main.cpp
#include <SFML/Graphics.hpp>
#include <iostream>
#include "ResourceHolder.h"

// Resource ID for sf::Texture
namespace Textures
{
        enum ID
        {
                Landscape,
                Airplane,
        };
}

int main()
{
        sf::RenderWindow window(sf::VideoMode(640, 480), "Resources");
        window.setFramerateLimit(20);

        // Try to load resources
        ResourceHolder<sf::Texture, Textures::ID> textures;
        try
        {
                textures.load(Textures::Landscape, "../Media/Image/Desert.png");
                textures.load(Textures::Airplane, "../Media/Image/Eagle.png");
        }
        catch (std::runtime_error& e)
        {
                std::cout << "Exception: " << e.what() << std::endl;
                return 1;
        }

        // Access resources
        sf::Sprite landscape(textures.get(Textures::Landscape));
        sf::Sprite airplane(textures.get(Textures::Airplane));
        airplane.setPosition(200.f, 200.f);

        while (window.isOpen())
        {
                sf::Event event;
                while (window.pollEvent(event))
                {
                        if (event.type == sf::Event::KeyPressed || event.type == sf::Event::Closed)
                                return 0;
                }

                window.clear();
                window.draw(landscape);
                window.draw(airplane);
                window.display();
        }
}

J'utilise Visual Studio Professional 2012 et tout fonctionnait parfaitement pour le chapitre 1. Alors, si quelqu'un pouvait m'aider, je lui en serais très reconnaissant !

Merci,
Titre: Re : SFML Game Development Book - Chapitre 2 - Error LNK2001 ?
Posté par: Laurent le Avril 29, 2014, 08:55:14 pm
Si tu compilais le code qui se trouve au bout du lien que tu donnes, ça fonctionnerait. Ton code est différent, et c'est cette différence qui provoque l'erreur.
Titre: Re : SFML Game Development Book - Chapitre 2 - Error LNK2001 ?
Posté par: PtichapronRouge le Mai 01, 2014, 12:10:35 pm
Le livre explique que tes fonctions templates doivent etre implementées dans un fichier .inl et non pas dans un fichier .cpp. De plus, tu ne dois pas inclure le header dans le .inl mais le .inl dans le header.
En gros ton header est de cette forme :

#ifndef RESOURCEHOLDER_HPP
#define RESOURCEHOLDER_HPP
<template<typename T, typename P>
class ResourceHolder
{
//attributs et methodes*
};

#include "ResourceHolder.inl"

#endif // ResourceHolder.hpp
 
Titre: Re : SFML Game Development Book - Chapitre 2 - Error LNK2001 ?
Posté par: PtichapronRouge le Mai 01, 2014, 12:11:52 pm
Et dans le .inl contenant les implementations, tu ne mets pas
#include "ResourceHolder.hpp
ou le compilo t'insulteras ;)
Titre: Re : SFML Game Development Book - Chapitre 2 - Error LNK2001 ?
Posté par: jonatansan le Mai 05, 2014, 08:39:24 pm
D'accord, merci pour les réponses !  :)

J'ai effectué les modifications et ai obtenu une tonne d'erreurs de compilation, mais il se trouve que la solution était ici : http://stackoverflow.com/questions/17876510/visual-studio-wont-compile-template-class-with-inl-implementation (Je partage, si jamais quelqu'un d'autres à le même type de problème que moi)