• Advertisement

Recommended Posts

Im trying to wrap my head around Entity Component Systems and was implementing my own based on others examples. I have a very basic stripped down version working without systems but as ive started to implement the systems im getting a linker error complaining about unresolved externals.

1>InputSystem.obj : error LNK2019: unresolved external symbol "public: class VelocityComponent * __thiscall Entity::getComponent<class VelocityComponent>(void)const " (??$getComponent@VVelocityComponent@@@Entity@@QBEPAVVelocityComponent@@XZ) referenced in function "public: virtual void __thiscall InputSystem::update(float)" (?update@InputSystem@@UAEXM@Z)

As i understand its complaining that in InputSystem::update() it cannot find the implementation of Entity::getComponent<T>(). InputSystem inherits from system which includes Entity.h.

If i move the implementation of getComponent<T>() from Entity.cpp into Entity.h then everything compiles fine. I thought when including a .h the associated .cpp is compiled along with it into an object file then the linker grabs all that to generate the final executable.

Please help me to understand why this is failing.

Edited by Mr_Mauve

Share this post


Link to post
Share on other sites
Advertisement

Templates behave differently than the standard header/cpp file dichotomy.  There are lots of references online; you should be able to find comprehensive info that explains why you need to have the implementation #included.

Share this post


Link to post
Share on other sites
1 hour ago, Mr_Mauve said:

If i move the implementation of getComponent<T>() from Entity.cpp into Entity.h then everything compiles fine. I thought when including a .h the associated .cpp is compiled along with it into an object file then the linker grabs all that to generate the final executable.

Not exactly.  See also How does the compilation/linking process work? and “Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?”.

Headers declare existence of symbols, not their actual definition.  Some of examples of such declarations are:

  • Global variable `extern int my_global_int`
  • Global function `bool is_equal(int,int,int)`
  • Class methods `void Foobar::frobnicate(int)`.

Source files provide the definitions of symbols:

  • `int my_global_int = 123;`
  • `bool is_equal(int a, int b, int c) { return a == b && a == c; }`
  • `Foobar::frobnicate(int x) { std::cout << "this=" << this << "x=" << x; }`

The source files are compiled into object code.  Object code is assembly instructions or data with associated symbols.  Headers allow others to know of these symbols’ existence, without having to compile those symbols themselves.  It’s the job of the linker to find all of those external symbols and find their definitions, be it in object code you compiled yourself, in a static library provided elsewhere, or even in a dynamic library.

Templates are a different kind of beast.  They are more code–generating code than they are actual code.  Their symbols and definitions aren’t known until it is used.  For example:

// is_equal.hpp:
template<typename T>
bool is_equal(T a, T b, T c);


// is_equal.cpp:
template<typename T>
bool is_equal(T a, T b, T c) {
    return a == b && a == c;
}

// explicitly instantiate code for `int` and `float`
template<> is_equal<int>(int,int,int);
template<> is_equal<float>(float,float,float);


// main.cpp:
#include "is_equal.hpp"
  
int main() {
  is_equal<int>(1,1,1);           // ok
  is_equal<float>(1.f,1.f,1.f);   // ok
  is_equal<double>(1.0,1.0,1.0);  // Unresolved external symbol
}

 

Unless the definition of `is_equal` is known upfront, then it cannot be used, unless the object code for those types are provided elsewhere.

Share this post


Link to post
Share on other sites

Thank you fastcall22 , the second link was just what i needed.

