Linking Problems

I am unable to understand why my program is unable to find a private static variable within the class it is defined in. From the terse output of the linker all I can make out is that Entity.cpp calls ComponentFactory's get_map method which for some reason cannot find map within the private field of the class. Here are the affected source files, any help would be greatly appreciated.
 
g++ -std=c++11 Entity.hpp Entity.cpp Component.hpp ComponentFactory.hpp GridPosition.hpp main.cpp -o main

1
2
3
4
5
6
7
/tmp/ccHoJBDl.o: In function `ComponentFactory::get_map()':
Entity.cpp:(.text._ZN16ComponentFactory7get_mapEv[_ZN16ComponentFactory7get_mapEv]+0xa): undefined reference to `ComponentFactory::map'
Entity.cpp:(.text._ZN16ComponentFactory7get_mapEv[_ZN16ComponentFactory7get_mapEv]+0x2b): undefined reference to `ComponentFactory::map'
Entity.cpp:(.text._ZN16ComponentFactory7get_mapEv[_ZN16ComponentFactory7get_mapEv]+0x32): undefined reference to `ComponentFactory::map'
collect2: error: ld returned 1 exit status
Makefile:6: recipe for target 'main' failed
make: *** [main] Error 1


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
#ifndef ENTITY_HPP_
#define ENTITY_HPP_

#include "ComponentFactory.hpp" // includes Component, map, and string

class Entity
{
    std::map<std::string, Component*> components;
public:
    Entity(std::initializer_list<std::string> l);
    ~Entity();
    
    void add_component(std::string s);
    void remove_component(std::string s);
    bool has_component(std::string s);
    template <typename T>
    T*   get_component(std::string s);
};

template <typename T>
T* get_component(std::string s)
{
    Component* ret = ComponentFactory::create_instance(s);
    return dynamic_cast<T*>(ret);
}
#endif /* ENTITY_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
#include "Entity.hpp"

Entity::Entity(std::initializer_list<std::string> l)
{
    for (auto s : l)
        add_component(s);
}

Entity::~Entity()
{
    for (std::pair<std::string, Component*> p : components)
        delete p.second;
}

void Entity::add_component(std::string s)
{
    if (has_component(s))
        return;

    Component* c = ComponentFactory::create_instance(s);
    if (c != nullptr) // Component is registered?
        components.insert({s, c});
}

void Entity::remove_component(std::string s)
{
    std::map<std::string, Component*>::iterator rem = components.find(s);
    if (rem != components.end())
        delete rem->second;

    components.erase(rem);
}

bool Entity::has_component(std::string s)
{
    return components.find(s) != components.end();
}

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
#ifndef COMPONENTFACTORY_H
#define COMPONENTFACTORY_H

#include "Component.hpp"
#include <map>
#include <functional>

#define REGISTER_COMPONENT_TYPE(NAME) \
    static ComponentRegister<NAME> reg

#define REGISTER_COMPONENT_INFO(NAME) \
    ComponentRegister<NAME> NAME::reg(#NAME)

// Template trick. On instantiating the function it creates a new
// instance of its generic type.
template <typename T>
Component* create_t() { return new T; }

struct ComponentFactory
{
    typedef std::map<std::string, std::function<Component*()>> map_type;

    static Component* create_instance(std::string const& s)
    {
        map_type::iterator it = get_map()->find(s);
        if (it == get_map()->end())
            return nullptr;
        return it->second();
    }

protected:
    static map_type* get_map()
    {
        if (!map) { map = new map_type; }
        return map;
    }

private:
    static map_type* map;
};

template <typename T>
struct ComponentRegister : ComponentFactory
{
    ComponentRegister(std::string const& s)
    {
        get_map()->insert(std::make_pair(s, &create_t<T>));
    }
};
 
#endif /* COMPONENTFACTORY_H */ 


The component registered...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef POSITION_HPP_
#define POSITION_HPP_

#include "ComponentFactory.hpp"
class GridPosition : public Component
{
    REGISTER_COMPONENT_TYPE(GridPosition);
public:
    int x, y;
};

REGISTER_COMPONENT_INFO(GridPosition);

#endif /* POSITION_HPP_ */ 

1
2
3
4
5
#include "Entity.hpp"
int main()
{
    Entity{"GridPosition"};
}
Last edited on
http://www.cplusplus.com/forum/general/113904/#msg622050
In one cpp
ComponentFactory::map_type *ComponentFactory::map;
Thank you so much. I had seen something about having to define the variable in a separate cpp file in some stackoverflow post but then I defined it incorrectly, as static ComponentFactory::map_type* map, and dismissed that potential problem. Got it compiled and running.
Topic archived. No new replies allowed.