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

Recursively adding subtrees to boost property tree

我想用 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);
     }
   }
 }

我的问题是,一旦我进入递归,就会在随机行出现错误,这不可能是错误的原因。 "basic_string::_M_construct null not valid" 是错误信息。我相信我可能会访问已删除的内容,这可能是由于我遍历 属性 树子项的方式所致。 我的做法是错误的还是有其他方法?

谢谢。

为什么 mapToPt 也是一个成员,而它也需要一个指向 Params 实例的指针?

哎呀,有点混乱。

在设计层面,您的 Params 类型看起来无法决定它是否是叶节点。此外,它受到 "Quasi Classes" 设计的影响,其中 getters 基本上保证没有 class 不变性。在这种情况下,宁愿只拥有一个包含成员字段的结构。

Note, if you fail to return by reference from getParamMap() and getChildren() then you already have Undefined Behaviour in both loops, because the iterators then point into non-existent copies of containers.

You should check this. Also, see my working demo below

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

        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"
    }
}