从列表/地图自动生成条件表达式

时间:2018-03-19 11:01:19

标签: c++ templates c-preprocessor

我正在开发一个程序,它必须根据定义每个对象的类型的列表初始化许多不同的对象。

执行此任务的代码如下所示:

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

2 个答案:

答案 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中检索Vpair

用法:

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

live example on wandbox.org

答案 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)