Program Listing for File value.hpp¶
↰ Return to documentation for file (lib/options/value.hpp
)
// ---------------------------------------------------------------------
// 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 <http://www.gnu.org/licenses/>.
// ---------------------------------------------------------------------
#pragma once
#include <functional>
#include <regex>
#include <stdexcept>
#include <string>
#include <map>
#include <vector>
#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 <typename T> T generic_fromyaml(const YAML::Node &node) {
return node.as<T>();
}
template <typename T> YAML::Node generic_toyaml(const T &x) {
YAML::Node node;
node = x;
return node;
}
template <typename T, bool Nullable = true> class Value : public ValueBase {
public:
using ValueType = T;
using ValidatorType = ValidatorFunc<T>;
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<T>(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<T, Nullable> &operator=(const T &value) {
set_value(value);
return (*this);
}
Value<T, Nullable> &operator=(const Value<T> &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<bool, false>;
using NullableBool = Value<bool, true>;
using Double = Value<double, false>;
using NullableDouble = Value<double, true>;
using Int = Value<int, false>;
using NullableInt = Value<int, true>;
using String = Value<std::string, false>;
using NullableString = Value<std::string, true>;
template <typename T> std::vector<T> vector_fromyaml(const YAML::Node &node) {
if (node.IsSequence()) {
return node.as<std::vector<T>>();
} else if (node.IsNull()) {
return std::vector<T>();
} else {
return std::vector<T>({node.as<T>()});
}
}
template <typename T, bool Nullable = false>
class Vector : public Value<std::vector<T>, Nullable> {
public:
Vector(const std::vector<T> &value = {},
ValidatorFunc<std::vector<T>> validator = {})
: Value<std::vector<T>, Nullable>(value, validator) {}
void from_yaml(const YAML::Node &node) override {
this->set_value(vector_fromyaml<T>(node));
}
};
template <typename VT>
class ValueMap : public ValueBase {
public:
using ValueType = typename VT::ValueType;
ValueMap(const VT &value = VT(),
const std::map<std::string, ValueType> map = {})
: ValueBase(), default_(value) {
set_map(map);
}
void set_map(std::map<std::string, ValueType> m) {
std::map<std::string, VT> 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<std::map<std::string, ValueType>>());
}
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<std::string, ValueType> get_map() const {
std::map<std::string, ValueType> m;
for (auto &k : map_) {
m[k.first] = k.second();
}
return m;
}
std::map<std::string, ValueType> 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<std::string, VT> map_;
VT default_;
};
template <typename T> 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 <typename T> class measurement_fromyaml {
public:
measurement_fromyaml(units::precise_unit u) : units_(u) {}
T operator()(const YAML::Node &node) {
std::string s = node.as<std::string>();
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 <typename T, bool Nullable = false>
class Measurement : public Value<T, Nullable> {
public:
Measurement(T value, std::string u, ValidatorFunc<T> validator = {},
std::vector<std::string> alt = {})
: Value<T, Nullable>(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<std::string>();
// 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<T>(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<T, Nullable> &operator=(const T &value) {
this->set_value(value);
return (*this);
}
Measurement<T, Nullable> &operator=(const Value<T> &value) {
this->set_value(value());
return (*this);
}
protected:
size_t index_;
units::precise_unit repr_unit_;
std::string repr_unit_str_;
std::vector<std::string> all_unit_repr_;
std::vector<units::precise_unit> all_unit_;
};
} // namespace options