C ++ SFML Gamedev Book - ResourceHolder类

时间:2015-06-28 08:23:49

标签: c++ compiler-errors sfml unresolved-external

我有以下三个文件,其中我找不到它产生的错误的来源:

Main.cpp的

#include <SFML/Graphics.hpp>
#include <iostream>

#include "ResourceHolder.h"

namespace Textures
{
    enum ID { Landscape, Airplane, Missile };
}

int main()
{
    //...

    try
    {
        ResourceHolder<sf::Texture, Textures::ID> textures;
        textures.load(Textures::Airplane, "Airplane.png");
    }
    catch (std::runtime_error& e)
    {
        std::cout << "Exception: " << e.what() << std::endl;
    }

    //...
}

ResourceHolder.h

#pragma once

#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);

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

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

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

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 was 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>
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中的try-catch组合,代码编译得很好;但是,如果我把它留在那里它会给我一个LNK2019(未解析的外部符号)错误。

此错误的来源是什么,我该如何解决?

2 个答案:

答案 0 :(得分:0)

您无法在.cpp文件中定义模板。它们必须在头文件中定义,以便编译器可以查看实现并生成特定的类。

这是一个更好的问题/答案,为什么会如此Why can templates only be implemented in the header file?

编辑: get功能错误

两件事。

首先是auto found = mResourcemap.find(id);。您的地图名称不正确,m应为大写 - &gt; mResourceMap

然后是return *found->second();行。 map迭代器包含一对,第一个和第二个成员不是函数,而是数据成员。你应该写return *found->second;

我建议您在使用模板之前了解您正在使用的结构。模板的编译错误非常混乱,难以阅读。您还可以创建一个单独的测试程序,并创建一个没有模板的资源管理器,以便更轻松地理解您的错误,然后在您的工作资源管理器之上构建模板。

答案 1 :(得分:0)

所有其他答案为您提供了足够的信息,说明您的代码为什么不编译并且可能无效,这是我之前为SFML编写的资源管理器,可能对您有用: / p>

HPP文件:

#ifndef RESOURCEMANAGER_HPP
#define RESOURCEMANAGER_HPP

/************ INCLUDES ***********/
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <memory>
#include "SFML/Graphics.hpp"
#include "SFML/Audio.hpp"


class ResourceManager
{
private:
    std::map<std::string,std::unique_ptr<sf::Texture>> listImageContainer;
    std::map<std::string,std::pair<std::unique_ptr<sf::SoundBuffer>,std::unique_ptr<sf::Sound>>> listSoundContainer;
    std::map<std::string,std::unique_ptr<sf::Font>> listFontContainer;

public:
    ResourceManager();
    std::unique_ptr<sf::Sound>& LoadSound(const std::string);
    std::unique_ptr<sf::Font>& LoadFont(const std::string);
    std::unique_ptr<sf::Texture>& LoadImage(const std::string);
    ~ResourceManager();
};
#endif

CPP文件:

#include "ResourceManager.hpp"

ResourceManager::ResourceManager()
{

}

std::unique_ptr<sf::Sound>& ResourceManager::LoadSound(const std::string _fileName)
{
    if (listSoundContainer.find(_fileName) == listSoundContainer.end())
    {
        std::unique_ptr<sf::SoundBuffer> soundBuffer(new sf::SoundBuffer());
        if (soundBuffer->loadFromFile("assets/sound/" + _fileName) != false)
        {
            std::unique_ptr<sf::Sound> sound(new sf::Sound(*soundBuffer));
            listSoundContainer[_fileName] = std::make_pair(std::move(soundBuffer), std::move(sound));
            return listSoundContainer[_fileName].second;
        }
        else
        {
            std::cerr << "Error loading sound..." << std::endl;
        }
    }
    else
    {
        return listSoundContainer[_fileName].second;
    }
}

std::unique_ptr<sf::Font>& ResourceManager::LoadFont(const std::string _fileName)
{
    if (listFontContainer.find(_fileName) == listFontContainer.end())
    {
        std::unique_ptr<sf::Font> font(new sf::Font());
        if (font->loadFromFile("assets/font/" + _fileName)!=false)
        {
            listFontContainer[_fileName] = std::move(font);
            return listFontContainer[_fileName];
        }
        else
        {
            std::cerr << "Error loading font..." << std::endl;
        }
    }
    else
    {
        return listFontContainer[_fileName];
    }
}

std::unique_ptr<sf::Texture>& ResourceManager::LoadImage(const std::string _fileName)
{
    if (listImageContainer.find(_fileName) == listImageContainer.end())
    {
        std::unique_ptr<sf::Texture> texture(new sf::Texture);
        if (texture->loadFromFile("assets/image/" + _fileName)!=false)
        {
            listImageContainer[_fileName] = std::move(texture);
            return listImageContainer[_fileName];
        }
        else
        {
            std::cerr << "Error loading image: " << _fileName << std::endl;
        }
    }
    else
    {
        return listImageContainer[_fileName];
    }
}

ResourceManager::~ResourceManager(){}

使用方法:

ResourceManager resourceManager;
auto& sound = resourceManager.LoadSound("nice.wav");
auto& image = resourceManager.LoadImage("head.png");
auto& sound2 = resourceManager.LoadSound("nice.wav"); //<--- already loaded
sound.play();
etc...