2

「コンポーネント」(コンポジションFTW)を保持する「エンティティ」に基づいて構築されたプログラムがあります。

コンポーネントには、スクリプト、アセットなど、さまざまなタイプが含まれる場合があります。

エンティティには文字列のマップと実際のコンポーネントがあり、タイプ名でコンポーネントを検索できます。

という関数が欲しい

<Component T>GetComponent(char* TypeName, <T>);

文字列と型名を受け取り、要求された型付きコンポーネントを返します。

C++ テンプレートでそのようなことを行うことは可能ですか? 上記は明らかに機能しません。どうすればよいかわかりません。

ありがとう

編集:

私は工場を探していません。

エンティティは、さまざまなタイプのコンポーネントのインスタンスを保持します。現在、これは

std::vector<Component> componentList; 

std::vector<char*> componentNames; 

そのインデックスは同じであることが保証されています。おそらく、後で適切なマップを書きます。

GetComponent が、Entity によって ComponentList に保持されている型名の既にインスタンス化されたコンポーネントへの適切に型指定された参照を返すようにしたいだけです。

4

4 に答える 4

11

関数はコンポーネントを作成しますか? それから工場です。クライアントが(潜在的に誤った)キャストを保存するために、そのテンプレートでラップすることができます。

関数テンプレートの型は次のようになります。

template< typename T >
T* GetComponent(const char*); // presuming it returns a pointer

これは次のように呼び出されます。

Foo* foo = GetComponent<Foo>("foo");
于 2009-11-08T21:46:13.310 に答える
3

適切な質問をすることは、良い答えを得る方法の少なくとも半分です。直面している特定の問題ではなく、達成したいことを実際に述べる必要があります。私には、あなたが実際に思っている以上に言語に問題を抱えているように思えます。

最初の部分は、コンポーネントから派生したコンポーネント階層があり、おそらく共通のインターフェイスを提供しているように見えることです。エンティティは、多くのコンポーネントを内部に保持しており、Component から派生した任意の型にすることができます。その場合は、Component オブジェクトを直接格納しているため、コンテナを作り直す必要があります。これにより、オブジェクトにスライスが生成されます (コンテナに入力する派生型に関係なく、コンテナはコンポーネントの共通部分のみを保持します)。物体)。

いくつかのベクトルで作業し、両方が常に同期されることを期待することは可能ですが、脆弱です。名前とコンポーネントが一緒に使用される場合は、名前とコンポーネントのペアを保存する必要があります。名前で検索する場合は、O(log N) 検索を直接提供するため、マップを使用する必要があります。

さて、質問に戻ります。達成したいことが単純な構文糖衣である場合 (必要に応じて、呼び出し元が明示的に動的にキャストされないようにする)、テンプレートを使用してそれを取得できます (詳細は後述)。しかし、あなたは本当にあなたのデザインを考えるべきです. Component は、実際のインターフェイスをコンポーネントに定義しますか? ユーザーがコンポーネントを使用する前に特定の型にダウンキャストする必要がある場合は、抽象化が悪い (コンポーネントは実際のインターフェイスを提供しない) か、オブジェクトが実際には適合しません。

最後にまだ実行したい場合は、テンプレート メソッド (またはフリー関数) 内で実行することにより、動的キャストを呼び出し元から隠すことができます。

class Entity {
   typedef std::map< std::string, boost::shared_ptr<Component> > component_map_t;
public:
   boost::shared_ptr<Component> getComponent( std::string const & name ) {
      component_map_t::iterator it = components_.find(name);
      if ( it == components_.end() ) { // not found, handle error
         // ...
      }
      return it->second;
   }
   template <typename T> // syntactic sugar
   boost::shared_ptr<T> getComponent( std::string const & name ) {
      return boost::dynamic_pointer_cast<T>( getComponent(name) );
   }
private:
   component_map_t components_;
};
template <typename T> // syntactic sugar also available as free function
boost::shared_ptr<T> getComponent( Entity & entity, std::string const & name ) {
   return boost::dynamic_pointer_cast<T>( entity.getComponent(name) );
}
int main() { // usage
   Entity e; // ... work with it add components...
   boost::shared_ptr<Component> c1 = e.getComponent( "one" ); // non-templated method returns Component
   boost::shared_ptr<DerivedComponent> c2 = e.getComponent<DerivedComponent>( "two" );
   boost::shared_ptr<DerivedComponent> c3 = getComponent<DerivedComponent>( e, "two" );
}

実際の参照を返す代わりに、インターフェイスをいじることができboost::shared_ptrます (コンポーネントがエンティティから削除された場合に、ユーザー コードがダングリング参照を使用しないように、有効期間を慎重に制御する必要があります)。

于 2009-11-08T23:30:49.433 に答える
1

次のようなものを使用できます。

struct Entity
{
    Component* GetBaseComponent (const char* TypeName)
    {
        // do lookup on TypeName and return corresponding Component.
    }

    template<typename T> T* GetComponent (const char* TypeName)
    {
        return dynamic_cast<T*> (GetBaseComponent (TypeName));
    }
};

次のように呼び出します。

entity.GetComponent<MyComponent> ("MyComponent");

コンポーネントを要求して型が間違っていると、キャストは null ptr を返します。

編集:これは、ファクトリとは呼ばれませんが、本質的に sbi と同じソリューションであることに気付きました。

于 2009-11-08T23:21:39.053 に答える
0

getComponent 関数には 2 つの別々のタスクがあります

1) 文字列識別子からオブジェクトを取得する

2) このオブジェクトを提供されたテンプレート引数の型にキャストする

テンプレートを使用すると、(2) は非常に簡単になります。ただし、(1) 文字列オブジェクトを処理する必要があるため、テンプレートだけではうまくいきません。別の方法でコンポーネント コンテナを埋める必要があります。保存とキャストに関しては、boost::any または boost::variant に興味があるかもしれません。

于 2009-11-08T23:10:09.520 に答える