Search code examples
c++yamlyaml-cpp

Updating node and values of YAML document with yaml-cpp


I need to parse some YAML in C++ (sort of newish to YAML). I'm looking to use yaml-cpp. My objectives are:

  • To create some generic/reusable utility functions to help parsing of this YAML.
  • To be able to update a YAML::Node and/or add missing values (by specifying default values)

If I take some example YAML, it might appear as:

...
  comms:
    messageBus:
      remoteHost: "message-bus"
      remotePort: 5672

or even:

...
  comms:

Nonetheless, I want to be able to update YAML reflecting default values. So to read the above YAML, the code would look something like:

auto &nodeComms = get_yaml_node_field_or_insert(node, "comms");
auto &nodeMessageBus = get_yaml_node_field_or_insert(nodeComms , "comms");
auto strRemoteHost = get_yaml_string_field_or_default_and_update(nodeMessageBus, "remoteHost", "127.0.0.1");
auto nRemotePort = get_yaml_uint16_field_or_default_and_update(nodeMessageBus, "remotePort", uint16_t{5672});

so that if the above code is run on the second example, the updated YAML would now be:

...
  comms:
    messageBus:
      remoteHost: "127.0.0.1"
      remotePort: 5672

For both get_yaml_string_field_or_default_and_update and get_yaml_uint16_field_or_default_and_update, it's fairly trivial to create a templated function to handle reading different value types and inserting them if necessary:

///////////////////////////////////////////////////////////////////////////
template <typename TYPE_IN, typename TYPE_RETURN>
TYPE_RETURN get_type_from_yaml_node_or_default_and_update(YAML::Node &node,
                                                         const char *pchFieldName,
                                                         TYPE_IN nValueDefault) noexcept
{
    if (!node)
    {
        assert(false);
        throw std::runtime_error("Invalid node");
    }
    if (!node[pchFieldName])
    {
        node[pchFieldName] = nValueDefault;
        return nValueDefault;
    }

    try
    {
        return node[pchFieldName].as<TYPE_RETURN>();
    }
    catch (const std::exception &e)
    {
        node[pchFieldName] = nValueDefault;
        return nValueDefault;
    }
    catch (...)
    {
        node[pchFieldName] = nValueDefault;
        return nValueDefault;
    }
}

The code I'm struggling with is get_yaml_node_field_or_insert:

///////////////////////////////////////////////////////////////////////////
YAML::Node  &get_yaml_node_field_or_insert(YAML::Node &node, const char *pchFieldName) noexcept
{
    if (!node)
    {
        assert(false);
        throw std::runtime_error("Invalid node");
    }

    // TODO: Either 1) Return reference if it exists or 2) insert if it does not and return reference.

    return node[pchFieldName];
}

It looks as if the operator[] does not return a reference according to API.

// indexing
template <typename Key>
const Node operator[](const Key& key) const;

template <typename Key>
Node operator[](const Key& key);

Any tips/suggestions would be greatly appreciated.


Solution

  • YAML::Node is a reference type already, so returning it from a function doesn’t make a deep copy. It’s also mutable, so you can just edit it and the change will update the root node.