Skip to content

CNR-STIIMA-IRAS/cnr_yaml

Repository files navigation

cnr_yaml

Build Status - Plain CMake codecov Codacy Badge FOSSA Status

A package that extends the yaml-cpp. The package provides a few functions to ease the manipulation of a YAML::Node.

The Design

The package provides two main headers

cnr_yaml.h: this provides a few functions to ease the get/set of parameters from/to a YAML::Node.

node_utils.h: this provides a few functions to ease the management of trees of YAML::Node.

Get/Set Functions

The package provides a unique interface to get and set parameters from/to a YAML::Node.

The core is the file cnr_yaml.h. The file provides four main functions:

template <typename T>
bool get(const YAML::Node& node, T& ret, std::string& what, const bool& implicit_cast_if_possible);

template <typename T>
bool set(const T& value, YAML::Node& ret, std::string& what);

The functions extend the common access to the node template<typename T> T YAML::Node().as<T>(), similarly to the template<typename T> bool YAML::decode<T>() it returns a true/false.

The main difference consists of:

  • Error and exceptions are caught, and the std::string& what reports what was the error.

  • There is the possibility to allow an implicit cast. For example, if the node stores a vector [1., 2., 3.] and you ask for a std::vector<double>. See below for the step-by-step process description.

  • It implements also the get/set for all the Eigen::Matrix<>.

Finally, the header implements a structure to get the value as an std::option, as shown by Andrew Lipscomb in stackoverflow.

namespace YAML
{
template <typename T>
struct as_if<T, std::optional<T>>
{
  explicit as_if(const Node& node);
  const Node& node;

  std::optional<T> operator()() const;
};

}

This template is specialized here. This allows you to write code without exception-catching mechanisms whaen you are not sure about what is stored inside the yaml.

std::optional<bool> as_bool          = YAML::as_if<bool, std::optional<bool> >(node)();
std::optional<int> as_int            = YAML::as_if<int, std::optional<int> >(node)();
std::optional<double> as_double      = YAML::as_if<double, std::optional<double> >(node)();
std::optional<std::string> as_string = YAML::as_if<std::string, std::optional<std::string> >(node)();
if (as_bool)
{
  tree = rclcpp::ParameterValue(as_bool.value());
}
else if (as_int)
{
  tree= rclcpp::ParameterValue(as_int.value());
}
else if (as_double)
{
  tree= rclcpp::ParameterValue(as_double.value());
}
else if (as_string)
{
  tree= rclcpp::ParameterValue(as_string.value());
}
else
{
  what = "The node is a scalar, but the conversion to rclcpp::Parameter failed";
  return false;
}

NOTE In yaml-cpp everything can be a std::string. The as_string in the example will be always true! Therefore, pay attention to the verification order in the case!

Node Management Utilities

A few functions to ease managing nodes are in the header nodes_utils.h

  • Merging nodes and override values if the key tree is the same.
const YAML::Node merge_nodes(const YAML::Node& default_node, const YAML::Node& override_node);
  • Add a tree of keys with empty nodes to a node.
YAML::Node init_tree(const std::vector<std::string> seq, const YAML::Node& node);
  • Get the value of a leaf of the node, using a key with delimiters to access it.
bool get_leaf(const YAML::Node& node, const std::string& key, YAML::Node& leaf, std::string& what, const std::string& delimeters = "/.");

For example, you get another_int2 from the following yaml

nested_param:
  another_int: 7
  another_int2: 7.0
  nested_param:
    another_int: 7
    another_int2: 7.0

by calling

get_leaf(root_node, "nested_param/nested_param/another_int2", output, what, "/");

if you prefer to use another delimiter, you can do it

get_leaf(root_node, "nested_param.nested_param.another_int2", output, what, ".");

The errors are caught and put in the what return variable.

  • Get the value of a leaf of the node as the previous, but the key is already split in a vector of strings
YAML::Node get_leaf(const std::vector<std::string>& keys, const YAML::Node& node);
  • Get the iterator to a value inside a node.
YAML::iterator get_node(const std::string& key, YAML::iterator& node_begin, YAML::iterator& node_end);

Complex Types

You must follow the standard way to allow the encoding and decoding of a complex type from YAML::Node. Here a simple example

namespace YAML
{
template <>
struct convert<ComplexType>
{
  static Node encode(const ComplexType& rhs)
  {
    Node node;
    node["name"] = rhs.name;
    node["value"] = rhs.value;
    return node;
  }

  static bool decode(const Node& node, ComplexType& rhs)
  {
    if (!node.IsMap() || !node["name"] || !node["value"])
    {
      return false;
    }
    rhs.name = node["name"].as<std::string>();
    rhs.value = node["value"].as<double>();
    return true;
  }
};
}  // namespace YAML

Implicit Cast

To enable the implicit cast you must define what are the types you consider equivalent by the specialization of two templates as follows:

namespace cnr { 
namespace yaml 
{
template <>
struct decoding_type_variant_holder<int>
{
  using base = int;
  using variant = std::variant<int32_t, int64_t, int16_t, int8_t, double, long double, float>;
};

template <>
struct decoding_type_variant_holder<double>
{
  using base = int;
  using variant = std::variant<double, long double, float, int32_t, int64_t, int16_t, int8_t>;
};

}
}

Contact

mailto:[email protected]

License

FOSSA Status

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published