c++yaml-cpp

Obtain Type of Value Stored in YAML::Node for 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.


Solution

  • Throwing lots of exceptions while parsing can impact your parsing speed. Not sure why yaml-cpp 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 std::optional (or boost::optional if you don't have C++17)

    // yaml-cpp/patch.hpp

    #pragma once
    
    #include <yaml-cpp/yaml.h>
    
    #include <optional>
    #include <string>
    
    namespace YAML {
    
    template <typename T>
    struct as_if<T, std::optional<T>> {
      explicit as_if(const Node &node) :
          node(node) {
      }
      const Node &node;
    
      std::optional<T> operator()() const {
        std::optional<T> val;
        T decoded;
        if ((node.m_pNode != nullptr) && convert<T>::decode(node, decoded)) {
          val = std::move(decoded);
        }
    
        return val;
      }
    };
    
    // There is already a string partial specialisation, so we need a full specialisation here
    template <>
    struct as_if<std::string, std::optional<std::string>> {
      explicit as_if(const Node &node) :
          node(node) {
      }
      const Node &node;
    
      std::optional<std::string> operator()() const {
        std::optional<std::string> val;
        std::string decoded;
        if ((node.m_pNode != nullptr) && convert<std::string>::decode(node, decoded)) {
          val = std::move(decoded);
        }
    
        return val;
      }
    };
    
    } // namespace YAML
    
    

    You can then run something like

    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)();
    

    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.