470 lines
14 KiB
C++
470 lines
14 KiB
C++
#ifndef NODE_CONVERT_H_62B23520_7C8E_11DE_8A39_0800200C9A66
|
|
#define NODE_CONVERT_H_62B23520_7C8E_11DE_8A39_0800200C9A66
|
|
|
|
#if defined(_MSC_VER) || \
|
|
(defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
|
|
(__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
|
|
#pragma once
|
|
#endif
|
|
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <list>
|
|
#include <map>
|
|
#include <unordered_map>
|
|
#include <sstream>
|
|
#include <type_traits>
|
|
#include <valarray>
|
|
#include <vector>
|
|
|
|
#if __cplusplus >= 201703L
|
|
#include <string_view>
|
|
#endif
|
|
|
|
#include "yaml-cpp/binary.h"
|
|
#include "yaml-cpp/node/impl.h"
|
|
#include "yaml-cpp/node/iterator.h"
|
|
#include "yaml-cpp/node/node.h"
|
|
#include "yaml-cpp/node/type.h"
|
|
#include "yaml-cpp/null.h"
|
|
|
|
|
|
namespace YAML {
|
|
class Binary;
|
|
struct _Null;
|
|
template <typename T>
|
|
struct convert;
|
|
} // namespace YAML
|
|
|
|
namespace YAML {
|
|
namespace conversion {
|
|
inline bool IsInfinity(const std::string& input) {
|
|
return input == ".inf" || input == ".Inf" || input == ".INF" ||
|
|
input == "+.inf" || input == "+.Inf" || input == "+.INF";
|
|
}
|
|
|
|
inline bool IsNegativeInfinity(const std::string& input) {
|
|
return input == "-.inf" || input == "-.Inf" || input == "-.INF";
|
|
}
|
|
|
|
inline bool IsNaN(const std::string& input) {
|
|
return input == ".nan" || input == ".NaN" || input == ".NAN";
|
|
}
|
|
}
|
|
|
|
// Node
|
|
template <>
|
|
struct convert<Node> {
|
|
static Node encode(const Node& rhs) { return rhs; }
|
|
|
|
static bool decode(const Node& node, Node& rhs) {
|
|
rhs.reset(node);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// std::string
|
|
template <>
|
|
struct convert<std::string> {
|
|
static Node encode(const std::string& rhs) { return Node(rhs); }
|
|
|
|
static bool decode(const Node& node, std::string& rhs) {
|
|
if (!node.IsScalar())
|
|
return false;
|
|
rhs = node.Scalar();
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// C-strings can only be encoded
|
|
template <>
|
|
struct convert<const char*> {
|
|
static Node encode(const char* rhs) { return Node(rhs); }
|
|
};
|
|
|
|
template <>
|
|
struct convert<char*> {
|
|
static Node encode(const char* rhs) { return Node(rhs); }
|
|
};
|
|
|
|
template <std::size_t N>
|
|
struct convert<char[N]> {
|
|
static Node encode(const char* rhs) { return Node(rhs); }
|
|
};
|
|
|
|
#if __cplusplus >= 201703L
|
|
template <>
|
|
struct convert<std::string_view> {
|
|
static Node encode(std::string_view rhs) { return Node(std::string(rhs)); }
|
|
|
|
static bool decode(const Node& node, std::string_view& rhs) {
|
|
if (!node.IsScalar())
|
|
return false;
|
|
rhs = node.Scalar();
|
|
return true;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
template <>
|
|
struct convert<_Null> {
|
|
static Node encode(const _Null& /* rhs */) { return Node(); }
|
|
|
|
static bool decode(const Node& node, _Null& /* rhs */) {
|
|
return node.IsNull();
|
|
}
|
|
};
|
|
|
|
namespace conversion {
|
|
template <typename T>
|
|
typename std::enable_if< std::is_floating_point<T>::value, void>::type
|
|
inner_encode(const T& rhs, std::stringstream& stream){
|
|
if (std::isnan(rhs)) {
|
|
stream << ".nan";
|
|
} else if (std::isinf(rhs)) {
|
|
if (std::signbit(rhs)) {
|
|
stream << "-.inf";
|
|
} else {
|
|
stream << ".inf";
|
|
}
|
|
} else {
|
|
stream << rhs;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
typename std::enable_if<!std::is_floating_point<T>::value, void>::type
|
|
inner_encode(const T& rhs, std::stringstream& stream){
|
|
stream << rhs;
|
|
}
|
|
|
|
template <typename T>
|
|
typename std::enable_if<(std::is_same<T, unsigned char>::value ||
|
|
std::is_same<T, signed char>::value), bool>::type
|
|
ConvertStreamTo(std::stringstream& stream, T& rhs) {
|
|
int num;
|
|
if ((stream >> std::noskipws >> num) && (stream >> std::ws).eof()) {
|
|
if (num >= (std::numeric_limits<T>::min)() &&
|
|
num <= (std::numeric_limits<T>::max)()) {
|
|
rhs = static_cast<T>(num);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
typename std::enable_if<!(std::is_same<T, unsigned char>::value ||
|
|
std::is_same<T, signed char>::value), bool>::type
|
|
ConvertStreamTo(std::stringstream& stream, T& rhs) {
|
|
if ((stream >> std::noskipws >> rhs) && (stream >> std::ws).eof()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#define YAML_DEFINE_CONVERT_STREAMABLE(type, negative_op) \
|
|
template <> \
|
|
struct convert<type> { \
|
|
\
|
|
static Node encode(const type& rhs) { \
|
|
std::stringstream stream; \
|
|
stream.precision(std::numeric_limits<type>::max_digits10); \
|
|
conversion::inner_encode(rhs, stream); \
|
|
return Node(stream.str()); \
|
|
} \
|
|
\
|
|
static bool decode(const Node& node, type& rhs) { \
|
|
if (node.Type() != NodeType::Scalar) { \
|
|
return false; \
|
|
} \
|
|
const std::string& input = node.Scalar(); \
|
|
std::stringstream stream(input); \
|
|
stream.unsetf(std::ios::dec); \
|
|
if ((stream.peek() == '-') && std::is_unsigned<type>::value) { \
|
|
return false; \
|
|
} \
|
|
if (conversion::ConvertStreamTo(stream, rhs)) { \
|
|
return true; \
|
|
} \
|
|
if (std::numeric_limits<type>::has_infinity) { \
|
|
if (conversion::IsInfinity(input)) { \
|
|
rhs = std::numeric_limits<type>::infinity(); \
|
|
return true; \
|
|
} else if (conversion::IsNegativeInfinity(input)) { \
|
|
rhs = negative_op std::numeric_limits<type>::infinity(); \
|
|
return true; \
|
|
} \
|
|
} \
|
|
\
|
|
if (std::numeric_limits<type>::has_quiet_NaN) { \
|
|
if (conversion::IsNaN(input)) { \
|
|
rhs = std::numeric_limits<type>::quiet_NaN(); \
|
|
return true; \
|
|
} \
|
|
} \
|
|
\
|
|
return false; \
|
|
} \
|
|
}
|
|
|
|
#define YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(type) \
|
|
YAML_DEFINE_CONVERT_STREAMABLE(type, -)
|
|
|
|
#define YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(type) \
|
|
YAML_DEFINE_CONVERT_STREAMABLE(type, +)
|
|
|
|
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(int);
|
|
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(short);
|
|
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long);
|
|
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long long);
|
|
YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned);
|
|
YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned short);
|
|
YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned long);
|
|
YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned long long);
|
|
|
|
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(char);
|
|
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(signed char);
|
|
YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned char);
|
|
|
|
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(float);
|
|
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(double);
|
|
YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long double);
|
|
|
|
#undef YAML_DEFINE_CONVERT_STREAMABLE_SIGNED
|
|
#undef YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED
|
|
#undef YAML_DEFINE_CONVERT_STREAMABLE
|
|
|
|
// bool
|
|
template <>
|
|
struct convert<bool> {
|
|
static Node encode(bool rhs) { return rhs ? Node("true") : Node("false"); }
|
|
|
|
YAML_CPP_API static bool decode(const Node& node, bool& rhs);
|
|
};
|
|
|
|
// std::map
|
|
template <typename K, typename V, typename C, typename A>
|
|
struct convert<std::map<K, V, C, A>> {
|
|
static Node encode(const std::map<K, V, C, A>& rhs) {
|
|
Node node(NodeType::Map);
|
|
for (const auto& element : rhs)
|
|
node.force_insert(element.first, element.second);
|
|
return node;
|
|
}
|
|
|
|
static bool decode(const Node& node, std::map<K, V, C, A>& rhs) {
|
|
if (!node.IsMap())
|
|
return false;
|
|
|
|
rhs.clear();
|
|
for (const auto& element : node)
|
|
#if defined(__GNUC__) && __GNUC__ < 4
|
|
// workaround for GCC 3:
|
|
rhs[element.first.template as<K>()] = element.second.template as<V>();
|
|
#else
|
|
rhs[element.first.as<K>()] = element.second.as<V>();
|
|
#endif
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// std::unordered_map
|
|
template <typename K, typename V, typename H, typename P, typename A>
|
|
struct convert<std::unordered_map<K, V, H, P, A>> {
|
|
static Node encode(const std::unordered_map<K, V, H, P, A>& rhs) {
|
|
Node node(NodeType::Map);
|
|
for (const auto& element : rhs)
|
|
node.force_insert(element.first, element.second);
|
|
return node;
|
|
}
|
|
|
|
static bool decode(const Node& node, std::unordered_map<K, V, H, P, A>& rhs) {
|
|
if (!node.IsMap())
|
|
return false;
|
|
|
|
rhs.clear();
|
|
for (const auto& element : node)
|
|
#if defined(__GNUC__) && __GNUC__ < 4
|
|
// workaround for GCC 3:
|
|
rhs[element.first.template as<K>()] = element.second.template as<V>();
|
|
#else
|
|
rhs[element.first.as<K>()] = element.second.as<V>();
|
|
#endif
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// std::vector
|
|
template <typename T, typename A>
|
|
struct convert<std::vector<T, A>> {
|
|
static Node encode(const std::vector<T, A>& rhs) {
|
|
Node node(NodeType::Sequence);
|
|
for (const auto& element : rhs)
|
|
node.push_back(element);
|
|
return node;
|
|
}
|
|
|
|
static bool decode(const Node& node, std::vector<T, A>& rhs) {
|
|
if (!node.IsSequence())
|
|
return false;
|
|
|
|
rhs.clear();
|
|
for (const auto& element : node)
|
|
#if defined(__GNUC__) && __GNUC__ < 4
|
|
// workaround for GCC 3:
|
|
rhs.push_back(element.template as<T>());
|
|
#else
|
|
rhs.push_back(element.as<T>());
|
|
#endif
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// std::list
|
|
template <typename T, typename A>
|
|
struct convert<std::list<T,A>> {
|
|
static Node encode(const std::list<T,A>& rhs) {
|
|
Node node(NodeType::Sequence);
|
|
for (const auto& element : rhs)
|
|
node.push_back(element);
|
|
return node;
|
|
}
|
|
|
|
static bool decode(const Node& node, std::list<T,A>& rhs) {
|
|
if (!node.IsSequence())
|
|
return false;
|
|
|
|
rhs.clear();
|
|
for (const auto& element : node)
|
|
#if defined(__GNUC__) && __GNUC__ < 4
|
|
// workaround for GCC 3:
|
|
rhs.push_back(element.template as<T>());
|
|
#else
|
|
rhs.push_back(element.as<T>());
|
|
#endif
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// std::array
|
|
template <typename T, std::size_t N>
|
|
struct convert<std::array<T, N>> {
|
|
static Node encode(const std::array<T, N>& rhs) {
|
|
Node node(NodeType::Sequence);
|
|
for (const auto& element : rhs) {
|
|
node.push_back(element);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
static bool decode(const Node& node, std::array<T, N>& rhs) {
|
|
if (!isNodeValid(node)) {
|
|
return false;
|
|
}
|
|
|
|
for (auto i = 0u; i < node.size(); ++i) {
|
|
#if defined(__GNUC__) && __GNUC__ < 4
|
|
// workaround for GCC 3:
|
|
rhs[i] = node[i].template as<T>();
|
|
#else
|
|
rhs[i] = node[i].as<T>();
|
|
#endif
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
static bool isNodeValid(const Node& node) {
|
|
return node.IsSequence() && node.size() == N;
|
|
}
|
|
};
|
|
|
|
|
|
// std::valarray
|
|
template <typename T>
|
|
struct convert<std::valarray<T>> {
|
|
static Node encode(const std::valarray<T>& rhs) {
|
|
Node node(NodeType::Sequence);
|
|
for (const auto& element : rhs) {
|
|
node.push_back(element);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
static bool decode(const Node& node, std::valarray<T>& rhs) {
|
|
if (!node.IsSequence()) {
|
|
return false;
|
|
}
|
|
|
|
rhs.resize(node.size());
|
|
for (auto i = 0u; i < node.size(); ++i) {
|
|
#if defined(__GNUC__) && __GNUC__ < 4
|
|
// workaround for GCC 3:
|
|
rhs[i] = node[i].template as<T>();
|
|
#else
|
|
rhs[i] = node[i].as<T>();
|
|
#endif
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
// std::pair
|
|
template <typename T, typename U>
|
|
struct convert<std::pair<T, U>> {
|
|
static Node encode(const std::pair<T, U>& rhs) {
|
|
Node node(NodeType::Sequence);
|
|
node.push_back(rhs.first);
|
|
node.push_back(rhs.second);
|
|
return node;
|
|
}
|
|
|
|
static bool decode(const Node& node, std::pair<T, U>& rhs) {
|
|
if (!node.IsSequence())
|
|
return false;
|
|
if (node.size() != 2)
|
|
return false;
|
|
|
|
#if defined(__GNUC__) && __GNUC__ < 4
|
|
// workaround for GCC 3:
|
|
rhs.first = node[0].template as<T>();
|
|
#else
|
|
rhs.first = node[0].as<T>();
|
|
#endif
|
|
#if defined(__GNUC__) && __GNUC__ < 4
|
|
// workaround for GCC 3:
|
|
rhs.second = node[1].template as<U>();
|
|
#else
|
|
rhs.second = node[1].as<U>();
|
|
#endif
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// binary
|
|
template <>
|
|
struct convert<Binary> {
|
|
static Node encode(const Binary& rhs) {
|
|
return Node(EncodeBase64(rhs.data(), rhs.size()));
|
|
}
|
|
|
|
static bool decode(const Node& node, Binary& rhs) {
|
|
if (!node.IsScalar())
|
|
return false;
|
|
|
|
std::vector<unsigned char> data = DecodeBase64(node.Scalar());
|
|
if (data.empty() && !node.Scalar().empty())
|
|
return false;
|
|
|
|
rhs.swap(data);
|
|
return true;
|
|
}
|
|
};
|
|
}
|
|
|
|
#endif // NODE_CONVERT_H_62B23520_7C8E_11DE_8A39_0800200C9A66
|