C ++模板返回

时间:2009-11-08 21:43:10

标签: c++ architecture templates composition

我有一个基于“实体”的程序,它包含“组件”(组成FTW)。

组件可能包含许多不同类型,包括脚本,资产等。我想构建一个名为

的实体函数

实体具有字符串映射和实际组件,以便可以按类型名称搜索组件。

我想要一个名为

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

它接受字符串和类型名称,并返回所请求的类型化组件。

是否可以使用C ++模板进行此类操作?以上显然不起作用,我不知道该怎么做。

谢谢

编辑:

我不是在寻找工厂。

实体包含不同类型组件的实例。目前,这是通过

完成的
std::vector<Component> componentList; 

std::vector<char*> componentNames; 

保证哪些索引相同。我可能稍后会写一张合适的地图。

我只是希望GetComponent返回一个正确类型的引用,该引用指向ComponentList中Entity持有的类型名称的已定时组件。

4 个答案:

答案 0 :(得分:11)

你的功能是创建组件吗?然后是工厂。您可以将其包装在该模板中,以便为客户端保存(可能是错误的)转换。

函数模板的类型如下所示:

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

它会像这样调用:

Foo* foo = GetComponent<Foo>("foo");

答案 1 :(得分:3)

提出正确的问题至少是获得良好答案的一半。你应该说明你想要实现的目标而不是你所面临的特定问题。在我看来,你的语言问题比你实际意识到的要多。

第一部分是,似乎你有一个派生自Component的组件层次结构,可能提供了一个通用接口。一个实体在内部拥有许多组件,可以是Component的任何派生类型。如果是这种情况,您应该在直接存储Component对象时重新处理容器,这将在对象中产生切片(无论您将哪种派生类型输入容器,容器只会保留公共组件的一部分。对象)。

研究几个向量并希望它们在任何时候都是同步的,这是可行的但是很脆弱。如果名称和组件一起使用,那么您希望存储名称/组件对。如果要按名称搜索,则应使用地图,因为它将直接提供O(log N)搜索。

现在,回到这个问题。如果您想要实现的是简单的语法糖(如果需要,可以避免显式动态转换调用者),那么您可以使用模板(稍后更多)获取它。但你应该考虑你的设计。 Component是否定义了任何组件的真实接口?如果用户在使用Component之前需要向下转换为特定类型,则抽象是错误的(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返回实际引用(包括它所包含的内容:必须小心控制生命周期,以便在删除组件时用户代码不会尝试使用悬空引用来自实体)。

答案 2 :(得分: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基本上是一样的解决方案,虽然没有把它称为工厂。

答案 3 :(得分:0)

您的getComponent函数有两个单独的任务

1)从字符串标识符

中检索对象

2)将此对象强制转换为提供的模板参数类型

使用模板使(2)非常简单。但是(1)需要对字符串对象进行处理,因此模板不会自行完成。您必须以其他方式填充组件容器。至于存储和转换,您可能对boost :: any或boost :: variant感兴趣。