Resource manager, szablony- wyjaśnienie błędu

0

Tę klasę prawdopodobnie zrefaktoryzuję, ale potrafi ktoś wyjaśnić dlaczego metoda release powoduje błąd kompilacji?

#pragma once

class Shader;
class Mesh;
class Texture;

typedef std::map<std::string, std::auto_ptr<Shader>>    shaders_map;
typedef std::map<std::string, std::auto_ptr<Mesh>>      meshes_map;
typedef std::map<std::string, std::auto_ptr<Texture>>   textures_map;

template<class C, class E>
inline int findElementInMap(const C& cont, E *ptr)
{
    C::const_iterator it = cont.begin();
    int i = 0;

    while(it != cont.end())
    {
        if(it->second.get() == ptr) // BŁĄD POJAWIA SIĘ W TEJ LINII
            return i;

        i++;
        it++;
    }

    return -1;
}

class ResourceManager
{
public:
    ResourceManager(void);
    ~ResourceManager(void);

    template<class T>
    inline T* get(const std::string &name)
    {
        if(typeid(T) == typeid(Shader)) {
            return (T*)getShader(name);
        }
        else if(typeid(T) == typeid(Mesh)) {
            return (T*)getMesh(name);
        }
        else if(typeid(T) == typeid(Texture)) {
            return (T*)getTexture(name);
        }

        return nullptr;
    }

    Shader*     getShader(const std::string &name);
    Mesh*       getMesh(const std::string &name);
    Texture*    getTexture(const std::string &name);

    template<class T>
    inline bool add(T *ptr)
    {
        if(typeid(T) == typeid(Shader)) {
            return addShader((Shader*)((void*)ptr));
        }
        else if(typeid(T) == typeid(Mesh)) {
            return addMesh((Mesh*)((void*)ptr));
        }
        else if(typeid(T) == typeid(Texture)) {
            return addTexture((Texture*)((void*)ptr));
        }

        return false;
    }

    bool        addShader(Shader *ptr);
    bool        addMesh(Mesh *ptr);
    bool        addTexture(Texture *ptr);

    template<class E>
    inline void release(E *ptr)
    {
        if(typeid(E) == typeid(Shader)) {
            release<shaders_map, E>(shaders, (E*)((void*)ptr));
            return;
        }
        else if(typeid(E) == typeid(Mesh)) {
            release<meshes_map, E>(meshes, (E*)((void*)ptr));
            return;
        }
        else if(typeid(E) == typeid(Texture)) {
            release<textures_map, E>(textures, ptr);
        }
    }

    // WYWOŁANIE TEJ METODY POWODUJE BŁĄD
    template<class C, class E>
    void release(C &container, E *ptr)
    {
        assert(ptr != nullptr);

        int index = findElementInMap<C, E>(container, ptr);
        if(index < 0)
            return;

        C::iterator it = container.begin();
        it->second.release();
    }
private:
    shaders_map     shaders;
    meshes_map      meshes;
    textures_map    textures;
};

Błąd brzmi tak:

error C2440: '==' : cannot convert from 'Shader *' to 'Mesh *'
int findElementInMap<C,E>(const C &,E *)' being compiled
      with
      [
          C=textures_map,
          E=Shader
      ]

Czyli typ kontenera nie odpowiada typowi elementu. Ale tej klasy używam tak:

    Shader *sh = new Shader();
    resourceManager.add<Shader>(sh);
    resourceManager.release<Shader>(sh); // BEZ TEJ LINII BŁĘDU NIE MA

Więc te typy powinny się zgadzać.

0

To jest właśnie dobitny przykład jak w C++ się zaplątać w kodzie za pomocą szablonów

0

Wyrzuć wszystkie std::auto_ptr na rzecz std::unique_ptr, std::shared_ptr albo ich boostowe odpowiedniki. Jeżeli chcesz mieć do wszystkiego jedną metodę to wrzuć też wszystkie zasoby do jednej mapy. Niech wszystkie dziedziczą z czegoś w stylu Asset czy Resource. Dzięki temu nie będziesz musiał w ogóle sprawdzać typów dodając zasób do mapy.
Szablony w C++ to nie generics znane z innych języków. Wszystkie klasy i metody z szablonami typów, które mogą zostać użyte będą wygenerowane na etapie kompilacji. A ty w metodzie release wstawiając szablony zamiast konkretnych typów dałeś kompilatorowi do zrozumienia, że chcesz tam wstawić wszystko, więc generuje wszystkie możliwości. Nie bierze pod uwagę tych ifów, bo one zostaną dopiero wykonane podczas runtime.

template<class E>
inline void release(E *ptr)
{
    if(typeid(E) == typeid(Shader)) {
        release<shaders_map, Shader>(shaders, (Shader*)ptr);
        return;
    }
    else if(typeid(E) == typeid(Mesh)) {
        release<meshes_map, Mesh>(meshes, (Mesh*)ptr);
        return;
    }
    else if(typeid(E) == typeid(Texture)) {
        release<textures_map, Texture>(textures, (Texture*)ptr);
    }
}

Ale całą tą klasę i tak trzeba przepisać.

1 użytkowników online, w tym zalogowanych: 0, gości: 1