your problem has not much to do with file inclusion or linkage. components
is a variable template, not an actual variable. For each type you instantiate it (in your case float
and int
), explicitly or implicitly, the compiler will create a variable for you.
So what you basically have:
- with
World.add<int>(...)
in main.cpp you create the variable World::components<int>
and fill it with values.
- with
layer.add(...)
in main.cpp you call World::add<float>
in layer.cpp which will create the variable World::components<float>
Both are distinct variables, this is how templates work. The same is happening for your function templates in World
. For each type the templates are instantiated, the compiler will create a new function for you.
Edit after comments:
Maybe I still got something wrong, but
layer.get(i)
and
world::get<float>(i)
give me the same value, the one added with layer.add(i/5.f)
so it works like it should, doesn't it?
Both give the values in World::components<float>
whereas World::get<int>(i)
gives me the values in World::components<int>
Output for the program (after fixing some small errors):
get<int>:0
Layer::get:0
get<float>:0
get<int>:1
Layer::get:0.2
get<float>:0.2
get<int>:2
Layer::get:0.4
get<float>:0.4
get<int>:3
Layer::get:0.6
get<float>:0.6
get<int>:4
Layer::get:0.8
get<float>:0.8
Second Edit (digging):
So as something should or should not work (and not only sometimes) I started digging. Indeed the compiler creates two instances of World::components<float>
. After
g++ -o prog layer.cpp main.cpp
and
nm prog | grep components
I get
0000000000407210 b _ZN5World12_GLOBAL__N_110componentsIfEE
0000000000407230 b _ZN5World12_GLOBAL__N_110componentsIfEE
00000000004071f0 b _ZN5World12_GLOBAL__N_110componentsIiEE
which tells me two components<float>
and one component<int>
reside in the BSS section of the object file.
Depending of the order of compilation, the program works either as expected or produces a segmentation fault. The order state above works,
g++ -o prog main.cpp layer.cpp
results in the segmentation fault reported by the op. Without checking the access to all three components in the compiled file, I assume the compilation of main.cpp creates the float
and int
version of the vector, the compilation of layer.cpp creates the second float
version of the vector. The first two are initialized in main.cpp, while the third remains empty - leading to undefined behavior when it is accessed with the []
operator later. It seems if compiled the other way around, the linker resolves every access to components<float>
to the same instance in the BSS section, so the program is actually running fine, but will probably produces unexpected/undefined behavior under different circumstances (e.g., if the second instance of components<float>
is accessed somewhere else somehow.
So, a solution is needed. I've change world.hpp to
#pragma once
#include <vector>
namespace World {
template<typename T>
struct Comp {
static std::vector<T> components ;
};
template<typename T>
T get(int i) {
return Comp<T>::components[i] ;
}
template<typename T>
void add (T elm) {
Comp<T>::components.push_back(elm) ;
}
}
template<typename T>
std::vector<T> World::Comp<T>::components;
which works, regardless of the order of compilation/linkage.
Third edit (remarks):
Btw. I read in your comments below your question that you want to hide components
from the outside or restrict access to it. If this is your goal you could also do the following:
- Change
World
from namespace to class
- Declare
components
as private
and static
member
- Declare your two function as
public
and static
I that case you also have to define your private static member the way I did in the example above (last two lines).