C++ 根据C+中的字符串创建对象+;

C++ 根据C+中的字符串创建对象+;,c++,reflection,C++,Reflection,我有一个超类的几个子类,希望根据给定的字符串创建一个特定类的实例 Superclass instantiateSubclass(string s); 我希望使用一个配置文件来实现这一点,而不是使用一个巨大的if级联 这允许我在不重新编译的情况下更改字符串s的可能值,我希望这会导致更简洁的代码 配置文件应该包含诸如“subclass 1”、“subclass 2”之类的字符串,但是如何根据字符串创建类呢 基本上我需要从字符串到类的映射,C++中有这个可能吗?我认为其他语言提供了解决这个问题的可能

我有一个超类的几个子类,希望根据给定的字符串创建一个特定类的实例

Superclass instantiateSubclass(string s);
我希望使用一个配置文件来实现这一点,而不是使用一个巨大的if级联

这允许我在不重新编译的情况下更改字符串s的可能值,我希望这会导致更简洁的代码

配置文件应该包含诸如“subclass 1”、“subclass 2”之类的字符串,但是如何根据字符串创建类呢

基本上我需要从字符串到类的映射,C++中有这个可能吗?我认为其他语言提供了解决这个问题的可能性,比如反射。

注册您的课程:

struct Base;

struct Derived1 : Base
{
    static Base * create() { return new Derived1; }
};

std::map<std::string, Base * (*)()> registry = { {"derived1", &Derived1::create},
                                                 /* ... */
                                               };

无论您是否使用配置文件,在某个时候都需要将字符串转换为“newsomeclass”调用[或类似的调用]。这将涉及到比较字符串,并选择正确的项来创建新的。一长串if/else if/else if。。。或者一个表,从中可以根据表中第二个条目的ouf得到的某个整数常量在switch语句中进行选择


当然,另一种可能性是将问题完全转移到另一个级别,并为每个类实现一个共享库,并根据其名称加载相关库,然后在共享库中拥有一个定义名称/序号的函数,您可以调用该函数“使我成为您的对象之一”

我以前也做过类似的事情。构建字符串(类型)/函数指针(工厂)对的无序_映射。每一个都指向一个创建该类型实例的静态函数

这仍然要求您拥有那些小存根工厂函数,通常它们是创建该类型的一行程序。宏可用于生成工厂方法

typedef std::unordered_map<std::string, Superclass *(*)()> TypeDirectory;
TypeDirectory types;

#define NAMEFACTORY(name_) static Superclass *Create() { return new name_; }

class Hello : public Superclass
{
   ...

   NAMEFACTORY(Hello)
};

static void RegisterFactoryNames()
{
  types.emplace_back("Hello", &Hello::Create);
  ...
}

static Superclass *MakeInstance(std::string &name)
{
  auto i = types.find(name);
  return i != types.end() ? types->second() : 0;
}
typedef std::无序映射类型目录;
类型目录类型;
#定义NAMEFACTORY(name_)静态超类*Create(){返回新名称}
类Hello:公共超类
{
...
名称工厂(你好)
};
静态无效RegisterFactoryNames()
{
types.emplace_back(“Hello”、&Hello::Create);
...
}
静态超类*MakeInstance(std::string和name)
{
自动i=类型。查找(名称);
返回i!=types.end()?types->second():0;
}
只有您知道factory数据的名称属于何处,在我的示例中,我没有将其“放”在任何内容中


注意:如果您使用的是MSVC 10或更低版本(2010或更低版本),则使用
类型因为emplace\u back在这些版本中的实现完全不正确。

使用以下代码,您可以注册从基本
接口派生的任意数量的类
,并使用从
注册类
返回的字符串实例化它们

缺点是不同平台之间的键可能不同,您必须知道每个类返回的typeid(CLASS).name()是什么,以确保您在配置文件中放置了正确的类键(它可能与类名不同)

阅读更多关于typeid的信息,了解这里发生了什么

   template <class INTERFACE> class factory
    {
    public:
        template <class CLASS> const std::string register_class()
        {
            static const std::string key = typeid(CLASS).name();
            class class_factory : public ifactory
            {
            private:
                virtual std::shared_ptr<INTERFACE> create() const
                {
                    return new CLASS;
                }
            };
            m_factory_map[key] = new class_factory;
            return key;
        }
        std::shared_ptr<INTERFACE> create(const std::string& key) const
        {
            const factory_map::const_iterator ifind = m_factory_map.find(key);
            if(ifind == m_factory_map.end())
                return 0;
            return ifind->second->create();
        }
    private:
        class ifactory
        {
        public:
            virtual ~ifactory() {}
            virtual std::shared_ptr<INTERFACE> create() const = 0;
        };
        typedef std::map<std::string, std::shared_ptr<ifactory> > factory_map;
        factory_map m_factory_map;
    };
模板类工厂
{
公众:
模板常量std::字符串寄存器\类()
{
静态常量std::string key=typeid(CLASS).name();
工厂类别:公共工厂
{
私人:
虚拟std::shared_ptr create()常量
{
返回新类;
}
};
m_-factory_-map[键]=新类_-factory;
返回键;
}
std::shared_ptr create(const std::string&key)const
{
常量工厂映射::常量迭代器ifind=m\u工厂映射.find(键);
如果(ifind==m_factory_map.end())
返回0;
返回ifind->second->create();
}
私人:
类工厂
{
公众:
虚拟~ifactory(){}
虚拟std::shared_ptr create()const=0;
};
typedef std::映射工厂图;
工厂地图,工厂地图;
};
像这样使用它

factory<MyInterface> fact;
const std::string key1 = fact.register_class<MyClass1>();
const std::string key2 = fact.register_class<MyClass2>();
const std::string key3 = fact.register_class<MyClass3>();
std::shared_ptr<MyInterface> p1 = fact.create(key1);
std::shared_ptr<MyInterface> p2 = fact.create(key2);
std::shared_ptr<MyInterface> p3 = fact.create(key3);
工厂事实;
const std::string key1=fact.register_class();
const std::string key2=fact.register_class();
const std::string key3=fact.register_class();
std::shared_ptr p1=fact.create(键1);
std::shared_ptr p2=fact.create(键2);
std::shared_ptr p3=事实。创建(键3);

< /代码>您想创建对象的类型(类名称)是由字符串指定的?没有在C++中添加子类而不需要重新编译的方法。您需要在某个地方创建一个具体类的实例,编译器需要查看它。或者你在说s.th。还有?我不想添加新的子类。子类已经存在。我想实例化一个由给定字符串指定的子类。我已经编辑了我的问题来澄清这一点。啊,我明白了,那么这就是关于配置类工厂的问题。然后像其他人在回答中提到的那样使用注册表。这是一种称为“工厂方法模式”的设计模式。RegisterFactoryNames()什么时候运行?@einpoklum您可以检查types.empty()并从
MakeInstance
调用它。但这不是线程安全的。如果需要线程安全,那么只需确保在
MakeInstance
之前的任何时间调用
RegisterFactoryNames
一次即可。
factory<MyInterface> fact;
const std::string key1 = fact.register_class<MyClass1>();
const std::string key2 = fact.register_class<MyClass2>();
const std::string key3 = fact.register_class<MyClass3>();
std::shared_ptr<MyInterface> p1 = fact.create(key1);
std::shared_ptr<MyInterface> p2 = fact.create(key2);
std::shared_ptr<MyInterface> p3 = fact.create(key3);