Прогулка по пути имен, чтобы получить глубокое значение от узла

Я нахожусь в процессе переноса консольного приложения PHP на C++, чтобы узнать больше о C++ и возродить свою старую любовь к этому языку.

Одна из вещей, которые мне нужны, - это пройти через проанализированное дерево YAML, чтобы получить элемент по его пути. В настоящее время я обрабатываю только строковые ключи и типы карт YAML, просто для простоты.

Вот тест, который я написал с помощью Catch для определения моей проблемы:

#define CATCH_CONFIG_MAIN

#include <yaml-cpp/yaml.h>
#include <boost/foreach.hpp>

#include "include/catch.hpp"

// In my actual implementation, this function is a method 
// of a class, and 'config' is a class member
// but the semantics and types are the same
YAML::Node lookup(YAML::Node config, std::vector<std::string>& path) {
    YAML::Node ptr = config;

    BOOST_FOREACH(std::string element, path)
    {
        ptr = ptr[element];
    }

    return ptr;
}


TEST_CASE ("Loading YAML data", "[loader]") {
    const char *str_config =
        "key:\n"
        "    child: Hello world\n"
    ;
    YAML::Node config = YAML::Load(str_config);

    std::vector<std::string> path;

    path.push_back("key");
    path.push_back("child");

    // the first one succeeds:
    REQUIRE( lookup(config, path).IsDefined() );

    // but the second one fails.
    REQUIRE( lookup(config, path).IsDefined() );
}

Теперь, если я запускаю этот тест, он завершается со следующим сообщением:

-------------------------------------------------------------------------------
Loading YAML data
-------------------------------------------------------------------------------
/home/gerard/work/z-cpp/test.cpp:26
...............................................................................

/home/gerard/work/z-cpp/test.cpp:42: FAILED:
  REQUIRE( lookup(config, path).IsDefined() )
with expansion:
  false

===============================================================================
test cases: 1 | 1 failed
assertions: 2 | 1 passed | 1 failed

Я выделил это, если я клонирую узел в методе поиска следующим образом:

YAML::Node ptr = YAML::Clone(config);

это работает просто отлично.

Что оно делает

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

Кроме того, если вектор инициализируется отдельно во второй раз другим экземпляром, он действует так же (ошибочно), так что это не ошибка вектора;)

Я немного погрузился в исходный код yaml-cpp и попытался выяснить, упускаю ли я некоторые очевидные указатели (каламбур) или неправильное использование API, но я не могу понять это...

Что он должен делать

Поскольку мой «поиск» — это просто операция чтения, я хотел бы иметь как можно больше вещей const и не изменять исходное состояние объекта. Кроме того, клонирование всего дерева сделает его очень дорогим, так как я планирую выполнять множество таких операций поиска во всем приложении...

Что я здесь упускаю?


person Gerard van Helden    schedule 30.05.2015    source источник


Ответы (1)


В yaml-cpp узлы являются ссылочными типами, поэтому operator= фактически изменяет их внутренности.

Часто это то, что вам нужно, но ваш пример показывает, что в некоторых случаях это приводит к действительно нелогичному поведению.

Я согласен, это странно. Я отправил проблему, чтобы подумать о том, как предотвратить это в интуитивном поведении.

Чтобы обойти это, в вашем примере вы можете переключиться на рекурсию:

template <typename Iter>
YAML::Node lookup(YAML::Node node, Iter start, Iter end) {
  if (start == end) {
    return node;
  }
  return lookup(node[*start], next(start), end);
}

...

vector<string> path = ...
YAML::Node value = lookup(config, path.begin(), path.end());
person Jesse Beder    schedule 30.05.2015
comment
А, теперь я понимаю проблему :) Спасибо и за альтернативную реализацию :) Я пытался возиться с указателями, чтобы обойти это, но тоже не смог заставить его работать. - person Gerard van Helden; 30.05.2015