1

this is my first post. I am new at game programming and I am using sfml 2.3.2 and visual studio 2015.I use the sfml game developement book. I've done all the work till chapter 3 where I get this error at building.

Severity Code Description Project File Line Error LNK2019 unresolved external symbol "public: class sf::Texture const & __thiscall ResourceHolder::get(enum Textures::ID)const " (?get@?$ResourceHolder@VTexture@sf@@W4ID@Textures@@@@QBEABVTexture@sf@@W4ID@Textures@@@Z) referenced in function "public: __thiscall Character::Character(enum Character::Type,class ResourceHolder const &)" (??0Character@@QAE@W4Type@0@ABV?$ResourceHolder@VTexture@sf@@W4ID@Textures@@@@@Z) SFML1 C:\Users\AdrielJunior\documents\visual studio 2015\Projects\SFML1\SFML1\Character.obj 1

I also put the code

ResourceHolder.h

#pragma once
#include <map>
#include <string>
#include <memory>
#include <stdexcept>
#include <cassert>
#include <SFML/Graphics.hpp>

namespace Textures
{
    enum ID
    {
        Main,
        Enemy,
    };
}

template <typename Resource,typename Identifier>
class ResourceHolder
{
public:
    ResourceHolder();
    ~ResourceHolder();
    void load(Identifier id, const std::string& filename);
    template<typename Parameter>
    void load(Identifier id,const std::string& filename,const Parameter& secondParam);
    void load(Identifier id, const std::string& path,const std::string& filename);
    Resource& get(Identifier id);
    const Resource& get(Identifier id) const;
private:


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

};

typedef sf::Texture texture;

typedef ResourceHolder<texture, Textures::ID> TextureHolder;

ResourceHolder.cpp

#include "ResourceHolder.h"

template<typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string & filename)
{
    std::unique_ptr<Resource> resource(new Resource);
    if (!resource->loadFromFile(filename))
        throw std::runtime_error("ResourceHolder::load - Failed to load " + filename);
    auto inserted = mResourceMap.insert(std::make_pair(id, std::move(resource)));
    assert(inserted.second);

}

template<typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string & path, const std::string & filename)
{
    std::unique_ptr<Resource> resource = new Resource(path);
    if (!resource->Load(filename))
        throw std::runtime_error("ResourceHolder::load - Failed to load " + path+ " " + filename);
    auto inserted = mResourceMap.insert(std::make_pair(id, std::move(resource)));
    assert(inserted.second);
}

template<typename Resource, typename Identifier>
Resource& ResourceHolder<Resource, Identifier>::get(Identifier id)
{
    auto found = mResourceMap.find(id);
    assert(found != mResourceMap.end());
    return *found->second;
    // TODO: insert return statement here
}

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;
    // TODO: insert return statement here
}

template<typename Resource, typename Identifier>
template<typename Parameter>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string & filename, const Parameter & secondParam)
{
    std::unique_ptr<Resource> resource ( new Resource());
    if (!resource->loadFromFile(filename,secondParam))
        throw std::runtime_error("ResourceHolder::load - Failed to load " + filename);
    auto inserted = mResourceMap.insert(std::make_pair(id, std::move(resource)));
    assert(inserted.second);
}

The error occur when I try to call ResourceHolder::get(Identifier Id) in this code.

Character.h

#pragma once
#include "Entity.h"
//#include "ResourceIdentifier.h"
#include "ResourceHolder.h"

class Character: public Entity
{
public:
    enum Type
    {
        Main,
        Enemy,
    };
    explicit Character(Type type,const ResourceHolder<sf::Texture, Textures::ID>& textures);
    virtual void drawCurrent(sf::RenderTarget& target, sf::RenderStates states) const;
private:

private:
    Type mType;
    sf::Sprite mSprite;
};

Character.cpp

#include "Character.h"

Textures::ID toTextureId(Character::Type type)
{
    switch (type)
    {
    case Character::Main:
        return Textures::Main;
        break;
    case Character::Enemy:
        return Textures::Enemy;
        break;

    }
}

Character::Character(Type type, const ResourceHolder<sf::Texture,Textures::ID> & textures):mType(type),
mSprite(textures.get(toTextureId(type)))
{

    sf::FloatRect bounds(mSprite.getLocalBounds());
    mSprite.setOrigin(bounds.width / 2.f, bounds.height / 2.f);
}

void Character::drawCurrent(sf::RenderTarget & target, sf::RenderStates states) const
{
    target.draw(mSprite, states);
}

Can you help me figure this error out? thanks in advance.

Adriel
  • 183
  • 1
  • 2
  • 11
  • 1
    Possible duplicate of [Why can templates only be implemented in the header file?](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Weak to Enuma Elish Nov 24 '15 at 05:15

1 Answers1

0

For template classes, the code for all the member functions needs to be in the header with the class declaration so that the compiler can generate the proper code with the types you're trying to instantiate it with.

(OK, there are ways to separate the class definition and the code but they're not easy.)

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56