Problems with templates in C++

Started by
6 comments, last by phayer 11 years, 9 months ago
Okey, help please - can't figure out how to fix this;

Here is Entity.cpp
[source lang="cpp"]#if !defined OP_ENTITY
#define OP_ENTITY

#include "OPEngine.h"

class Entity
{
public:
void initialize();

bool attachComponent(Component* compPtr);

template<class T>
void addAttribute(std::string name);

void handleMessage(Message* msg);

template<class T>
Attribute<T>* getAttribute(std::string name);

template<class T>
T* getAttributeDataPointer(std::string name);


private:

protected:
sf::Vector2f m_Position;
float m_Rotation;
std::map<uint, Component*> m_Components;
std::map<uint, void*> m_Attributes;
uint m_EntityName;

};

#endif[/source]

Here is the cpp file
[source lang="cpp"]#include "Entity.h"

// Public
bool Entity::attachComponent(Component* compPtr)
{
uint hashName = compPtr->getNameHash();
if(this->m_Components.find(hashName) != this->m_Components.end())
{
WARN("Tried to attach component but a component with the same name was already attached");
return false;
}
else
{
std::pair<uint, Component*> componentPair(hashName, compPtr);
this->m_Attributes.insert(componentPair);
LOG("Component successfully attached to entity");
return true;
}
}

template<class T>
void Entity::addAttribute(std::string name)
{
void* ptr = new Attribute<T>();
uint hashname = System::hash(name);
std::pair<std::string, void*> pair(hashname, ptr);
this->m_Attributes.insert(pair);

/*
if(this->m_Attributes.find(hashName) != this->m_Attributes.end())
{
WARN("Tried to register attribute but name was already taken");
return false;
}
else
{
void* attrPtr = new Attribute<T>();
std::pair<uint, void*> attributePair(hashName, attrPtr);
this->m_Attributes.insert(attributePair);
return true;
}
*/
}

template<class T>
Attribute<T>* Entity::getAttribute(std::string name)
{
uint nameHash = System::hash(name);

std::map<uint, void*>::iterator it;
for(it = this->m_Attributes.begin(); it != this->m_Attributes.end(); it++)
{
if((*it).first == nameHash)
//return static_cast<T*>((*it).second);
return static_cast<Attribute<T>*>((*it).second);
}

return NULL;
}

template<class T>
T* Entity::getAttributeDataPointer(std::string name)
{
Attribute<T>* temp = this->getAttribute<T>(name);
return temp->GetDataPointer();
//return this->getAttribute(name)->GetDataPointer();
}

// Private

// Protected[/source]

And this is the line that provokes the error
[source lang="cpp"]Entity e;
e.addAttribute<float>("TEST");[/source]

And at last the error;
[source lang="java"]1>------ Build started: Project: ComponentBase, Configuration: Debug Win32 ------
1> main.cpp
1> Creating library i:\Cpp Projects\ComponentBase\Debug\ComponentBase.lib and object i:\Cpp Projects\ComponentBase\Debug\ComponentBase.exp
1>main.obj : error LNK2019: unresolved external symbol "public: void __thiscall Entity::addAttribute<float>(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??$addAttribute@M@Entity@@QAEXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function _main
1>i:\Cpp Projects\ComponentBase\Debug\ComponentBase.exe : fatal error LNK1120: 1 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
[/source]

What does this error mean? And how can I fix it?
Advertisement
http://www.parashift.com/c++-faq-lite/templates.html#faq-35.13

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Sorry for being such a dumb ass, but I can't figure out where my code is equal to that in the logical way. Could you please point out in my code / logic where my error is?
The link states that you cannot, under usual circumstances, separate template definitions and declarations. The full definition of the template (which for functions mean the whole function body) has to be in the header file. You have separated the definition into a separate source file, and you cannot do that.
So normal practice is if the method or class uses a template it must be in the header?

'Cause I moved the getAttribute to the Attribute.h but still no luck.
That is the normal template usage, yes. However, addAttribute and getAttributeDataPointer are templates also and have to be in the header.
Okey, now I got

Attribute.h
[source lang="cpp"]#if !defined OP_ATTRIBUTE
#define OP_ATTRIBUTE

#include "OPEngine.h"

template<class T>
class Attribute
{
public:
Attribute()
{
this->m_Data = new T;
}

T getData()
{
return *this->m_Data;
}

T* getDataPointer()
{
return &this->m_Data;
}

void setData(T data)
{
*this->m_Data = data;
}

private:

protected:
T* m_Data;

};

#endif[/source]

Entity.h
[source lang="cpp"]#if !defined OP_ENTITY
#define OP_ENTITY

#include "OPEngine.h"

class Entity
{
public:
void initialize();

bool attachComponent(Component* compPtr);

template<class T>
void addAttribute(std::string name)
{
if(this->m_Attributes.find(hashName) != this->m_Attributes.end())
{
WARN("Tried to register attribute but name was already taken");
return false;
}
else
{
void* attrPtr = new Attribute<T>();
std::pair<uint, void*> attributePair(hashName, attrPtr);
this->m_Attributes.insert(attributePair);
return true;
}
}

void handleMessage(Message* msg);

template<class T>
Attribute<T>* getAttribute(std::string name)
{
uint nameHash = System::hash(name);

std::map<uint, void*>::iterator it;
for(it = this->m_Attributes.begin(); it != this->m_Attributes.end(); it++)
{
if((*it).first == nameHash)
//return static_cast<T*>((*it).second);
return static_cast<Attribute<T>*>((*it).second);
}

return NULL;
}

template<class T>
T* getAttributeDataPointer(std::string name)
{
Attribute<T>* temp = this->getAttribute<T>(name);
return temp->GetDataPointer();
//return this->getAttribute(name)->GetDataPointer();
}


private:

protected:
sf::Vector2f m_Position;
float m_Rotation;
std::map<uint, Component*> m_Components;
std::map<uint, void*> m_Attributes;
uint m_EntityName;

};

#endif[/source]

Entity.cpp
[source lang="cpp"]#include "Entity.h"

// Public
bool Entity::attachComponent(Component* compPtr)
{
uint hashName = compPtr->getNameHash();
if(this->m_Components.find(hashName) != this->m_Components.end())
{
WARN("Tried to attach component but a component with the same name was already attached");
return false;
}
else
{
std::pair<uint, Component*> componentPair(hashName, compPtr);
this->m_Attributes.insert(componentPair);
LOG("Component successfully attached to entity");
return true;
}
}[/source]

And get the error
[source lang="cpp"]1>------ Build started: Project: ComponentBase, Configuration: Debug Win32 ------
1> Entity.cpp
1> Generating Code...
1> Skipping... (no relevant changes detected)
1> main.cpp
1> Component.cpp
1> Creating library i:\Cpp Projects\ComponentBase\Debug\ComponentBase.lib and object i:\Cpp Projects\ComponentBase\Debug\ComponentBase.exp
1>main.obj : error LNK2019: unresolved external symbol "public: void __thiscall Entity::addAttribute<float>(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??$addAttribute@M@Entity@@QAEXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function _main
1>i:\Cpp Projects\ComponentBase\Debug\ComponentBase.exe : fatal error LNK1120: 1 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
[/source]
Can't seem to edit my post: anyway, got it working now. Thanks! And I see now after reading the FAQ, linked over, for the 4th time, what I was doing wrong.

This topic is closed to new replies.

Advertisement