Silverlan
Silverlan

Reputation: 2911

Static member of template base class doesn't get exported to shared library

I have a class 'ModelManager' which is derived from a template class 'TModelManager'. Their declaration is as follows:

template<class TModel,class TModelMesh,class TModelSubMesh>
    class TModelManager
{
protected:
    static std::map<std::string,ModelHandle> m_models;
    static std::vector<std::map<std::string,ModelHandle>::iterator> m_marked;
    [...]
};

class DLLNETWORK ModelManager
    : public TModelManager<Model,ModelMesh,ModelSubMesh>
{
    [...]
};

There are two shared libraries, "shared" and "server".

In the "shared"-library, DLLNETWORK is equivalent to '__attribute__((visibility("default")))' on linux systems, to export the class. The members of the template class are defined as such (in a cpp-file):

template<class TModel,class TModelMesh,class TModelSubMesh>
    DLLNETWORK std::map<std::string,ModelHandle> TModelManager<TModel,TModelMesh,TModelSubMesh>::m_models;
template<class TModel,class TModelMesh,class TModelSubMesh>
    DLLNETWORK std::vector<std::map<std::string,ModelHandle>::iterator> TModelManager<TModel,TModelMesh,TModelSubMesh>::m_marked;

In the "server"-library, DLLNETWORK is an empty definition, to make sure the class is imported. The "server"-library is linked against the "shared"-library.

This is compiling just fine.

During runtime, the "server"-library is loaded in dynamically, which also causes the "shared"-library to be loaded in. However, this results in the following error:

Unable to load library 'lib/libserver_x64.so': lib/libserver_x64.so: undefined symbol: _ZN13TModelManagerI5Model9ModelMesh12ModelSubMeshE8m_markedE

I've used the "ldd" utility on the "server"-library, which confirms that it is linked against the "shared"-library, and it can find it.

I've then used the "nm" utility to look for all symbols related to the TModelManager-class. This is the result:

nm -D libshared_x64.so | grep ModelManager
0000000000b15610 u _ZGVN13TModelManagerI5Model9ModelMesh12ModelSubMeshE8m_modelsE
000000000072d1b0 T _ZN12ModelManager21CreateFromBrushMeshesERSt6vectorIP9BrushMeshSaIS2_EE
000000000072d10c T _ZN12ModelManager4LoadESs
000000000072d950 W _ZN13TModelManagerI5Model9ModelMesh12ModelSubMeshE21CreateFromBrushMeshesERSt6vectorIP9BrushMeshSaIS6_EE
000000000072d62c W _ZN13TModelManagerI5Model9ModelMesh12ModelSubMeshE4LoadESsPFP8MaterialPKcE
0000000000b155e0 u _ZN13TModelManagerI5Model9ModelMesh12ModelSubMeshE8m_modelsE

Everything is there, except for the "m_marked" member. The "m_models"-member is found twice with a different address.

I really don't understand this at all, the 'm_marked'-member is defined in the same way as the 'm_models'-member, so why isn't it exported? Why does the 'm_models'-member show up twice?

// EDIT:

I didn't notice the 'U' in front of the nm-results for 'm_models' which means they're undefined.

Is something wrong with my defining them?

Upvotes: 0

Views: 268

Answers (1)

Jonathan Wakely
Jonathan Wakely

Reputation: 171373

The members of the template class are defined as such (in a cpp-file):

You seem to be assuming that those definitions will be instantiated in the cpp file, but unless something in that cpp file uses them the members will not be instantiated, and so the corresponding symbols will not be emitted in the compiled object file.

The code in the server library that uses those static members can't implicitly instantiate them, because you've hidden the definitions in the cpp file.

The static member definitions either need to be explicitly instantiated for all the specializations you're using, or you need to put those definitions in the header.

Upvotes: 2

Related Questions