2011-07-09 14 views
3

私はすべての種類のリソースを管理するジェネリッククラスを持っていますが、すべてのTに対してResourceManagerのインスタンスを作成したくないので(つまり、それぞれのタイプTに対して1つのリソースマネージャを持っています)、 ResourceManagerクラスにTの型を知らせないようにします。void *をより高いものにキャストする安全な方法はありますか?

void *ポインタのマップを保存し、誰かがテンプレート化されたLoad()メソッドから特定の型を要求した場合に、それらを必要な形式に変換し直します。

template<> 
AwesomeType* Load(const std::string &location) 
{ 
    //etc. 

    return static_cast<AwesomeType*>(m_resources[location]); 
} 

が、私はこれは醜いであることを承知していますが、今はその周りに方法はありません。

template <typename T> 
T* Load(const std::string &location) 
{ 
    //do some stuff here 

    //everybody take cover!!! 
    return static_cast<T*>(m_resources[location]); 
} 

は、私はクラスに異なるローダーを導入するテンプレート特殊化を使用しています。私は、特殊なLoadメソッドの内部に静的なマップを導入することができますが、その方法では、ResourceManagerオブジェクトの存続期間にリソースの存続期間をバインドすることができません。これは不可欠な機能です。

しかし、これは幾分危険です(これらのvoid *ポインタは何でもかまいません)ので、少なくとも実行時に変換がうまくいくかどうかチェックしたいので、アプリケーションのクラッシュを起こすことなく対応できます。

どうすればいいですか?

+2

'dynamic_cast'を使って' NULL'をチェックしますか? –

+1

'dynamic_cast'は、既知の型を可能な限り別の型に変換します。 'void *'は既知の型ではなく、型なしです。コンパイラはあなたにそれをさせません。 – Damon

答えて

6

あなたが保存された値の種類を拡張する場合は、簡単に、これを行うことができます - それもtype_infoオブジェクトを保存構造体作り:

#include <type_info> 

struct ResourceInfo 
{ 
    std::type_info const& info; 
    void* ptr; 
}; 

// ... 

// just to give you the general idea 
template<class Res> 
void CacheResource(std::string const& location, Res* res) 
{ 
    ResourceInfo ri = { typeid(Res), res }; 
    m_resources.insert(std::make_pair(location, ri)); 
} 

template<class Res> 
Res* Load(std::string const& location) 
{ 
    map_type::const_iterator res_it = m_resources.find(location); 
    if(res_it != m_resources.end()) 
    { 
    if(typeid(Res) != res_it->second.info) 
    { 
     throw SorryBuddyWrongResourceType(some_info_here); 
    } 
    return static_cast<Res*>(res_it->second.ptr); 
    } 
} 

これは、私はそれを行う方法と似ていますが、私はshared_ptr<void>を使用リソースを節約する。

+0

それは素晴らしいです。ありがとうございました! – TravisG

+0

@heishe:これはリソースタイプ間の基本的な共分散を許さないことを覚えておいてください。キャッシングのテンプレート(すべてのプリロードなど)とすべての読み込みにテンプレートと同じ型を使用する必要があります。 – Xeo

5

(私はこれはすでに、ボイドポインタについての他の多くの質問で答えていると確信しているが、ここでは...行く)

しかし、これは、これらのvoid *型のポインタ以来( やや危険であるため、 何でもいいが)、私は を実行時に確認したいと思います。 が動作するので、私はそれに反応することができます。 アプリケーションクラッシュがありません。

チェックできません。これはvoid *ポインタに関するものです。あなたが指しているものが何かを指摘していないし、そのタイプを知らなくても指し示すメモリを検査することはできません。

void*の場合は、実際に何を指しているのかを事前に把握してから、適切にキャストするだけで済みます。

7

void*にキャストできるものを確認する方法はありません。各ポインタで実際のタイプを示す追加情報を保存しない限り

さらに、 "C++の方法"は、抽象基本クラスResourceから各リソースクラスを派生させ、Resourceへのポインタのマップをリソースマネージャに格納することです。次に、dynamic_cast<T*>を使用して必要な型に変換できます。ポインタが間違った型のオブジェクトの場合はNULLを返します。または(あなたが何をしたいかによって)Resource*ポインタを返し、仮想関数を使用して各リソースの機能を実装することができます。

+0

+この回答も非常に役に立ちますが、Xeoの回答は少し拡大していますので、私は彼の答えを受け入れます。とにかくありがとう! – TravisG