Рекурсивное добавление поддеревьев для увеличения дерева свойств - PullRequest
0 голосов
/ 13 июня 2018

Я хочу написать сервер параметров в 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» - это сообщение об ошибке.Я полагаю, что я могу получить доступ к удаленному контенту, и это может быть связано с тем, как я выполняю итерацию по дочерним элементам дерева свойств.Мой способ сделать это неправильно или есть другой способ сделать это?

Спасибо.

1 Ответ

0 голосов
/ 13 июня 2018

Почему mapToPt является участником, когда он также ожидает указатель на Params экземпляр?

В любом случае, существует некоторая путаница.

На уровне проектирования ваш тип Params выглядит так, как будто он не может решить, является ли он листовым узлом или нет.Кроме того, он страдает от дизайна «квази-классов», где геттеры по существу гарантируют, что не возможен инвариант класса.В таких случаях предпочитайте просто иметь структуру с полями-членами.

Обратите внимание: если вам не удастся вернуть по ссылке из getParamMap() и getChildren(), то у вас уже есть Неопределенное поведение в обоих циклах, поскольку итераторы затем указывают на несуществующие копии контейнеров.

Вы должны проверить это.Также см. Мою рабочую демонстрацию ниже

На уровне реализации это вызывает у вас проблемы:

        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На 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"
    }
}
...