什么是从字符串到类型(C ++)的最佳方式

时间:2013-11-21 11:18:24

标签: c++ types rtti

我希望能够将字符串指定为字符串并在C ++中创建该类型。我知道C ++不直接支持,但最好的解决方法是什么?

我目前有一个包含信息的xml,但我想扩展它以包含组件。

<entity>
   <component>ComponentA</component>
   <component>ComponentB</component>
</entity>

我有一个通用工厂,它接收这些xml并构建实体。我希望能够避免if("componentA") { new ComponentA; }支持更通用的东西。主要是因为组件将由客户定义,而工厂则不是。

我认为组件可以在工厂注册并存储地图,但这需要保留我想避免的所有组件的副本。

我更喜欢跨平台解决方案。

2 个答案:

答案 0 :(得分:2)

AFAIK,至少对于一般的C ++,没有使用字符串创建类的隐式方法。但是,我过去曾使用过另一种机制。

首先,您定义组件的概念:

class Component /* or IComponent if you're feeling adventurous - we may not have interfaces in C++, but dammit we like 'em! */
{
protected:
    Component() { };

public:
    virtual ~Component() = 0 { };
}; // eo class Component

一种某种创造者的概念:

class ComponentCreator
{
protected:
    Component() { };

public:
    virtual ~ComponentCreator() = 0 { };
    virtual Component* create() const = 0;  // Might want to use smart-pointers here - this is for illustrative purposes only.
}; // eo class ComponentCreator

好的,我们现在有了基础,我们需要一个可以让这些创作者注册的工厂:

class Factory
{
private:
    std::map<std::string, ComponentCreator*> _creators;

public:
    Factory() : _creators(new std::map<std::string, ComponentCreator*>();
    {
    };

    ~Factory()
    {
       // cleanup of _creators ommited.
    };

    // call to register a creator
    void register(const std::string& name, ComponentCreator* creator)
    {
        // normally you'd put checks to see if it exists etc.
        _creators[name] = creator;
    }; // eo register


    // call to create an instance
    Component* create(const std::string& name)
    {
        std::map<std::string, ComponentCreator*>::const_iterator cit(_creators.find(name));
        if(cit != _creators.end())
            return cit->create();
        else
            return NULL; // or nullptr
    }; // eo create
}; // eo class Factory

如此声明你的课程(我只做一个):

class ComponentA : public Component { /* implementation */ };

不要忘记创作者:

class ComponentCreatorForA : public ComponentCreator
{
public:
    virtual Component* create() const { return new ComponentA(); };
}; // eo class ComponentCreatorForA

在程序初始化期间,您注册组件创建者:

factory.register("componentA", new ComponentCreatorForA());
factory.register("componentB", new ComponentCreatorForB());

稍后,我们可以按名称创建组件:

Component* component = factory.create("componentA");

备注

  • 此方法假定组件在编译时是已知的。如果没有人可以引入一个插件架构,以便额外的DLL可以在启动时通过工厂注册他们的组件,这样你就可以在不必重新部署所有内容的情况下使其可扩展。

  • 在现实世界中,我们会使用其中一些智能指针,并输入很多东西以便于打字!

答案 1 :(得分:0)

您必须存储有关组件类的元信息。可能的解决方案可能使用模板:

// the component interface
class BaseComponent {...}

// structure containing meta-information
template<typename ComponentType>
struct tComponentMeta {
    typedef ComponentType type;
    std::string componentTypeName;

    tComponentMeta() : componentTypeName("no valid component type") {}
} 

// providing run time type information 
// (optional, but i like it if the component know about their type
template<typename ComponentType>
class TComponent : public BaseComponent
{
     tComponentMeta<ComponentType> metaInfo;
     TComponent(const std::string& uniqueTypeName) {...};
}

class ConcreteComponent : public TComponent<ConcreteComponent>
{
   ...
}

现在客户端必须为ConcreteComponent类型定义专用的tComponentMeta。这可以通过在ConcreteComponent的类声明之后添加以下代码来实现:

template <>
struct tComponentMeta {
    typedef ConcreteComponent type
    tComponentMeta() : componentTypeName("ConcreteComponent") {}
}

因此,如果您的客户端为组件定义模板特化,您可以为它们提供具有以下类型的模板方法的通用工厂,也必须由设计组件的客户端调用:

...
template<typename ComponentType>
registerComponentType() {
    tComponentMeta<ComponentType> metaInfo;
    nameToMetaMap.put(metaInfo.name, metaInfo)
}

使用这些构建块,您可以通过强制客户端提供其组件的tComponentMeta特化并在您的通用工厂注册组件类型,以通用方式生成组件。

此代码尚未经过我自己的测试,因此您可以假设存在一些语法错误,但我希望这个想法很明确。

由于模板的性质,该方法也适用于使用DLL的插件架构。