Undefined reference to function

Hello !
I'm doing a project for my Bachelor's Degree (which is an implementation of a simple Entity Component System, but that's not the point of my topic :p), but I have an error and I don't understand why. I'm getting the error "Undefined reference to `void MyClass::MyFunction<T>()`", I don't understand why since my function is well defined and the implementation file of the class is compiled...

Here's the header and the cpp file of the class who give me this problem :

Manager.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#pragma once

#include "ECS.h"
#include "ComponentManager.h"
#include "SystemManager.h"

class Manager {
    public:
        static Manager& get() {
            static Manager m;
            return m;
        }

        Entity createEntity();

        void destroyEntity(Entity e);

        void setSignature(Entity e, Signature s);

        Signature& getSignature(Entity e);

        SignatureArray& getSignatureArray();

        template<typename T>
        T& addComponent(Entity e, T& comp);

        template<typename T, typename... Args>
        T& createComponent(Entity e, Args&&... args);

        template<typename T>
        std::shared_ptr<ComponentArray<T>> registerComponentArray();

        template<typename T>
        T& getComponent(Entity e);

        template<typename T>
        void registerSystem();

        void update();

    private:
        Manager();

        std::queue<Entity> entities;
        Entity entitiesAlive{0};

        SignatureArray entitySignatures;

        std::unique_ptr<ComponentManager> components;
        std::unique_ptr<SystemManager> systems;
};


Manager.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include "Manager.h"

Manager::Manager() {
    std::cout << "8\n";
    components = std::make_unique<ComponentManager>();
    systems = std::make_unique<SystemManager>();
    for(Entity e = 0 ; e < MAX_ENTITIES ; e++) {
        entities.push(e);
    }
}

Entity Manager::createEntity() {
    assert(entitiesAlive < MAX_ENTITIES &&
     "Too many entities.");

    Entity id = entities.front();
    entities.pop();

    entitiesAlive++;
    return id;
}

void Manager::destroyEntity(Entity e) {
    assert(e < MAX_ENTITIES &&
     "Out of range.");

    entitySignatures[e].reset();

    entities.push(e);
    entitiesAlive--;
}

void Manager::setSignature(Entity e, Signature s) {
    assert(e < MAX_ENTITIES &&
     "Entity out of range.");

    entitySignatures[e] = s;
}

Signature& Manager::getSignature(Entity e) {
    assert(e < MAX_ENTITIES &&
     "Entity out of range.");

    return entitySignatures[e];
}

SignatureArray& Manager::getSignatureArray() {
    return entitySignatures;
}

template<typename T>
T& Manager::addComponent(Entity e, T& comp) {
    entitySignatures[e][getComponentTypeID<T>()] = true;
    return components->addComponent<T>(e,comp);
}

template<typename T, typename... Args>
T& Manager::createComponent(Entity e, Args&&... args) {
    entitySignatures[e][getComponentTypeID<T>()] = true;
    return components->createComponent<T>(e,args...);
}

template<typename T>
std::shared_ptr<ComponentArray<T>> Manager::registerComponentArray() {
    return components->registerComponentArray<T>();
}

template<typename T>
T& Manager::getComponent(Entity e) {
    return components->getComponent<T>(e);
}

template<typename T>
void Manager::registerSystem() {
    systems->registerSystem<T>();            
}

void Manager::update() {
    std::cout << "9\n";
    systems->update();
}


Also here's the Makefile of my project, if needed :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CXXFLAGS = -std=c++17 -Wall -Wextra
LDFLAGS = -Llib -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -lSDL2_ttf
INCFLAGS = -Iinclude -IECS/include -IEngine/include -IComponents/include -ISystems/include

bin/main: obj/main.o obj/Engine.o obj/AssetsLoader.o obj/Manager.o obj/icon.res
	g++ $(CXXFLAGS) -o bin/main obj/main.o obj/Engine.o obj/AssetsLoader.o obj/Manager.o obj/icon.res $(INCFLAGS) $(LDFLAGS)

obj/main.o: main.cpp 
	g++ $(CXXFLAGS) -o obj/main.o -c main.cpp $(INCFLAGS) $(LDFLAGS)

obj/Engine.o: Engine/src/Engine.cpp
	g++ $(CXXFLAGS) -o obj/Engine.o -c Engine/src/Engine.cpp $(INCFLAGS) $(LDFLAGS)

obj/AssetsLoader.o: Engine/src/AssetsLoader.cpp
	g++ $(CXXFLAGS) -o obj/AssetsLoader.o -c Engine/src/AssetsLoader.cpp $(INCFLAGS) $(LDFLAGS)

obj/Manager.o: ECS/src/Manager.cpp
	g++ $(CXXFLAGS) -o obj/Manager.o -c ECS/src/Manager.cpp $(INCFLAGS) $(LDFLAGS)

obj/icon.res:
	windres res/icon.rc -O coff -o obj/icon.res


I hope you can help me with my problem, thanks in advance ! (and sorry if I make some gramatical/syntaxic errors, I'm not a native english speaker :p)
The template member functions need to be placed in the header file.

When compiling Manager.cpp, the compiler does not know what <T> is, so none of the template member functions are expanded and compiled.

If you put the template function definitions in the header file, then when they are called (in main() or wherever) the compiler will see what expansions are needed, and compile them accordingly.
You can't split templated function between .h and .cpp files. They need to be fully defined in the .h file.

Also note that for the constructor, you can do this:

1
2
3
4
5
6
Manager::Manager() : components(std::make_unique<ComponentManager>()), systems(std::make_unique<SystemManager>()) {
    std::cout << "8\n";

    for (Entity e = 0 ; e < MAX_ENTITIES ; e++)
        entities.push(e);
}

Last edited on
It worked ! Thanks for the help ! ^^
Topic archived. No new replies allowed.