2013-08-06 15 views
0

我在我的代碼庫中有幾個「資源」。它們都是類,除了一個類共享相同的接口外,ShaderProgram只有一種方式,它需要兩個字符串作爲頂點和片段文件的文件名。這可以通過模板專業化解決,如果不是,那麼如何?

我有一個叫ResourceManager的模板類,處理所有這些資源除了着色器之一,因爲它需要兩個文件和其他人需要一個,我可以解決這個用模板特殊化?它需要是ResourceManager看到GetOrLoadFromFile(字符串,字符串)而不是(字符串)版本,而其他人有相反的,他們看到(字符串)和不(字符串,字符串)。 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); 
     } 
    } 

回答

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。爲了編譯和測試這個例子中,你需要使用「--std = C++ 11」標誌,從GCC或鐺++編譯器

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將能夠採取他需要什麼。添加新的參數很容易,它具有相同簽名的代碼少,您不必使用模板特化。

+0

這是一個好主意,我考慮做這樣的。 – EddieV223

+0

這是不是有可能調用的ResourceManager :: AttempLoad(常量LoadConfiguration),這是一個字符串類?在你的例子中沒有任何東西可以阻止它。 – EddieV223

+0

這個想法是爲AttempLoad嘗試執行正確類型的LoadConfiguration的dynamic_cast。是的,從某種意義上說,您正在從編譯時間檢查轉到運行時間檢查。但從另一個角度來說,傳遞LoadConfiguration而不是ExtendedLoadConfiguration應該像傳遞一個空的第二個字符串一樣被處理 - 可能並且應該很容易處理。我認爲標準和可擴展API的好處和舒適性比這個問題要大。 – Vadim

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 
} 

「錯誤」的成員函數將不會被實例化,除非確實有它的呼叫,在這種情況下,編譯器將具有診斷退出。