2017-10-16 15 views
1

私はゲームエンジンを再加工してsmart-pointersを使用しています。私はすべてが継承するObjectクラスを持っています。私はIRenderable(純粋な仮想レンダリング関数を定義するクラス)から継承し、はオブジェクトから継承しないようにレンダリング可能なGameObjectを持っています。私はRenderSystemを持っていて、シーン内のすべてのIRenderableにshared_ptrを保持するはずです。shared_ptrをある親クラスから別の親クラスにキャストするにはどうすればよいですか?

問題は私のGameObject shared_ptrをRenderSystemのIRenderableにキャストすることですか?私が試してみました

アイデア:

  • RenderSystemは、ゲームオブジェクトのshared_ptrのを使用していますが、これは必ずしもすべてのゲームオブジェクトがレンダリング可能であるため、それが危険であるとして動作しません。
  • IRenderableはObjectから継承しているので、それを継承することができます。これは、Objectが他の関数を含んでいるため、IRenderableはもはやインタフェースではないことを意味します。

これで完全に行う可能な生のポインタと、そのように、私は感じてスマートポインタと同じ結果を達成するためにいくつかの方法があるよう

例:このように

// Object.h 
class Object : public enable_shared_from_this<Object> { ... } 
// GameObject.h 
class GameObject : public Object { ... } 
// MeshRenderer.h 
class MeshRenderer : public GameObject, IRenderable { 
public: 
    void initialize() 
    { 
     // Not able to cast Object to IRenderable 
     RenderSystem::instance().addRenderable(getShared()); 

     // AND 

     // Not able to cast Object to IRenderable 

RenderSystem::instance().addRenderable(std::static_pointer_cast<IRenderable>(getShared())); 
    } 
} 
// RenderSystem.h 
class RenderSystem 
{ 
    std::list<std::shared_ptr<IRenderable>> m_renderables; 
public: 
    void addRenderable(std::shared_ptr<IRenderable> ptr) 
    { 
     m_renderables.push_back(ptr); 
    } 
} 

// main.cpp 
... 
auto meshRenderer = std::shared_ptr<MeshRenderer>(); 
... 
+3

おそらくhttps://stackoverflow.com/q/20219385/103167の複製ですか? 'std :: dynamic_pointer_cast'が問題を解決しないかどうかを見てください。 –

+0

私は今それを試みます。 – Matthew

+0

shared_ptrは重く、遅いことに注意してください。実際のアプリケーションでは、パフォーマンスは重要ではない場所で使用されます。 –

答えて

3

#include <memory> 

// everything is an Object - yuk, but ok, if you wish... 
struct Object : std::enable_shared_from_this<Object> 
{ 
}; 

struct GameObject : Object 
{ 

}; 

struct IRenderable 
{ 
    virtual void render() {}; 
}; 

struct RederableGameObject : GameObject, IRenderable 
{ 
    auto as_shared_renderable() -> std::shared_ptr<IRenderable> 
    { 
     // builds a new shared pointer to IRenderable which 
     // uses the same lifetime control block as me 
     return std::shared_ptr<IRenderable> 
     { 
      this->shared_from_this(), // where to get the control block 
      this      // what to point to 
     }; 
    } 
}; 

ドキュメント:

参照コンストラクタの数(8)

http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr

更新:ここ

は、任意のオブジェクトから正しいshared_pointerをフェッチするフリー機能のための出発点は、それが最終的に公にstd::enable_shared_from_this

から誘導される設けられています
#include <memory> 
#include <type_traits> 

namespace notstd 
{ 
    // stuff that I think *should be* std 

    using namespace std; 

    // a trait to determine whether class T is derived from template 
    // Tmpl<...> 

    template <typename T, template <class...> class Tmpl> 
    struct is_derived_from_template_impl 
    { 
     static std::false_type test(...); 

     template <typename...Us> 
     static std::true_type test(Tmpl<Us...> const &); 

     using result = decltype(test(std::declval<T>())); 
    }; 

    template <typename T, template <class...> class Tmpl> 
    using is_derived_from_template = typename is_derived_from_template_impl<T, Tmpl>::result; 

    template <typename T, template <class...> class Tmpl> 
    constexpr auto is_derived_from_template_v = is_derived_from_template<T, Tmpl>::value; 


    // free function version of shared_from_this 

    template<class T> 
    auto shared_from(enable_shared_from_this<T>* p) 
    -> std::shared_ptr<T> 
    { 
     return p->shared_from_this(); 
    } 

    // specific shared_ptr construction from type T 

    template<class T> 
    auto shared_from(T*p) 
    -> enable_if_t 
    < 
     is_derived_from_template_v 
     < 
      T, 
      enable_shared_from_this 
     >, 
     std::shared_ptr<T> 
    > 
    { 
     return std::shared_ptr<T>(p->shared_from_this(), p); 
    } 

} 

// everything is an Object - yuk, but ok, if you wish... 
struct Object : std::enable_shared_from_this<Object> 
{ 
}; 

struct GameObject : Object 
{ 

}; 

struct IRenderable 
{ 
    virtual void render() {}; 
}; 

extern int emit(const char* str); 

struct RederableGameObject : GameObject, IRenderable 
{ 
    auto as_shared_renderable() -> std::shared_ptr<RederableGameObject> 
    { 
     return notstd::shared_from(this); 
    } 

    auto as_shared_renderable() const -> std::shared_ptr<const RederableGameObject> 
    { 
     return notstd::shared_from(this); 
    } 

    void e() const { 
     emit("const"); 
    } 
    void e() { 
     emit("mutable"); 
    } 


}; 



int main() 
{ 

    auto rgo = std::make_shared<RederableGameObject>(); 

    // prove it works 
    auto p1 = rgo->as_shared_renderable(); 

    // prove it works with a const object also 
    auto p2 = static_cast<const RederableGameObject&>(*rgo).as_shared_renderable(); 

    p1->e(); 
    p2->e(); 
} 
+0

'return std :: static_pointer_cast (shared_from_this());'もうまくいくでしょう。'* this'の型にダウンキャストして、他の基本型に暗黙のアップキャストを許可します。 –

+0

@JonathanWakelyが合意した。しかし、その質問は明示的にもっとよく答えられたと感じました。実際に私自身のコードでは、Tが 'std :: enable_shared_from_this'から直接派生した場合、変換を確実にするために適切なenable-iffingを付けて、テンプレートテンプレート'テンプレート std :: shared_ptr shared_from(T * self) 'を使用します。それは標準的ではないようなものです。 –

+0

@RichardHodgesテンプレートバージョンを追加してもよろしいですか?他のクラスで同じ機能を実行する必要があるかもしれません。 – Matthew

4

static_pointer_castは、shared_ptr<Object>shared_ptr<IRenderable>から直接関連付けることはできません。

しかし、あなたはshared_ptr<IRenderable>への暗黙的な変換ができ、その後、派生型にキャストすることができます。これは、その後、派生型への最初の基底クラスからの明示的な変換、二への暗黙的な変換を実行

auto p = std::static_pointer_cast<MeshRenderer>(getShared()); 
std::shared_ptr<IRenderer> q = p; 

をベースタイプ。派生型は両方の基底に関係することが知られているため、静的には許可されていますが、関連のない塩基間の直接変換は不可能です。

関連する問題