0

Requirements: Ability to contain generic class objects.

I am making a static library (actually several static libraries) to encapsulate OpenGL functionality into reusable classes.

I have a basic resource management class definition (which originally came from an external OGL tutorial site), that defines static maps:

class ResourceManager
{
    public:
    static std::map<std::string, Shader> Shaders;
    static std::map<std::string, Texture2D> Textures;
    etc...

These have worked fine in the past, but now I am in need of something generic to handle containers of object types that will be declared outside of the scope of this library. Here is what I've managed to piece together after searching over the internet:

...
template <class T>
static std::map<std::string, std::map<std::string, T>> MetaMap;

template <class T>
static void AddData(T data, std::string name)
{
    //This line is the real issue
    MetaMap[typeid(T)::name()][name] = data;
}

As is required, I have another declaration of MetaMap in my .cpp file:

template <class T>
std::map<std::string, std::map<std::string, T>>   ResourceManager::MetaMap;

The intended use of this in another project would look like:

ResourceManager::AddData<Shader>(spriteshader, "spriteshader");
ResourceManager::AddData<Texture2D>(spritetexCoin, "cointex");
ResourceManager::AddData<SpriteAtlasAsymetric>(SAasym, "asymatlas");
etc...

The templated AddData() method causes no issues when empty, but when trying to reference MetaMap from inside, causes the error: "C3245 - 'ResourceManager::MetaMap': use of a variable template requires template argument list"

Now, to me, this suggests that I need to place "< T>" somewhere around the reference to MetaMap in AddData.

template <typename T>
static void AddData(T data, std::string name)
{
    MetaMap<T>[typeid(T)::name()][name] = data;
}

Doing so clears up the compilation error, but presents a linking error: "LNK2001 - unresolved external symbol..."

This is as close to figuring this one out as I've gotten. Is this functionality actually achievable with C++? Because I've also read that:

"Templates are specialized at compile time so they are not still parameterized types at runtime"

and

"Templates, in contrast, resolve into ordinary types at compile time and the resulting types may not be specialized in other assemblies."

...which kind of sounds like I'm dead in the water. Can this approach be made to work in the way I desire?

Thanks for your time.

RESOLUTION

I might as well update this question with my solution for the benefit of others. All in the header file:

template <typename T>
class Mapper {
public:
    static std::map<std::string, T> myMap;    

    static inline void AddData(T data, std::string name) { myMap[name] = data; }// *(new T(data)); }
    static inline T GetData(std::string name) { return myMap[name]; }
    static inline T* GetData_ptr(std::string name) { return &myMap[name]; }

private:
    //Private constructor
    Mapper() { }

};
//Must be implemented in header. Linker will not be able to resolve the symbol if inside cpp file.
template <typename T>
std::map<std::string, T> Mapper<T>::myMap;

After battling this concept, I'm happy to see that the most elegant solution is the correct one.

This can be used from an external project in the following way, and without any need for setup or further initialisation (regarding the above requirement that myMap be implemented inside the header, but outside the class definition) :

Mapper<int>::AddData(6, "myint");

int temp = Mapper<int>::GetData("myint");

Mapper<Texture2D>::AddData(spritetexCoin, "texcoin");
Texture2D temptex = Mapper<Texture2D>::GetData("texcoin");

//Doesn't alter value referenced in Mapper
temp = 9;
//temp back to 6 again
temp = Mapper<int>::GetData("myint");
Matthew Lacey
  • 47
  • 2
  • 6

0 Answers0