我有一个基于“实体”的程序,它包含“组件”(组成FTW)。
组件可能包含许多不同类型,包括脚本,资产等。我想构建一个名为
的实体函数实体具有字符串映射和实际组件,以便可以按类型名称搜索组件。
我想要一个名为
的函数<Component T>GetComponent(char* TypeName, <T>);
它接受字符串和类型名称,并返回所请求的类型化组件。
是否可以使用C ++模板进行此类操作?以上显然不起作用,我不知道该怎么做。
谢谢
编辑:
我不是在寻找工厂。
实体包含不同类型组件的实例。目前,这是通过
完成的std::vector<Component> componentList;
和
std::vector<char*> componentNames;
保证哪些索引相同。我可能稍后会写一张合适的地图。
我只是希望GetComponent返回一个正确类型的引用,该引用指向ComponentList中Entity持有的类型名称的已定时组件。
答案 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感兴趣。