我正在开发一个程序,它必须根据定义每个对象的类型的列表初始化许多不同的对象。
执行此任务的代码如下所示:
// name is one entry of the type list
// itemList is a std::vector where the new items are appended
if(name == "foo")
{
initItem<FooObject>(itemList);
}
else if(name == "bar")
{
initItem<BarObject>(itemList);
}
else if(name == "baz")
{
initItem<Object3>(itemList);
}
....
initItem(ItemList)分配类型为T的对象并将其附加到itemList。
在代码中的其他位置,对于不同的对象类型存在类似的条件语句。 对于添加的每个新对象类型,我必须在所有条件语句中添加一个新的else,这有点烦人。
有没有办法在某个地方定义某种类型的地图,就像
一样"foo", FooObject,
"bar", BarObject,
"baz", Object3,
然后模板/自动生成(可能是预处理器)if-else语句,所以我不必每次都手动设置它们?
编辑:这是包含代码片段的整个方法(还有更多的if()语句都根据相同的主体工作。
bool Model::xml2Tree(const pugi::xml_node &xml_node, std::vector<TreeItem*> &parents)
{
bool all_ok = true;
bool sucess;
pugi::xml_node child_node = xml_node.first_child();
for (; child_node; child_node = child_node.next_sibling())
{
sucess = true;
bool ok = false;
std::string name = child_node.name();
if(name == "foo")
{
ok = initTreeItem<FooObject>(child_node, parents);
}
else if(name == "bar")
{
ok = initTreeItem<BarObject>(child_node, parents);
}
...
...
...
else
{
ok = false;
std::cout << "Unknown Element" << std::endl;
continue;
}
if(!sucess)
{
continue;
}
all_ok = all_ok && ok;
// recursiv
ok = xml2Tree(child_node, parents);
all_ok = all_ok && ok;
}
parents.pop_back();
return all_ok;
}
template<class T>
bool Model::initTreeItem(const pugi::xml_node &xml_node,
std::vector<TreeItem *> &parents)
{
bool ok = false;
T *pos = new T(parents.back());
parents.back()->appendChild(pos);
ok = pos->initFromXml(xml_node);
parents.push_back(pos);
return ok;
}
答案 0 :(得分:2)
首先,您可以在类型系统中对映射进行编码,如下所示:
template <typename T>
struct type_wrapper { using type = T; };
template <typename T>
inline constexpr type_wrapper<T> t{};
template <typename K, typename V>
struct pair
{
K _k;
V _v;
constexpr pair(K k, V v) : _k{k}, _v{v} { }
};
template <typename... Ts>
struct map : Ts...
{
constexpr map(Ts... xs) : Ts{xs}... { }
};
constexpr auto my_map = map{
pair{[]{ return "foo"; }, t<FooObject>},
pair{[]{ return "bar"; }, t<BarObject>},
pair{[]{ return "baz"; }, t<Object3>}
};
我们正在使用lambdas,因为它们在C ++ 17中隐式constexpr
,以模拟“constexpr
参数”。如果您不需要这个,可以在字符串文字上创建一个constexpr
包装器,然后使用它。
然后您可以使用以下内容完成映射:
template <typename... Pairs>
void for_kv_pairs(const std::string& name, map<Pairs...> m)
{
([&]<typename K, typename V>(const pair<K, V>& p)
{
if(name == p._k())
{
initItem<typename V::type>();
}
}(static_cast<const Pairs&>(m)), ...);
}
这是在lambda中使用 fold表达式而不是逗号运算符和C ++ 20模板语法。后者可以通过提供额外的实现函数来替换,以从K
预C ++ 20中检索V
和pair
。
用法:
template <typename X>
void initItem()
{
std::cout << typeid(X).name() << '\n';
}
struct FooObject { };
struct BarObject { };
struct Object3 { };
constexpr auto my_map = map{
pair{[]{ return "foo"; }, t<FooObject>},
pair{[]{ return "bar"; }, t<BarObject>},
pair{[]{ return "baz"; }, t<Object3>}
};
int main()
{
for_kv_pairs("bar", my_map);
}
输出:
9BarObject
答案 1 :(得分:1)
您可以使用高阶宏(或x-macros)来生成类似的代码,例如:
#define each_item(item, sep) \
item("foo", FooObject) sep \
item("bar", BarObject) sep \
item("baz", Object3)
#define item_init(item_name, item_type) \
if (name == item_name) { \
initItem<item_type>(itemList); \
}
each_item(item_init, else)