.. _program_listing_file_lib_options_value.hpp: Program Listing for File value.hpp ================================== |exhale_lsh| :ref:`Return to documentation for file ` (``lib/options/value.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp // --------------------------------------------------------------------- // This file is part of falcon-core. // // Copyright (C) 2015, 2016, 2017 Neuro-Electronics Research Flanders // // Falcon-server is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Falcon-server is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with falcon-core. If not, see . // --------------------------------------------------------------------- #pragma once #include #include #include #include #include #include #include "yaml-cpp/yaml.h" #include "../utilities/string.hpp" #include "units/units.hpp" #include "validation.hpp" namespace options { class ValueBase { public: ValueBase() {} virtual ~ValueBase() {} virtual void from_yaml(const YAML::Node &node) = 0; virtual YAML::Node to_yaml() const = 0; virtual bool is_nullable() const = 0; virtual bool is_null() const = 0; virtual void set_null() = 0; protected: virtual void unset_null() = 0; }; template T generic_fromyaml(const YAML::Node &node) { return node.as(); } template YAML::Node generic_toyaml(const T &x) { YAML::Node node; node = x; return node; } template class Value : public ValueBase { public: using ValueType = T; using ValidatorType = ValidatorFunc; Value(const T &value, ValidatorType validator = {}) : ValueBase(), validator_(validator) { set_value(value); } Value(ValidatorType validator = {}) : ValueBase(), validator_(validator) { if constexpr (!Nullable) { set_value(T()); } } T validate(T value) { if (validator_) { return validator_(value); } return value; } void from_yaml(const YAML::Node &node) override { set_value(generic_fromyaml(node)); } YAML::Node to_yaml() const override { YAML::Node node; if (is_nullable() && is_null()) { return node; } node = generic_toyaml(value_); return node; } const T &get_value() const { if constexpr (Nullable) { if (is_null()) { throw std::runtime_error("Value has not been set."); } } return value_; } const T &operator()() const { return get_value(); } void set_value(const T &value) { value_ = validate(value); if constexpr (Nullable) { unset_null(); } } Value &operator=(const T &value) { set_value(value); return (*this); } Value &operator=(const Value &value) { set_value(value()); return (*this); } void set_validator(ValidatorType validator = {}) { validator_ = validator; } bool is_nullable() const final { return Nullable; } bool is_null() const final { if constexpr (Nullable) { return value_is_null_; } else { throw std::runtime_error("Value::is_null : value is not nullable."); } } void set_null() final { if constexpr (Nullable) { value_is_null_ = true; } else { throw std::runtime_error("Value::set_null : value is not nullable."); } } protected: void unset_null()final { if constexpr (Nullable) { value_is_null_ = false; } else { throw std::runtime_error("Value::unset_null : value is not nullable."); } } private: T value_; ValidatorType validator_; bool value_is_null_ = true; }; using Bool = Value; using NullableBool = Value; using Double = Value; using NullableDouble = Value; using Int = Value; using NullableInt = Value; using String = Value; using NullableString = Value; template std::vector vector_fromyaml(const YAML::Node &node) { if (node.IsSequence()) { return node.as>(); } else if (node.IsNull()) { return std::vector(); } else { return std::vector({node.as()}); } } template class Vector : public Value, Nullable> { public: Vector(const std::vector &value = {}, ValidatorFunc> validator = {}) : Value, Nullable>(value, validator) {} void from_yaml(const YAML::Node &node) override { this->set_value(vector_fromyaml(node)); } }; template class ValueMap : public ValueBase { public: using ValueType = typename VT::ValueType; ValueMap(const VT &value = VT(), const std::map map = {}) : ValueBase(), default_(value) { set_map(map); } void set_map(std::map m) { std::map tmp; for (auto &k : m) { tmp.emplace(k.first, default_); tmp[k.first] = k.second; } map_ = tmp; } virtual void from_yaml(const YAML::Node &node) { if (!node.IsMap()) { throw std::runtime_error("Not a map"); } set_map(node.as>()); } virtual YAML::Node to_yaml() const { YAML::Node node = YAML::Node(YAML::NodeType::Map); for (auto &k : map_) { node[k.first] = k.second.to_yaml(); } return node; } VT &operator[](const std::string &key) { // if key is not in map if (!map_.count(key)) { map_.emplace(key, default_); } return map_[key]; } std::map get_map() const { std::map m; for (auto &k : map_) { m[k.first] = k.second(); } return m; } std::map operator()() const { return get_map(); } virtual bool is_nullable() const { return false; } virtual bool is_null() const { throw std::runtime_error("Not nullable."); } virtual void set_null() { throw std::runtime_error("Not nullable."); } protected: virtual void unset_null() { throw std::runtime_error("Not nullable."); } protected: std::map map_; VT default_; }; template class measurement_toyaml { public: measurement_toyaml(units::precise_unit u) : units_(u) {} YAML::Node operator()(const T &x) { YAML::Node node; node = std::to_string(x) + " " + units::to_string(units_); return node; } protected: units::precise_unit units_; }; template class measurement_fromyaml { public: measurement_fromyaml(units::precise_unit u) : units_(u) {} T operator()(const YAML::Node &node) { std::string s = node.as(); auto m = units::measurement_from_string(s); if (!units_.has_same_base(m.units())) { throw std::runtime_error("Incorrect units. Not same base."); } double value = m.value_as(units_); if (std::isnan(value)) { throw std::runtime_error("Incorrect units: NaN"); } return T(value); } protected: units::precise_unit units_; }; template class Measurement : public Value { public: Measurement(T value, std::string u, ValidatorFunc validator = {}, std::vector alt = {}) : Value(value, validator), index_(0) { all_unit_repr_.reserve(1 + alt.size()); all_unit_repr_.push_back(u); all_unit_repr_.insert(all_unit_repr_.end(), alt.begin(), alt.end()); for (auto &k : all_unit_repr_) { if (k.size() == 0) { all_unit_.push_back(units::precise::one); } else { all_unit_.push_back(units::unit_from_string(k)); } } repr_unit_ = all_unit_[0]; repr_unit_str_ = all_unit_repr_[0]; } void set_repr_unit(std::string s) { if (s.size() == 0) { return; } // check that it is the same base units auto u = units::unit_from_string(s); if (!u.equivalent_non_counting(all_unit_[index_])) { throw std::runtime_error("Representation unit (" + units::to_string(u) + ") are not compatible with base unit (" + units::to_string(all_unit_[index_]) + ")."); } repr_unit_ = u; repr_unit_str_ = s; } units::precise_unit unit() const { return all_unit_[index_]; } std::string to_string() const { double factor = units::convert(repr_unit_, all_unit_[index_]); std::ostringstream out; out << T(this->get_value() / factor); return (out.str() + " " + repr_unit_str_); } void from_yaml(const YAML::Node &node) override { std::string s = node.as(); // split number from unit std::regex re("^\\s*([+-]?[0-9,.]*(?:e[+-]?[0-9]*)?)\\s*(.*)?$"); std::smatch m; if (!std::regex_match(s, m, re)) { throw std::runtime_error("Could not convert yaml to value."); } size_t idx = 0; bool matched = false; double factor = 1.; if (m.size() > 2 && m[2].str().size() > 0) { auto u = units::unit_from_string(m[2].str()); for (idx = 0; idx < all_unit_.size(); ++idx) { if ((matched = u.equivalent_non_counting(all_unit_[idx]))) { factor = units::convert(u, all_unit_[idx]); break; } } if (!matched) { throw std::runtime_error( "Representation unit (" + units::to_string(u) + ") are not compatible with any permissable unit."); } } this->set_value(from_string(m[1].str()) * factor); this->index_ = idx; this->set_repr_unit(m[2].str()); } YAML::Node to_yaml() const override { YAML::Node node; node = this->to_string(); return node; } Measurement &operator=(const T &value) { this->set_value(value); return (*this); } Measurement &operator=(const Value &value) { this->set_value(value()); return (*this); } protected: size_t index_; units::precise_unit repr_unit_; std::string repr_unit_str_; std::vector all_unit_repr_; std::vector all_unit_; }; } // namespace options