Because the implementation was in a separate .cpp and i didn't explicitly instantiate the template functions with their types then when the linker went to link the symbol for getComponent<VelocityComponent> it couldn't because its definition was never generated.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By mangine
      Hello. I am developing a civ 6 clone set in space and I have a few issues. I am using Lua for the logic and UI of the game and c++ directx 12 for the graphics. I need a way to send information between Lua and c++ occasionally and was wondering what is the best and most flexible (and hopefully fast) way to do this. Don't forget that I also need to send things from c++ back to Lua. I know of a lua extension called "LuaBridge" on github but it is a little old and I am worried that it will not work with directx 12. Has anybody done something similar and knows a good method of sending data back and forth? I am aware that Lua is used more and more in the industry and surely plenty of AAA game programmers know the answer to this. I want a good solution that will hopefully still be viable code in a couple of years...
    • By owenjr
      Hi there.
      I'm pretty new to this and I don't know if it has been asked before, but here I go.
      I'm developing a game using SFML and C++.
      I would like to use the "Tiled" tool to load maps into my game but I don't actually find any tutorial or guide on how to exaclty use it (I know that I have to read an XML file and stuff). I just step into diverse projects that make all a mess. 
      Anyone knows where can I find good information to make my map loader by myself?
      Thanks in advantage!!
    • By MHG OstryTM
      Hello guys,
      I've released my game for the first time. I'm very excited about it and I hope you'll enjoy the game - Beer Ranger. It's a retro-like puzzle-platfromer which makes you think a lot or die trying. You have a squad of skilled dwarfs with special powers and your goal is tasty beer. There is a lot of traps as well as many solutions how to endure them - it is up to your choice how to complete the level! 
      Link to the project: Project site
      Link to the Steam site with video: Beer Ranger
      Have fun and please write feedback if you feel up to.
      Some screens: 




    • By francoisdiy
      So I wrote a programming language called C-Lesh to program games for my game maker Platformisis. It is a scripting language which tiles into the JavaScript game engine via a memory mapper using memory mapped I/O. Currently, I am porting the language as a standalone interpreter to be able to run on the PC and possibly other devices excluding the phone. The interpreter is being written in C++ so for those of you who are C++ fans you can see the different components implemented. Some background of the language and how to program in C-Lesh can be found here:

      http://www.codeloader.net/readme.html
      As I program this thing I will post code from different components and explain.
    • By isu diss
      I'm trying to duplicate vertices using std::map to be used in a vertex buffer. I don't get the correct index buffer(myInds) or vertex buffer(myVerts). I can get the index array from FBX but it differs from what I get in the following std::map code. Any help is much appreciated.
      struct FBXVTX { XMFLOAT3 Position; XMFLOAT2 TextureCoord; XMFLOAT3 Normal; }; std::map< FBXVTX, int > myVertsMap; std::vector<FBXVTX> myVerts; std::vector<int> myInds; HRESULT FBXLoader::Open(HWND hWnd, char* Filename, bool UsePositionOnly) { HRESULT hr = S_OK; if (FBXM) { FBXIOS = FbxIOSettings::Create(FBXM, IOSROOT); FBXM->SetIOSettings(FBXIOS); FBXI = FbxImporter::Create(FBXM, ""); if (!(FBXI->Initialize(Filename, -1, FBXIOS))) { hr = E_FAIL; MessageBox(hWnd, (wchar_t*)FBXI->GetStatus().GetErrorString(), TEXT("ALM"), MB_OK); } FBXS = FbxScene::Create(FBXM, "REALMS"); if (!FBXS) { hr = E_FAIL; MessageBox(hWnd, TEXT("Failed to create the scene"), TEXT("ALM"), MB_OK); } if (!(FBXI->Import(FBXS))) { hr = E_FAIL; MessageBox(hWnd, TEXT("Failed to import fbx file content into the scene"), TEXT("ALM"), MB_OK); } FbxAxisSystem OurAxisSystem = FbxAxisSystem::DirectX; FbxAxisSystem SceneAxisSystem = FBXS->GetGlobalSettings().GetAxisSystem(); if(SceneAxisSystem != OurAxisSystem) { FbxAxisSystem::DirectX.ConvertScene(FBXS); } FbxSystemUnit SceneSystemUnit = FBXS->GetGlobalSettings().GetSystemUnit(); if( SceneSystemUnit.GetScaleFactor() != 1.0 ) { FbxSystemUnit::cm.ConvertScene( FBXS ); } if (FBXI) FBXI->Destroy(); FbxNode* MainNode = FBXS->GetRootNode(); int NumKids = MainNode->GetChildCount(); FbxNode* ChildNode = NULL; for (int i=0; i<NumKids; i++) { ChildNode = MainNode->GetChild(i); FbxNodeAttribute* NodeAttribute = ChildNode->GetNodeAttribute(); if (NodeAttribute->GetAttributeType() == FbxNodeAttribute::eMesh) { FbxMesh* Mesh = ChildNode->GetMesh(); if (UsePositionOnly) { NumVertices = Mesh->GetControlPointsCount();//number of vertices MyV = new XMFLOAT3[NumVertices]; for (DWORD j = 0; j < NumVertices; j++) { FbxVector4 Vertex = Mesh->GetControlPointAt(j);//Gets the control point at the specified index. MyV[j] = XMFLOAT3((float)Vertex.mData[0], (float)Vertex.mData[1], (float)Vertex.mData[2]); } NumIndices = Mesh->GetPolygonVertexCount();//number of indices MyI = (DWORD*)Mesh->GetPolygonVertices();//index array } else { FbxLayerElementArrayTemplate<FbxVector2>* uvVertices = NULL; Mesh->GetTextureUV(&uvVertices); int idx = 0; for (int i = 0; i < Mesh->GetPolygonCount(); i++)//polygon(=mostly triangle) count { for (int j = 0; j < Mesh->GetPolygonSize(i); j++)//retrieves number of vertices in a polygon { FBXVTX myVert; int p_index = 3*i+j; int t_index = Mesh->GetTextureUVIndex(i, j); FbxVector4 Vertex = Mesh->GetControlPointAt(p_index);//Gets the control point at the specified index. myVert.Position = XMFLOAT3((float)Vertex.mData[0], (float)Vertex.mData[1], (float)Vertex.mData[2]); FbxVector4 Normal; Mesh->GetPolygonVertexNormal(i, j, Normal); myVert.Normal = XMFLOAT3((float)Normal.mData[0], (float)Normal.mData[1], (float)Normal.mData[2]); FbxVector2 uv = uvVertices->GetAt(t_index); myVert.TextureCoord = XMFLOAT2((float)uv.mData[0], (float)uv.mData[1]); if ( myVertsMap.find( myVert ) != myVertsMap.end() ) myInds.push_back( myVertsMap[ myVert ]); else { myVertsMap.insert( std::pair<FBXVTX, int> (myVert, idx ) ); myVerts.push_back(myVert); myInds.push_back(idx); idx++; } } } } } } } else { hr = E_FAIL; MessageBox(hWnd, TEXT("Failed to create the FBX Manager"), TEXT("ALM"), MB_OK); } return hr; } bool operator < ( const FBXVTX &lValue, const FBXVTX &rValue) { if (lValue.Position.x != rValue.Position.x) return(lValue.Position.x < rValue.Position.x); if (lValue.Position.y != rValue.Position.y) return(lValue.Position.y < rValue.Position.y); if (lValue.Position.z != rValue.Position.z) return(lValue.Position.z < rValue.Position.z); if (lValue.TextureCoord.x != rValue.TextureCoord.x) return(lValue.TextureCoord.x < rValue.TextureCoord.x); if (lValue.TextureCoord.y != rValue.TextureCoord.y) return(lValue.TextureCoord.y < rValue.TextureCoord.y); if (lValue.Normal.x != rValue.Normal.x) return(lValue.Normal.x < rValue.Normal.x); if (lValue.Normal.y != rValue.Normal.y) return(lValue.Normal.y < rValue.Normal.y); return(lValue.Normal.z < rValue.Normal.z); }  
  • Advertisement