递归添加子树以提升属性树

时间:2018-06-13 12:08:33

标签: c++ boost boost-propertytree

我想在C ++中编写一个参数服务器,我可以递归地将参数树转储到属性树中,然后将其写入JSON文件。 转储功能如下所示:

 void Params::dump(string filename) {
   // Create a root
   pt::ptree root;

   // Fill the root with parameters
   mapToPt(curParams, root);

   // Write to cout
   pt::write_json(cout, root);
 }

mapToPt应该以递归的方式遍历我的参数服务器的层次结构并填充属性树,同时这样做:

  void Params::mapToPt(boost::shared_ptr<Params> curParams, pt::ptree &root) {
   // Fill current root with parameters from curParams ParameterMap
   map<string, boost::shared_ptr<Param> >::iterator it;
   for ( it = curParams->getParamMap().begin(); it != curParams-getParamMap().end(); it++ ) {
     root.put(it->first, it->second->getValue());
     cout << "Add Parameter: \n";
     cout << "Parameter name: " << it->first << "\n";
     cout << "Parameter value: " << it->second->getValue() << "\n";
   }

   // Recursively go through all children to do the same to them
   if(curParams->hasChildren()) { //ERROR LINE
     map<string, boost::shared_ptr<Params> >::iterator it;
     for ( it = curParams->getChildren().begin(); it != curParams-getChildren().end(); it++ ) {
       pt::ptree new_tree;
       root.add_child(it->second->getName(), new_tree);
       cout << "Add Child: \n";
       cout << "Child name: " << it->second->getName() << "\n";
       mapToPt(it->second, new_tree);
     }
   }
 }

我的问题是,一旦我进入递归,错误就会发生在随机行中,这些错误不会导致错误。 &#34; basic_string :: _ M_construct null无效&#34;是错误消息。我相信我可能会访问已删除的内容,这可能是由于我遍历属性树子的方式。 我这样做的方式是错误还是有另一种方法可以做到这一点?

谢谢。

1 个答案:

答案 0 :(得分:1)

为什么mapToPt成员也需要指向Params实例的指针?

Anyhoops,有很多混乱。

在设计级别,您的Params类型看起来无法决定它是否为叶子节点。此外,它还受到了#34; Quasi Classes&#34;设计,其中getters基本上保证不存在类不变量。在这种情况下,更喜欢只有一个带有成员字段的结构。

  

注意,如果您未能从getParamMap()getChildren()返回引用,则两个循环中已经有Undefined Behaviour,因为迭代器会指向不存在的容器副本。

     

你应该检查一下。另外,请参阅下面的工作演示

在实施层面,这会给您带来问题:

        pt::ptree new_tree;
        root.add_child(it->second->getName(), new_tree);

add_child会插入new_tree的副本。将来对new_tree的任何修改都无效。相反,写一下:

        pt::ptree& new_tree = root.add_child(it->second->getName(), {});

此处,new_tree成为实际添加的树的引用

尝试修复

风格仍低于我的预期。我个人在这段代码中仔细审查了shared_ptr的用法。

但这可能对你有帮助:

<强> Live On Coliru

#include <boost/make_shared.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
#include <map>

namespace pt = boost::property_tree;

struct Param {
    std::string getValue() const { return "42"; }
};

struct Params {
    using ParamMap = std::map<std::string, boost::shared_ptr<Param> >;
    using Children = std::map<std::string, boost::shared_ptr<Params> >;

    Params(std::string name = "") : _name(name) {}

    std::string getName() const         { return _name; }

    ParamMap& getParamMap()             { return _map; } 
    ParamMap const& getParamMap() const { return _map; } 

    bool hasChildren() const            { return !_children.empty(); }
    Children& getChildren()             { return _children; } 
    Children const& getChildren() const { return _children; } 

    static void mapToPt(boost::shared_ptr<Params> curParams, pt::ptree &root);

  private:
    std::string _name;
    ParamMap _map;
    Children _children;
};

void Params::mapToPt(boost::shared_ptr<Params> curParams, pt::ptree &root) {
    // Fill current root with parameters from curParams ParameterMap
    std::map<std::string, boost::shared_ptr<Param> >::iterator it;
    for (it = curParams->getParamMap().begin(); it != curParams->getParamMap().end(); it++) {
        root.put(it->first, it->second->getValue());
        //std::cout << "Add Parameter: \n";
        //std::cout << "Parameter name: " << it->first << "\n";
        //std::cout << "Parameter value: " << it->second->getValue() << "\n";
    }

    // Recursively go through all children to do the same to them
    if (curParams->hasChildren()) {
        for (auto it = curParams->getChildren().begin(); it != curParams->getChildren().end(); it++) {
            pt::ptree& new_tree = root.add_child(it->second->getName(), {});
            //std::cout << "Add Child: \n";
            //std::cout << "Child name: " << it->second->getName() << "\n";
            mapToPt(it->second, new_tree);
        }
    }
}

int main() {
    auto a = boost::make_shared<Params>("rootparams");

    a->getParamMap().emplace("one", boost::make_shared<Param>());
    a->getParamMap().emplace("two", boost::make_shared<Param>());
    a->getParamMap().emplace("three", boost::make_shared<Param>());

    a->getChildren().emplace("child1", boost::make_shared<Params>("child1-name"))
        .first->second->getParamMap().emplace("four", boost::make_shared<Param>());

    a->getChildren().emplace("child2", boost::make_shared<Params>("child2-name"))
        .first->second->getParamMap().emplace("five", boost::make_shared<Param>());

    pt::ptree root;
    a->mapToPt(a, root);

    write_json(std::cout, root);
}

打印

{
    "one": "42",
    "three": "42",
    "two": "42",
    "child1-name": {
        "four": "42"
    },
    "child2-name": {
        "five": "42"
    }
}