Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unresolved external symbol with template function

I searched around the web but I haven't found an answer yet, as to why I get this error:

Error 1 error LNK2019: unresolved external symbol "public: class Mesh * __thiscall AssetManager::GetAsset(class std::basic_string,class std::allocator >)" (??$GetAsset@PAVMesh@@@AssetManager@@QAEPAVMesh@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function "public: void __thiscall SceneManager::AddMesh(class std::basic_string,class std::allocator >)" (?AddMesh@SceneManager@@QAEXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) C:\Users\Dirk\documents\visual studio 2010\Projects\OpenGameEngine\OpenGameEngine\SceneManager.obj

Here is my code:

AssetManager.h

#pragma once
#include <string>
#include <map>
#include "Asset.h"
#include "Mesh.h"

using namespace std;

class AssetManager
{
    public:
        AssetManager(string rootFolder);
        bool LoadAsset(string assetName, int assetType, string assetFile, bool subDirectory);
        void UnloadAsset(string assetName);
        template <class T> T GetAsset(string assetName);
        bool AddAssetSubDirectory(int assetType, string subDirectory);  

    private:
        string m_rootFolder;
        map<int, string> m_assetSubs;
        map<string, Asset*> m_assets;
};

AssetManager.cpp

#include "AssetManager.h"

AssetManager::AssetManager(string rootFolder)
{
    m_rootFolder = rootFolder;
}

bool AssetManager::AddAssetSubDirectory(int assetType, string subDirectory)
{
    if (m_assetSubs.find(assetType) == m_assetSubs.end())
    {
        m_assetSubs[assetType] = subDirectory;
        return true;
    }
    else
    {
        return false;
    }
}

bool AssetManager::LoadAsset(string assetName, int type, string assetFile, bool subDirectory)
{
    string filePos;
    if (subDirectory)
    {
        filePos = m_rootFolder.append(m_assetSubs[type]).append(assetFile);
    }
    else
    {
        filePos = m_rootFolder.append(assetFile);
    }
    return true;
}

void AssetManager::UnloadAsset(string assetName)
{
    if (m_assets.find(assetName) != m_assets.end())
    {
        m_assets.erase(assetName);
    }
}

template <class T> T AssetManager::GetAsset(string assetName)
{
    if (m_assets.find(assetName) != m_assets.end())
    {
        return m_assets[assetName];
    }
    else
    {
        return null;
    }
}

SceneManager.h

#pragma once
#include <string>
#include <map>
#include "AssetManager.h"

using namespace std;

class SceneManager
{
    public:
    static SceneManager* Instance();
    void AddMesh(string assetName);
    void RemoveMesh(string assetName);
    void Draw();
    void Run();
    void SetAssetManager(AssetManager*);
    void Destroy();

    private:
    SceneManager();
    SceneManager(SceneManager const&);
    ~SceneManager();
    SceneManager& operator=(SceneManager const&){};
    static SceneManager* m_Instance;
    AssetManager *m_assetMgr;

    private:
    map<string, Mesh*> m_staticMeshes;
};

SceneManager.cpp

#include "SceneManager.h"
#include "AssetManager.h"

SceneManager* SceneManager::m_Instance = NULL;

SceneManager::SceneManager()
{
    m_assetMgr = 0;
}

SceneManager::SceneManager(SceneManager const&)
{

}

SceneManager::~SceneManager()
{
    delete m_assetMgr;
    m_assetMgr = 0;
}

void SceneManager::Destroy()
{
    delete m_Instance;
    m_Instance = 0;
}

SceneManager* SceneManager::Instance()
{
    if (!m_Instance)
        m_Instance = new SceneManager();

    return m_Instance;
}

void SceneManager::SetAssetManager(AssetManager *am)
{
    m_assetMgr = am; 
}

void SceneManager::AddMesh(string assetName)
{
    m_assetMgr->GetAsset<Mesh*>(assetName);
}

void SceneManager::RemoveMesh(string assetName)
{
    if (m_staticMeshes.find(assetName) != m_staticMeshes.end())
    {
        m_staticMeshes.erase(assetName);
    }
}

void SceneManager::Draw()
{
    for (map<string, Mesh*>::Iterator it = m_staticMeshes.begin(); it != m_staticMeshes.end(); ++it)
    {
        it->second->Draw();
    }
}

void SceneManager::Run()
{

}

Thanks in advance for the responses!

like image 938
Wouter Standaert Avatar asked Apr 27 '14 18:04

Wouter Standaert


People also ask

How do I fix unresolved external symbol?

So when we try to assign it a value in the main function, the linker doesn't find the symbol and may result in an “unresolved external symbol” or “undefined reference”. The way to fix this error is to explicitly scope the variable using '::' outside the main before using it.

What is unresolved external symbol?

The compiler can identify when a symbol isn't declared, but it can't tell when the symbol isn't defined. It's because the definition may be in a different source file or library. If a symbol is referred to but never defined, the linker generates an unresolved external symbol error.


2 Answers

C++ does not allow you to declare a template in a header file and define it in a .cpp file. The reason is that templates can only be created when the template parameters are known and so they can't be complied in advance.

To solve your problem, you will need to declare and define template <class T> T GetAsset(string assetName) in the AssetManager.h file

like image 75
Caesar Avatar answered Oct 12 '22 23:10

Caesar


Template methods must be implemented in header files, not in CPP.

Templates are just a kind of "macro". When you use the method GetAsset<Mesh*> in your SceneMagager.cpp file, the C++ compiler searches in that compilation unit the source code for GetAsset() in order to substitute the T typename with Mesh and compile the new method (create on the fly with that substituion). But SceneManager.cpp only knows about AssetManager.h (not the .cpp where GetAsset<T> is implemented) so the real code is not available and the compilation fails.

Just move your AssetManager::GetAsset implementation from the .cpp file to the .h and it should work.

like image 22
Googol Avatar answered Oct 12 '22 23:10

Googol