Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Obtain Type of Value Stored in YAML::Node for yaml-cpp

Tags:

c++

yaml-cpp

Given this code:

void LoadFromYaml(const YAML::Node& node){
    const YAML::Node& Data=node["Data"];
    if(Data){
        if(Data.ValueIsInt)// Do something with integer.
        if(Data.ValueIsFloat)// Do something with float.
        if(Data.ValueIsString)// Do something with string.
    }
}

How do I check is the data contained in the YAML Node 'Data' is an integer, float, or a string? Note: I don't want to check if the Node is a scalar, map, sequence, etc.

like image 725
ARandomFurry Avatar asked Oct 03 '22 10:10

ARandomFurry


2 Answers

You can try to convert the node to each type:

try {
  int value = data.as<int>();
  // do something
} catch (const BadConversion& e) {
  // do something else
}
like image 57
Jesse Beder Avatar answered Oct 07 '22 19:10

Jesse Beder


Throwing lots of exceptions while parsing can impact your parsing speed. Not sure why yamlcpp doesn't have an is_type<T> method or something similar to std::optional or std::expected getters

Hacky way to get around it though with Boost - injecting your own template specialisation into the YAML namespace and return boost::optional (or std::optional if you have C++17)

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

        const boost::optional<T> operator()() const
        {
            boost::optional<T> val;
            T t;
            if (node.m_pNode && convert<T>::decode(node, t))
                val = std::move(t);

            return val;
        }
    };

    // There is already a std::string partial specialisation, so we need a full specialisation here
    template <>
    struct as_if<std::string, boost::optional<std::string> >
    {
        explicit as_if(const Node& node_) : node(node_) {}
        const Node& node;

        const boost::optional<std::string> operator()() const
        {
            boost::optional<std::string> val;
            std::string t;
            if (node.m_pNode && convert<std::string>::decode(node, t))
                val = std::move(t);

            return val;
        }
    };
}

You can then run something like

boost::optional<bool> as_bool          = YAML::as_if<bool, boost::optional<bool> >(node)();
boost::optional<int> as_int            = YAML::as_if<int, boost::optional<int> >(node)();
boost::optional<double> as_double      = YAML::as_if<double, boost::optional<double> >(node)();
boost::optional<std::string> as_string = YAML::as_if<std::string, boost::optional<std::string> >(node)();

Total cost of construction here is 4 optional values + 4 default values. This may or may not be faster than dealing with the exceptions, I haven't tested.

like image 27
Andrew Lipscomb Avatar answered Oct 07 '22 17:10

Andrew Lipscomb