0

我的代码库中有几个“资源”。它们都是类,除了一个类外,共享相同的接口,ShaderProgram只有一个不同之处,它需要两个字符串作为顶点和片段文件的文件名。

我有一个名为 ResourceManager 的模板类,它处理除着色器一个之外的所有这些资源,因为它需要两个文件,而其他文件需要一个,我可以通过模板专业化解决这个问题吗?需要 ResourceManager 看到 GetOrLoadFromFile(string, string) 而不是 (string) 版本,而其他版本则相反,他们看到的是 (string) 而不是 (string, string)。AttemptLoad 也需要处理。我该如何解决这个问题,请包含代码,我以前从未做过模板专业化。

template < class ResType > class ResourceManager
{
public:
    ResourceManager(void);
    ~ResourceManager(void);

    SmartPointer<ResType> GetOrLoadFromFile( const std::string & fileName );

    //weak_ptr<ResType> GetResourceFromID( ResourceID & resID );

    void DestroyResources();
    void ReleaseResources();
    void ReloadResources();
protected:


private:
    SmartPointer<ResType> AttemptLoad( const std::string & fileName );

    std::unordered_map<string, SmartPointer<ResType> > mResMap;

};

// Relevant methods ( SNIPPED )
template < class ResType> SmartPointer<ResType>   ResourceManager<ResType>::GetOrLoadFromFile( const std::string & fileName )
    {
    if ( !mResMap.empty() )
        {
        auto index = mResMap.begin();
        auto end = mResMap.end();

        while ( index != end )
            {
            if ( index->first == fileName )
                {
                return index->second;
                }
            ++index;
            }
        }

    return AttemptLoad(fileName);
    }

template < class ResType > SmartPointer<ResType> ResourceManager<ResType>::AttemptLoad( const std::string & fileName )
    {
    SmartPointer<ResType> pRes( new ResType() );

    if ( pRes->LoadFromFile( fileName ) )
        {
        mResMap.insert( std::make_pair( fileName, pRes ) );
        return pRes;
        }
    else
        {
        LogFailure("Failed to load resource file " + fileName)
        return SmartPointer<ResType>(nullptr);
        }
    }
4

3 回答 3

1

如果这两个类都在你的控制之下,我会建议一个不同的解决方案。为什么不将 AttempLoad 方法更改为类似

SmartPointer<ResType> AttemptLoad( const LoadConfiguration &p_loadConfiguration );

在哪里

class LoadConfiguration 
{
    public:
            std::string FirstFileName;
};

class ExtendedLoadConfiguration : public  LoadConfiguration 
{
    public:
            std::string SecondFileName;
};

然后,您可以始终使用 LoadConfiguration 并且每个 AttemptLoad 都可以获取他需要的内容。添加新参数将很容易,具有相同签名的代码更少,并且您不必使用模板专业化。

于 2013-08-06T17:51:55.360 回答
0

只需实现两个“GetOrLoadFromFile”函数:

#include <string>

struct R1 
{
  void load (const std::string &name) {}
};

struct R2 
{
  void load (const std::string &name0, const std::string name1) {}
};

template<typename R>
struct M
{
  R *get_or_load (const std::string &name)
  {
    R *p = new R();
    p->load (name);
    return p;
  }

  R *get_or_load (const std::string &name0, 
                  const std::string &name1)
  {
    R *p = new R();
    p->load (name0, name1);
    return p;
  }
};


M<R1> m1;

M<R2> m2;


int
main ()
{
  R1 *p0 = m1.get_or_load ("foo");
  // R1 *p1 = m2.get_or_load ("foo"); // error
  R2 *q0 = m2.get_or_load ("foo", "bar");
  // R2 *q1 = m1.get_or_load ("foo", "bar");  // error
}

“错误”的成员函数不会被实例化,除非实际上有调用它,在这种情况下编译器将退出并进行诊断。

于 2013-08-06T18:06:37.927 回答
0

模板背后的想法是您在执行时间之前(即在编译时间)就知道您的类型。如果这是真的,那么您要做的就是使用模板进行重载。所以,下面我只是放了一个通用代码,你可以适应你的代码,在编译时进行重载。

请注意,为避免重复编写代码,每个常用方法都放在基类中,只让派生类使用那些发散的方法。

#include <memory>
#include <string>
#include <iostream>

using namespace std;

class Base
{
  // put common codes here
};

template <typename ResType>
class ResourceManager : public Base
{
public:
  unique_ptr<ResType> GetorLoad(const string &f) { cout << f << endl; return 0;}
};

// Specilizing class ResourceManager for string type
template <>
class ResourceManager<string> : public Base
{
public:
  unique_ptr<string> GetorLoad(const string &f1, const string &f2) {cout << f1 << f2 << endl; return 0;}
};

int main()
{
  ResourceManager<int> i;
  ResourceManager<string> s;

  i.GetorLoad("int");
  s.GetorLoad("string", "string");
}

PS。要编译和测试这个例子,你需要使用来自 gcc 或 clang++ 编译器的 '--std=c++11' 标志

于 2013-08-06T18:40:08.470 回答