2015-06-28 4 views
0

У меня есть следующие три файла, из которого я не могу найти источник ошибки, что она производит:C++ SFML Gamedev Book - неразрешенный внешний символ из ResourceHolder класса

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, код компилируется нормально; Однако, если я оставлю его там, он дает мне ошибку LNK2019 (неразрешенный внешний символ).

Какова причина этой ошибки и как ее исправить?

+0

Ошибка: LNK2019: неразрешенный внешний символ "public: void __thiscall ResourceHolder :: load (enum Textures :: ID, class std :: basic_string < char, struct std :: char_traits , класс std :: allocator > const &) "(? load @?$ ResourceHolder @ VTexture @ SF @@ W4ID @ текстуры @@@@ QAEXW4ID @ текстуры @@ ABV? $ Basic_string @ DU? $ Char_traits @ D @ станд @@ V? $ Распределитель @ D @ 2 @@ станд @@@ Z), на которые ссылается функция _main – random5999

ответ

0

Вы не можете определить шаблоны внутри .cpp-файлов. Они должны быть определены в заголовке, чтобы компилятор мог видеть реализацию и генерировать определенные классы.

Вот лучший вопрос/ответ, почему это так Why can templates only be implemented in the header file?.

EDIT: Что плохого в get функции

две вещи.

Во-первых, это auto found = mResourcemap.find(id);. Неправильное имя вашей карты, m должно быть в верхнем регистре ->mResourceMap.

Затем линия return *found->second();. Итератор карты содержит пару, а первый и второй члены не являются функциями, а членами данных. Вы должны написать return *found->second;.

Я бы посоветовал вам понять структуры, с которыми вы работаете, прежде чем использовать шаблоны. Ошибки компиляции с шаблонами довольно грязные и труднее читать. Также вы можете создать отдельную тестовую программу и создать диспетчер ресурсов без шаблонов, чтобы легче понять ваши ошибки, а затем создать шаблон поверх своего рабочего менеджера ресурсов.

+0

Это интересно. Я поместил код из CPP в файл H, и теперь код работает нормально, однако я не могу использовать функцию get для установки текстур для спрайтов и т. Д. Знаете ли вы, что может вызвать ошибку компиляции при использовании чего-то вроде " textures.get (текстуры :: Самолет) "? – random5999

+0

@ Cupcake26699 Я отредактировал свой ответ. – aslg

+0

Спасибо. Я изменил все, как вы сказали, и теперь он отлично работает. – random5999

0

Со всеми другими ответами, в которых содержится достаточно информации о том, почему ваш код не компилируется и может быть недействительным, это менеджер ресурсов, который я написал для SFML некоторое время назад, может вам пригодиться:

ГЭС FILE:

#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 FILE:

#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...