我有一个属性向量,可以包含不同的类型:
class base_attribute_vector; // no template args template<typename T> class raw_attribute_vector : public base_attribute_vector; raw_attribute_vector<int> foo; raw_attribute_vector<std::string> foo;
根据类型的运行时输入,我想创建适当的数据结构.伪代码:
std::string type("int"); raw_attribute_vector<type> foo;
显然,这失败了.如果出现以下情况,一个简单但丑陋且难以维护的解决方法是运行时切换/链接:
base_attribute_vector *foo; if(type == "int") foo = new raw_attribute_vector<int>; else if(type == "string") ...
我读到了有关仿函数的运行时多态性,但发现它对于概念上容易的任务来说非常复杂.
使这项工作最好,最干净的方法是什么?我玩了boost :: hana,发现虽然我可以创建从字符串到类型的映射,但查找只能在编译时完成:
auto types = hana::make_map( hana::make_pair(BOOST_HANA_STRING("int"),hana::type_c<int>),hana::make_pair(BOOST_HANA_STRING("string"),hana::type_c<std::string>) );
所有可能的类型在编译时都是已知的.任何建议都非常感谢.在一个完美的解决方案中,我将在一个地方创建name->类型映射.之后,我会像这样使用它
std::vector<base_attribute_vector*> foo; foo.push_back(magic::make_templated<raw_attribute_vector,"int">); foo.push_back(magic::make_templated<raw_attribute_vector,"std::string">); foo[0]->insert(123); foo[1]->insert("bla"); foo[0]->print(); foo[1]->print();
这种魔法不需要在编译时发生.我的目标是拥有尽可能可读的代码.
解决方法
enum class Type { Int,String,// ... Unknown }; Type TypeFromString(const std::string& s) { if (s == "int") { return Type::Int; } if (s == "string") { return Type::String; } // ... return Type::Unknown; } template <template <typename> class> struct base_of; template <template <typename> class C> using base_of_t = typename base_of<C>::type;
然后是通用工厂
template <template <typename> class C> std::unique_ptr<base_of_t<C>> make_templated(const std::string& typeStr) { Type type = TypeFromString(typeStr); static const std::map<Type,std::function<std::unique_ptr<base_of_t<C>>()>> factory{ {Type::Int,[] { return std::make_unique<C<int>>(); } },{Type::String,[] { return std::make_unique<C<std::string>>(); } },// ... {Type::Unknown,[] { return nullptr; } } }; return factory.at(type)(); }
每个基地都需要专业化:
template <> struct base_of<raw_attribute_vector> { using type = base_attribute_vector; };
然后
auto p = make_templated<raw_attribute_vector>(s);