简体   繁体   English

C ++模板化返回

[英]C++ Templated return

I have a program which is built on "Entities", which hold "Components" (composition FTW). 我有一个基于“实体”的程序,它包含“组件”(组成FTW)。

Components may include many different types including scripts, assets, etc. I would like to build an Entity function called 组件可能包括许多不同的类型,包括脚本,资产等。我想构建一个名为Entity的函数

Entities have a map of strings, and actual Components, such that the Components can be searched for by type name. 实体具有字符串映射和实际组件,以便可以按类型名称搜索组件。

I would like to have a function called 我想要一个叫做的函数

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

Which takes in a string and a type name, and which returns the typed component that is requested. 其中包含字符串和类型名称,并返回所请求的类型化组件。

Is it possible to do such a thing with C++ templates? 用C ++模板做这样的事情有可能吗? The above clearly does not work, and I'm not sure how to go about it. 以上显然不起作用,我不知道如何去做。

Thanks 谢谢

Edit: 编辑:

I'm not looking for a factory. 我不是在找工厂。

Entity holds instances of different types of components. 实体保存不同类型组件的实例。 Currently this is done with 目前这已经完成了

std::vector<Component> componentList; 

and an

std::vector<char*> componentNames; 

Whose indexes are guaranteed to be the same. 谁的索引保证是相同的。 Likely I will write a proper map later. 我可能稍后会写一张合适的地图。

I simply want GetComponent to return a properly typed reference to the already instantied component of type name held by Entity in the ComponentList. 我只是希望GetComponent返回一个正确类型的引用,该引用指向ComponentList中Entity持有的类型名称的已定时组件。

Does your function create components? 你的功能是否创造了组件? Then it is a factory. 然后是工厂。 You could wrap it in that template in order to save clients the (potentially erroneous) casting. 您可以将其包装在该模板中,以便为客户端保存(可能是错误的)转换。

The type of the function template would look like this: 函数模板的类型如下所示:

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

and it would be called like this: 它将被称为这样:

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

Asking the proper question is at least half of the way to getting a good answer. 提出正确的问题至少是得到一个好答案的一半。 You should really state what you want to achieve rather than the particular problem you are facing. 你应该说明你想要实现的目标而不是你所面临的特定问题。 It seems to me as if you have more problems with the language than you actually realize. 在我看来,你的语言问题比你实际意识到的要多。

The first part is that it seems as if you have a component hierarchy that derives from Component, probably providing a common interface. 第一部分是,似乎你有一个派生自Component的组件层次结构,可能提供了一个通用接口。 An entity holds many components internally, that can be of any derived type from Component. 一个实体在内部拥有许多组件,可以是Component的任何派生类型。 If that is the case, you should rework your containers as you are storing Component objects directly, and that will produce slicing in your objects (no matter what derived type you enter into the container, the container will only keep the common Component part of the object). 如果是这种情况,您应该在直接存储Component对象时重新处理容器,这将在对象中产生切片(无论您将哪种派生类型输入容器,容器只会保留公共组件的一部分。宾语)。

Working on a couple of vectors and hoping that both of them will be synchronized at all times is feasible but fragile. 研究几个向量并希望它们在任何时候都是同步的,这是可行的但是很脆弱。 If the name and the component go together, then you want to store pairs of name/component. 如果名称和组件一起使用,那么您希望存储名称/组件对。 If you want to search by name, you should use a map as it will provide O(log N) search directly. 如果要按名称搜索,则应使用地图,因为它将直接提供O(log N)搜索。

Now, going back to the question. 现在,回到这个问题。 If what you want to achieve is plain syntactic sugar (avoid the caller from explicitly dynamic casting if needed) then you can get it with a template (more later on). 如果您想要实现的是简单的语法糖(如果需要,可以避免显式动态转换调用者),那么您可以使用模板(稍后更多)获取它。 But you should really think on your design. 但你应该考虑你的设计。 Does Component define the real interface into any component? Component是否定义了任何组件的真实接口? If users need to downcast to particular types before using a Component, either the abstraction is bad (Component does not provide a real interface) or the objects do not really fit together. 如果用户在使用Component之前需要向下转换为特定类型,则抽象是错误的(Component不提供真实的接口)或者对象并不真正适合在一起。

If at the end of it you still want to do it, you can hide the dynamic cast from the caller by doing it within a template method (or free function). 如果在它结束时你仍然想要这样做,你可以通过在模板方法(或自由函数)中执行它来隐藏调用者的动态强制转换。

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" );
}

You could play with the interface so that instead of boost::shared_ptr you return real references (with what it entails: lifetime must be carefully controlled so that user code does not try to use a dangling reference if the component is removed from the entity). 你可以使用接口来代替boost::shared_ptr你可以返回真正的引用(它带来的含义:生命周期必须小心控制,以便用户代码不会尝试使用悬挂引用,如果从实体中删除组件) 。

You could use something like: 你可以使用类似的东西:

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));
    }
};

and call it with something like: 并用以下方式调用它:

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

If you ask for a component and get the type wrong the cast will return a null ptr. 如果你要求一个组件并得到错误的类型,那么强制转换将返回一个null ptr。

Edit: Just realised this is essentially the same solution as sbi, albeit without calling it a factory. 编辑:刚刚意识到这与sbi基本上是一样的解决方案,虽然没有把它称为工厂。

Your getComponent function has two separate tasks 您的getComponent函数有两个单独的任务

1) retrieve an object from a string identifier 1)从字符串标识符中检索对象

2) cast this object into the provided template argument type 2)将此对象强制转换为提供的模板参数类型

Using templates makes (2) pretty straight forward. 使用模板使(2)非常直接。 But (1) needs work on string objects, so templates won't do the trick on their own. 但是(1)需要对字符串对象进行处理,因此模板不会自行完成。 You have got to fill your component container some other way. 您必须以其他方式填充组件容器。 As for storing and casting, you may be interested in boost::any or boost::variant. 至于存储和转换,您可能对boost :: any或boost :: variant感兴趣。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM