Add conversion and emitter support for various STL containers
* std::set -- Rendered as a yaml sequence * std::unordered_set -- Rendered the same as std::set * std::unordered_map -- Rendered the same as std::map * std::deque -- Rendered the same as std::vector * std::forward_list -- Rendered as a sequence, but decoding happens from back to front * std::bitset -- Rendered as a yaml string of zeroes and ones * std::tuple -- Rendered as a yaml sequence
This commit is contained in:
parent
bedb28fdb4
commit
942620bcba
@ -8,10 +8,19 @@
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
#include <deque>
|
||||
#include <forward_list>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "yaml-cpp/binary.h"
|
||||
@ -76,7 +85,7 @@ struct convert<const char*> {
|
||||
|
||||
template <std::size_t N>
|
||||
struct convert<const char[N]> {
|
||||
static Node encode(const char(&rhs)[N]) { return Node(rhs); }
|
||||
static Node encode(const char (&rhs)[N]) { return Node(rhs); }
|
||||
};
|
||||
|
||||
template <>
|
||||
@ -161,83 +170,161 @@ struct convert<bool> {
|
||||
YAML_CPP_API static bool decode(const Node& node, bool& rhs);
|
||||
};
|
||||
|
||||
// std::map
|
||||
template <typename K, typename V>
|
||||
struct convert<std::map<K, V>> {
|
||||
static Node encode(const std::map<K, V>& rhs) {
|
||||
// Helper class for converting something which is "sequence-like" to and from a
|
||||
// Node.
|
||||
template <class T>
|
||||
struct convert_as_sequence {
|
||||
static Node encode(const T& sequence) {
|
||||
Node node(NodeType::Sequence);
|
||||
for (const auto& item : sequence) {
|
||||
node.push_back(item);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
static bool decode(const Node& node, T& sequence) {
|
||||
if (!node.IsSequence())
|
||||
return false;
|
||||
sequence.clear();
|
||||
for (const auto& item : node) {
|
||||
sequence.push_back(item.template as<typename T::value_type>());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper class for converting something which is "map-like" to and from a Node.
|
||||
template <class T, class Key = typename T::key_type,
|
||||
class Value = typename T::mapped_type>
|
||||
struct convert_as_map {
|
||||
static Node encode(const T& map) {
|
||||
Node node(NodeType::Map);
|
||||
for (typename std::map<K, V>::const_iterator it = rhs.begin();
|
||||
it != rhs.end(); ++it)
|
||||
node.force_insert(it->first, it->second);
|
||||
for (const auto& kv : map) {
|
||||
node.force_insert(kv.first, kv.second);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, std::map<K, V>& rhs) {
|
||||
static bool decode(const Node& node, T& map) {
|
||||
if (!node.IsMap())
|
||||
return false;
|
||||
map.clear();
|
||||
for (const auto& kv : node) {
|
||||
// if we're dealing with GCC (note: clang also defines __GNUC__)
|
||||
#if defined(__GNUC__)
|
||||
#if __GNUC__ < 4
|
||||
// gcc version < 4
|
||||
map.insert(std::make_pair(kv.first.template as<Key>(),
|
||||
kv.second.template as<Value>()));
|
||||
#elif __GNUC__ == 4 && __GNUC_MINOR__ < 8
|
||||
// 4.0 <= gcc version < 4.8
|
||||
map.insert(std::make_pair(kv.first.as<Key>(), kv.second.as<Value>()));
|
||||
#else
|
||||
// 4.8 <= gcc version
|
||||
map.emplace(kv.first.as<Key>(), kv.second.as<Value>());
|
||||
#endif // __GNUC__ < 4
|
||||
#else
|
||||
// for anything not GCC or clang...
|
||||
// probably some more #ifdef guards are needed for MSVC.
|
||||
map.emplace(kv.first.as<Key>(), kv.second.as<Value>());
|
||||
#endif // defined(__GNUC__)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper class for converting something which is "set-like" to and from a Node.
|
||||
// A set is realized as a yaml sequence.
|
||||
template <class T>
|
||||
struct convert_as_set {
|
||||
static Node encode(const T& set) {
|
||||
Node node(NodeType::Sequence);
|
||||
for (const auto& item : set) {
|
||||
node.push_back(item);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, T& set) {
|
||||
if (!node.IsSequence())
|
||||
return false;
|
||||
|
||||
set.clear();
|
||||
for (const auto& item : node) {
|
||||
|
||||
rhs.clear();
|
||||
for (const_iterator it = node.begin(); it != node.end(); ++it)
|
||||
#if defined(__GNUC__) && __GNUC__ < 4
|
||||
// workaround for GCC 3:
|
||||
rhs[it->first.template as<K>()] = it->second.template as<V>();
|
||||
#else
|
||||
rhs[it->first.as<K>()] = it->second.as<V>();
|
||||
set.insert(item.template as<typename T::value_type>());
|
||||
#else // __GNUC__ is not defined or __GNUC__ >= 4
|
||||
set.insert(item.as<typename T::value_type>());
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// std::vector
|
||||
template <typename T>
|
||||
struct convert<std::vector<T>> {
|
||||
static Node encode(const std::vector<T>& rhs) {
|
||||
Node node(NodeType::Sequence);
|
||||
for (typename std::vector<T>::const_iterator it = rhs.begin();
|
||||
it != rhs.end(); ++it)
|
||||
node.push_back(*it);
|
||||
return node;
|
||||
}
|
||||
template <typename T, class Alloc>
|
||||
struct convert<std::vector<T, Alloc>>
|
||||
: public convert_as_sequence<std::vector<T, Alloc>> {};
|
||||
|
||||
static bool decode(const Node& node, std::vector<T>& rhs) {
|
||||
if (!node.IsSequence())
|
||||
return false;
|
||||
|
||||
rhs.clear();
|
||||
for (const_iterator it = node.begin(); it != node.end(); ++it)
|
||||
#if defined(__GNUC__) && __GNUC__ < 4
|
||||
// workaround for GCC 3:
|
||||
rhs.push_back(it->template as<T>());
|
||||
#else
|
||||
rhs.push_back(it->as<T>());
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
};
|
||||
// std::deque
|
||||
template <class T, class Alloc>
|
||||
struct convert<std::deque<T, Alloc>>
|
||||
: public convert_as_sequence<std::deque<T, Alloc>> {};
|
||||
|
||||
// std::list
|
||||
template <typename T>
|
||||
struct convert<std::list<T>> {
|
||||
static Node encode(const std::list<T>& rhs) {
|
||||
template <typename T, class Alloc>
|
||||
struct convert<std::list<T, Alloc>>
|
||||
: public convert_as_sequence<std::list<T, Alloc>> {};
|
||||
|
||||
// std::map
|
||||
template <class Key, class T, class Compare, class Alloc>
|
||||
struct convert<std::map<Key, T, Compare, Alloc>>
|
||||
: public convert_as_map<std::map<Key, T, Compare, Alloc>> {};
|
||||
|
||||
// std::unordered_map
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Alloc>
|
||||
struct convert<std::unordered_map<Key, T, Hash, KeyEqual, Alloc>>
|
||||
: public convert_as_map<std::unordered_map<Key, T, Hash, KeyEqual, Alloc>> {
|
||||
};
|
||||
|
||||
// std::set
|
||||
template <class Key, class Compare, class Alloc>
|
||||
struct convert<std::set<Key, Compare, Alloc>>
|
||||
: public convert_as_set<std::set<Key, Compare, Alloc>> {};
|
||||
|
||||
// std::unordered_set
|
||||
template <class Key, class Hash, class KeyEqual, class Alloc>
|
||||
struct convert<std::unordered_set<Key, Hash, KeyEqual, Alloc>>
|
||||
: public convert_as_set<std::unordered_set<Key, Hash, KeyEqual, Alloc>> {};
|
||||
|
||||
// std::forward_list
|
||||
template <class T, class Alloc>
|
||||
struct convert<std::forward_list<T, Alloc>> {
|
||||
static Node encode(const std::forward_list<T, Alloc>& sequence) {
|
||||
Node node(NodeType::Sequence);
|
||||
for (typename std::list<T>::const_iterator it = rhs.begin();
|
||||
it != rhs.end(); ++it)
|
||||
node.push_back(*it);
|
||||
for (const auto& item : sequence) {
|
||||
node.push_back(item);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, std::list<T>& rhs) {
|
||||
static bool decode(const Node& node, std::forward_list<T, Alloc>& sequence) {
|
||||
if (!node.IsSequence())
|
||||
return false;
|
||||
|
||||
rhs.clear();
|
||||
for (const_iterator it = node.begin(); it != node.end(); ++it)
|
||||
sequence.clear();
|
||||
|
||||
// Walk the node backwards, because std::forward_list does not have
|
||||
// push_back, only push_front.
|
||||
for (std::size_t i = node.size() - 1; i != (std::size_t)-1; --i) {
|
||||
#if defined(__GNUC__) && __GNUC__ < 4
|
||||
// workaround for GCC 3:
|
||||
rhs.push_back(it->template as<T>());
|
||||
sequence.push_front(node[i].template as<T>());
|
||||
#else
|
||||
rhs.push_back(it->as<T>());
|
||||
sequence.push_front(node[i].as<T>());
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -245,15 +332,15 @@ struct convert<std::list<T>> {
|
||||
// std::array
|
||||
template <typename T, std::size_t N>
|
||||
struct convert<std::array<T, N>> {
|
||||
static Node encode(const std::array<T, N>& rhs) {
|
||||
static Node encode(const std::array<T, N>& sequence) {
|
||||
Node node(NodeType::Sequence);
|
||||
for (const auto& element : rhs) {
|
||||
node.push_back(element);
|
||||
for (const auto& item : sequence) {
|
||||
node.push_back(item);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, std::array<T, N>& rhs) {
|
||||
static bool decode(const Node& node, std::array<T, N>& sequence) {
|
||||
if (!isNodeValid(node)) {
|
||||
return false;
|
||||
}
|
||||
@ -261,9 +348,9 @@ struct convert<std::array<T, N>> {
|
||||
for (auto i = 0u; i < node.size(); ++i) {
|
||||
#if defined(__GNUC__) && __GNUC__ < 4
|
||||
// workaround for GCC 3:
|
||||
rhs[i] = node[i].template as<T>();
|
||||
sequence[i] = node[i].template as<T>();
|
||||
#else
|
||||
rhs[i] = node[i].as<T>();
|
||||
sequence[i] = node[i].as<T>();
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
@ -275,34 +362,109 @@ struct convert<std::array<T, N>> {
|
||||
}
|
||||
};
|
||||
|
||||
// std::pair
|
||||
template <typename T, typename U>
|
||||
struct convert<std::pair<T, U>> {
|
||||
static Node encode(const std::pair<T, U>& rhs) {
|
||||
// std::bitset
|
||||
template <std::size_t N>
|
||||
struct convert<std::bitset<N>> {
|
||||
using value_type = std::bitset<N>;
|
||||
|
||||
static Node encode(const value_type& rhs) {
|
||||
return convert<std::string>::encode(rhs.to_string());
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, value_type& rhs) {
|
||||
std::string representation;
|
||||
|
||||
if (!convert<std::string>::decode(node, representation))
|
||||
return false;
|
||||
if (representation.size() != N)
|
||||
return false;
|
||||
try {
|
||||
// bitset constructor will throw std:invalid_argument if the decoded
|
||||
// string contains characters other than 0 and 1.
|
||||
rhs = value_type(representation);
|
||||
} catch (const std::invalid_argument& /*error*/) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <std::size_t Index = 0, typename... Args>
|
||||
inline typename std::enable_if<Index == sizeof...(Args), void>::type
|
||||
encode_tuple(Node& /*node*/, const std::tuple<Args...>& /*tup*/) {}
|
||||
|
||||
template <std::size_t Index = 0, typename... Args>
|
||||
inline typename std::enable_if<Index != sizeof...(Args), void>::type
|
||||
encode_tuple(Node& node, const std::tuple<Args...>& tup) {
|
||||
node.push_back(std::get<Index>(tup));
|
||||
encode_tuple<Index + 1, Args...>(node, tup);
|
||||
}
|
||||
|
||||
template <std::size_t Index = 0, typename... Args>
|
||||
inline typename std::enable_if<Index == sizeof...(Args), void>::type
|
||||
decode_tuple(const Node& /*node*/, std::tuple<Args...>& /*tup*/) {}
|
||||
|
||||
template <std::size_t Index = 0, typename... Args>
|
||||
inline typename std::enable_if<Index != sizeof...(Args), void>::type
|
||||
decode_tuple(const Node& node, std::tuple<Args...>& tup) {
|
||||
std::get<Index>(tup) =
|
||||
node[Index]
|
||||
.template as<
|
||||
typename std::tuple_element<Index, std::tuple<Args...>>::type>();
|
||||
decode_tuple<Index + 1, Args...>(node, tup);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// std::tuple
|
||||
template <typename... Args>
|
||||
struct convert<std::tuple<Args...>> {
|
||||
static Node encode(const std::tuple<Args...>& tup) {
|
||||
static_assert(sizeof...(Args) > 0,
|
||||
"wrong template specialization selected");
|
||||
Node node(NodeType::Sequence);
|
||||
node.push_back(rhs.first);
|
||||
node.push_back(rhs.second);
|
||||
detail::encode_tuple(node, tup);
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, std::pair<T, U>& rhs) {
|
||||
if (!node.IsSequence())
|
||||
return false;
|
||||
if (node.size() != 2)
|
||||
static bool decode(const Node& node, std::tuple<Args...>& tup) {
|
||||
static_assert(sizeof...(Args) > 0,
|
||||
"wrong template specialization selected");
|
||||
if (!node.IsSequence() || node.size() != sizeof...(Args))
|
||||
return false;
|
||||
detail::decode_tuple(node, tup);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#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
|
||||
// std::tuple -- empty
|
||||
template <>
|
||||
struct convert<std::tuple<>> {
|
||||
static Node encode(const std::tuple<>& /*tup*/) {
|
||||
return convert<_Null>::encode(Null);
|
||||
}
|
||||
static bool decode(const Node& node, std::tuple<>& /*tup*/) {
|
||||
return convert<_Null>::decode(node, Null);
|
||||
}
|
||||
};
|
||||
|
||||
// std::pair -- special case of std::tuple
|
||||
template <class First, class Second>
|
||||
struct convert<std::pair<First, Second>> {
|
||||
static Node encode(const std::pair<First, Second>& tup) {
|
||||
Node node(NodeType::Sequence);
|
||||
node.push_back(std::get<0>(tup));
|
||||
node.push_back(std::get<1>(tup));
|
||||
return node;
|
||||
}
|
||||
|
||||
static bool decode(const Node& node, std::pair<First, Second>& tup) {
|
||||
if (!node.IsSequence() || node.size() != 2)
|
||||
return false;
|
||||
std::get<0>(tup) = node[0].template as<First>();
|
||||
std::get<1>(tup) = node[1].template as<Second>();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@ -231,5 +231,55 @@ TEST(NodeTest, LoadTildeAsNull) {
|
||||
ASSERT_TRUE(node.IsNull());
|
||||
}
|
||||
|
||||
TEST(NodeTest, MapOfSequences) {
|
||||
const auto doc = Load(R"(
|
||||
homebrews:
|
||||
vita:
|
||||
- name: "PSP2048"
|
||||
author: dots-tb
|
||||
- name: "PSVident"
|
||||
author: Freakler
|
||||
)");
|
||||
|
||||
const auto homebrews = doc["homebrews"];
|
||||
// should succeed
|
||||
std::map<std::string, std::vector<std::map<std::string, std::string>>> info;
|
||||
info = homebrews.as<
|
||||
std::map<std::string, std::vector<std::map<std::string, std::string>>>>();
|
||||
const auto vita = info["vita"];
|
||||
const auto vita0 = vita[0];
|
||||
const auto vita1 = vita[1];
|
||||
EXPECT_NE(vita0.end(), vita0.find("name"));
|
||||
EXPECT_NE(vita1.end(), vita1.find("name"));
|
||||
EXPECT_NE(vita0.end(), vita0.find("author"));
|
||||
EXPECT_NE(vita1.end(), vita1.find("author"));
|
||||
|
||||
const auto name0 = vita0.find("name");
|
||||
const auto name1 = vita1.find("name");
|
||||
const auto author0 = vita0.find("author");
|
||||
const auto author1 = vita1.find("author");
|
||||
|
||||
EXPECT_EQ(name0->second, "PSP2048");
|
||||
EXPECT_EQ(name1->second, "PSVident");
|
||||
EXPECT_EQ(author0->second, "dots-tb");
|
||||
EXPECT_EQ(author1->second, "Freakler");
|
||||
}
|
||||
|
||||
TEST(NodeTest, SeqMap) {
|
||||
const auto doc = Load(R"(
|
||||
- firstname: john
|
||||
surname: doe
|
||||
)");
|
||||
|
||||
const auto info = doc.as<std::vector<std::map<std::string, std::string>>>();
|
||||
|
||||
EXPECT_EQ(1, info.size());
|
||||
|
||||
auto name = info[0];
|
||||
EXPECT_EQ(2, name.size());
|
||||
EXPECT_EQ("john", name["firstname"]);
|
||||
EXPECT_EQ("doe", name["surname"]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace YAML
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
#include "yaml-cpp/emitter.h"
|
||||
#include "yaml-cpp/node/emit.h"
|
||||
#include "yaml-cpp/node/node.h"
|
||||
#include "yaml-cpp/node/impl.h"
|
||||
#include "yaml-cpp/node/convert.h"
|
||||
#include "yaml-cpp/node/iterator.h"
|
||||
#include "yaml-cpp/node/detail/impl.h"
|
||||
#include "yaml-cpp/node/emit.h"
|
||||
#include "yaml-cpp/node/impl.h"
|
||||
#include "yaml-cpp/node/iterator.h"
|
||||
#include "yaml-cpp/node/node.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
@ -211,6 +211,20 @@ TEST(NodeTest, StdVector) {
|
||||
EXPECT_EQ(primes, node["primes"].as<std::vector<int>>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdDeque) {
|
||||
std::deque<int> primes;
|
||||
primes.push_back(2);
|
||||
primes.push_back(3);
|
||||
primes.push_back(5);
|
||||
primes.push_back(7);
|
||||
primes.push_back(11);
|
||||
primes.push_back(13);
|
||||
|
||||
Node node;
|
||||
node["primes"] = primes;
|
||||
EXPECT_EQ(primes, node["primes"].as<std::deque<int>>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdList) {
|
||||
std::list<int> primes;
|
||||
primes.push_back(2);
|
||||
@ -225,6 +239,20 @@ TEST(NodeTest, StdList) {
|
||||
EXPECT_EQ(primes, node["primes"].as<std::list<int>>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdForwardList) {
|
||||
std::forward_list<int> primes;
|
||||
primes.push_front(13);
|
||||
primes.push_front(11);
|
||||
primes.push_front(7);
|
||||
primes.push_front(5);
|
||||
primes.push_front(3);
|
||||
primes.push_front(2);
|
||||
|
||||
Node node;
|
||||
node["primes"] = primes;
|
||||
EXPECT_EQ(primes, node["primes"].as<std::forward_list<int>>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdMap) {
|
||||
std::map<int, int> squares;
|
||||
squares[0] = 0;
|
||||
@ -239,6 +267,291 @@ TEST(NodeTest, StdMap) {
|
||||
EXPECT_EQ(squares, actualSquares);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdUnorderedMap) {
|
||||
std::unordered_map<int, int> squares;
|
||||
squares[0] = 0;
|
||||
squares[1] = 1;
|
||||
squares[2] = 4;
|
||||
squares[3] = 9;
|
||||
squares[4] = 16;
|
||||
|
||||
Node node;
|
||||
node["squares"] = squares;
|
||||
const auto actualSquares = node["squares"].as<std::unordered_map<int, int>>();
|
||||
EXPECT_EQ(squares, actualSquares);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdSet) {
|
||||
std::set<int> primes;
|
||||
primes.insert(2);
|
||||
primes.insert(3);
|
||||
primes.insert(5);
|
||||
primes.insert(7);
|
||||
primes.insert(11);
|
||||
primes.insert(13);
|
||||
|
||||
Node node;
|
||||
node["primes"] = primes;
|
||||
const auto actualPrimes = node["primes"].as<std::set<int>>();
|
||||
EXPECT_EQ(primes, actualPrimes);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdUnorderedSet) {
|
||||
std::unordered_set<int> primes;
|
||||
primes.insert(0);
|
||||
primes.insert(1);
|
||||
primes.insert(4);
|
||||
primes.insert(9);
|
||||
primes.insert(16);
|
||||
|
||||
Node node;
|
||||
node["primes"] = primes;
|
||||
const auto actualPrimes = node["primes"].as<std::unordered_set<int>>();
|
||||
EXPECT_EQ(primes, actualPrimes);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdBitset) {
|
||||
std::bitset<8> bits;
|
||||
bits.set(0, true);
|
||||
bits.set(1, true);
|
||||
bits.set(2, false);
|
||||
bits.set(3, true);
|
||||
bits.set(4, true);
|
||||
bits.set(5, false);
|
||||
bits.set(6, false);
|
||||
bits.set(7, false);
|
||||
|
||||
Node node;
|
||||
node["bits"] = bits;
|
||||
const auto actual = node["bits"].as<std::bitset<8>>();
|
||||
EXPECT_EQ(bits, actual);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdBitsetWrongSize) {
|
||||
std::bitset<8> bits;
|
||||
bits.set(0, true);
|
||||
bits.set(1, true);
|
||||
bits.set(2, false);
|
||||
bits.set(3, true);
|
||||
bits.set(4, true);
|
||||
bits.set(5, false);
|
||||
bits.set(6, false);
|
||||
bits.set(7, false);
|
||||
|
||||
Node node;
|
||||
node["bits"] = bits;
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<1>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<2>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<3>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<4>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<5>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<6>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<7>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION((node["bits"].as<std::bitset<9>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdBitsetWrongRepresentation) {
|
||||
const std::string representation =
|
||||
"0011"
|
||||
"1000"
|
||||
"1101"
|
||||
"x000"; // 16 bits, note the wrong character 'x' here
|
||||
Node node;
|
||||
node["not_really_bits"] = representation;
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION(
|
||||
(node["not_really_bits"].as<std::bitset<16>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
}
|
||||
|
||||
#define TEST_FROM_TO(TESTNAME, FROM, TO) \
|
||||
TEST(NodeTest, TESTNAME) { \
|
||||
const std::FROM primes = {{2, 3, 5, 7, 11, 13}}; \
|
||||
std::size_t count0 = std::distance(std::begin(primes), std::end(primes)); \
|
||||
const Node node(primes); \
|
||||
const auto decoded = node.as<std::TO>(); \
|
||||
auto iter0 = primes.begin(); \
|
||||
auto iter1 = decoded.begin(); \
|
||||
std::size_t count1 = 0; \
|
||||
while (iter0 != primes.end() && iter1 != decoded.end()) { \
|
||||
EXPECT_EQ(*iter0, *iter1); \
|
||||
++iter0; \
|
||||
++iter1; \
|
||||
++count1; \
|
||||
} \
|
||||
EXPECT_EQ(count0, count1); \
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION((node.as<std::map<int, int>>()), \
|
||||
ErrorMsg::BAD_CONVERSION); \
|
||||
}
|
||||
|
||||
// needed because the comma would otherwise be seen as the start of a new macro
|
||||
// argument.
|
||||
#define ARRAY_TYPE array<int, 6>
|
||||
|
||||
TEST_FROM_TO(StdFromVectorToDeque, vector<int>, deque<int>);
|
||||
TEST_FROM_TO(StdFromVectorToList, vector<int>, list<int>);
|
||||
TEST_FROM_TO(StdFromVectorToForwardList, vector<int>, forward_list<int>);
|
||||
TEST_FROM_TO(StdFromVectorToArray, vector<int>, ARRAY_TYPE);
|
||||
|
||||
TEST_FROM_TO(StdFromDequeToVector, deque<int>, vector<int>);
|
||||
TEST_FROM_TO(StdFromDequeToList, deque<int>, list<int>);
|
||||
TEST_FROM_TO(StdFromDequeToForwardList, deque<int>, forward_list<int>);
|
||||
TEST_FROM_TO(StdFromDequeToArray, deque<int>, ARRAY_TYPE);
|
||||
|
||||
TEST_FROM_TO(StdFromListToVector, list<int>, vector<int>);
|
||||
TEST_FROM_TO(StdFromListToDeque, list<int>, deque<int>);
|
||||
TEST_FROM_TO(StdFromListToForwardList, list<int>, forward_list<int>);
|
||||
TEST_FROM_TO(StdFromListToArray, list<int>, ARRAY_TYPE);
|
||||
|
||||
TEST_FROM_TO(StdFromForwardListToVector, forward_list<int>, vector<int>);
|
||||
TEST_FROM_TO(StdFromForwardListToDeque, forward_list<int>, deque<int>);
|
||||
TEST_FROM_TO(StdFromForwardListToList, forward_list<int>, list<int>);
|
||||
TEST_FROM_TO(StdFromForwardListToArray, forward_list<int>, ARRAY_TYPE);
|
||||
|
||||
TEST_FROM_TO(StdFromArrayToVector, ARRAY_TYPE, vector<int>);
|
||||
TEST_FROM_TO(StdFromArrayToDeque, ARRAY_TYPE, deque<int>);
|
||||
TEST_FROM_TO(StdFromArrayToList, ARRAY_TYPE, list<int>);
|
||||
TEST_FROM_TO(StdFromArrayToForwardList, ARRAY_TYPE, forward_list<int>);
|
||||
|
||||
#undef ARRAY_TYPE
|
||||
#undef TEST_FROM_TO
|
||||
|
||||
TEST(NodeTest, SequenceWithDuplicatesCanBeParsedAsSet) {
|
||||
|
||||
const std::vector<int> withDuplicates{1, 5, 3, 6, 4, 7, 5, 3, 3, 4, 5,
|
||||
5, 3, 2, 7, 8, 9, 9, 9, 5, 2, 0};
|
||||
|
||||
const auto node = Node(withDuplicates);
|
||||
|
||||
// should succeed -- an std::set is considered a yaml sequence
|
||||
const auto withoutDuplicatesAndSorted = node.as<std::set<int>>();
|
||||
EXPECT_EQ(withoutDuplicatesAndSorted.size(), 10);
|
||||
std::size_t i = 0;
|
||||
for (const auto digit : withoutDuplicatesAndSorted) {
|
||||
EXPECT_EQ(digit, i);
|
||||
++i;
|
||||
}
|
||||
EXPECT_EQ(i, 10);
|
||||
}
|
||||
|
||||
TEST(NodeTest, SequenceWithDuplicatesCanBeParsedAsUnorderedSet) {
|
||||
|
||||
const std::vector<int> withDuplicates{1, 5, 3, 6, 4, 7, 5, 3, 3, 4, 5,
|
||||
5, 3, 2, 7, 8, 9, 9, 9, 5, 2, 0};
|
||||
|
||||
const auto node = Node(withDuplicates);
|
||||
|
||||
// should succeed -- an std::unordered_set is considered a yaml sequence
|
||||
const auto withoutDuplicates = node.as<std::unordered_set<int>>();
|
||||
EXPECT_EQ(withoutDuplicates.size(), 10);
|
||||
int occurences[10];
|
||||
std::fill(std::begin(occurences), std::end(occurences), 0);
|
||||
for (const auto digit : withoutDuplicates) {
|
||||
++occurences[digit];
|
||||
}
|
||||
for (std::size_t i = 0; i < 10; ++i)
|
||||
EXPECT_EQ(1, occurences[i]);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdFromMapToUnorderedMap) {
|
||||
std::map<std::string, std::vector<std::string>> typeSafeTravisFile{
|
||||
{"language", {"c++"}},
|
||||
{"os", {"linux", "osx"}},
|
||||
{"compiler", {"clang", "gcc"}},
|
||||
{"before_install", {"do some stuff", "do some more stuff"}},
|
||||
{"before_script", {"mkdir build", "cd build", "cmake .."}},
|
||||
{"script", {"make", "test/run-tests"}}};
|
||||
|
||||
const auto node = Node(typeSafeTravisFile);
|
||||
|
||||
// should succeed -- precisely two values in the sequence
|
||||
const auto os = node["os"].as<std::pair<std::string, std::string>>();
|
||||
EXPECT_EQ(os.first, "linux");
|
||||
EXPECT_EQ(os.second, "osx");
|
||||
|
||||
// should succeed -- yaml doesn't know the difference between ordered and
|
||||
// unordered.
|
||||
using T = std::unordered_map<std::string, std::vector<std::string>>;
|
||||
EXPECT_THAT(node.as<T>(),
|
||||
testing::UnorderedElementsAreArray(typeSafeTravisFile.begin(),
|
||||
typeSafeTravisFile.end()));
|
||||
|
||||
// should fail -- a yaml map is not a yaml sequence
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION(
|
||||
(node.as< // looks structurally somewhat like a map
|
||||
std::vector<std::pair<std::string, std::vector<std::string>>>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION(
|
||||
(node.as< // looks structurally somewhat like a map
|
||||
std::deque<std::pair<std::string, std::deque<std::string>>>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION(
|
||||
(node.as< // looks structurally somewhat like a map
|
||||
std::list<std::pair<std::string, std::list<std::string>>>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION(
|
||||
(node.as< // looks structurally somewhat like a map
|
||||
std::forward_list<
|
||||
std::pair<std::string, std::forward_list<std::string>>>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdFromUnorderedMapToMap) {
|
||||
std::unordered_map<std::string, std::vector<std::string>> typeSafeTravisFile{
|
||||
{"language", {"c++"}},
|
||||
{"os", {"linux", "osx"}},
|
||||
{"compiler", {"clang", "gcc"}},
|
||||
{"before_install", {"do some stuff", "do some more stuff"}},
|
||||
{"before_script", {"mkdir build", "cd build", "cmake .."}},
|
||||
{"script", {"make", "test/run-tests"}}};
|
||||
|
||||
const auto node = Node(typeSafeTravisFile);
|
||||
|
||||
// should succeed -- precisely two values in the sequence
|
||||
const auto os = node["os"].as<std::pair<std::string, std::string>>();
|
||||
EXPECT_EQ(os.first, "linux");
|
||||
EXPECT_EQ(os.second, "osx");
|
||||
|
||||
// should succeed -- yaml doesn't know the difference between ordered and
|
||||
// unordered.
|
||||
using T = std::map<std::string, std::vector<std::string>>;
|
||||
EXPECT_THAT(node.as<T>(),
|
||||
testing::UnorderedElementsAreArray(typeSafeTravisFile.begin(),
|
||||
typeSafeTravisFile.end()));
|
||||
|
||||
// should fail -- a yaml map is not a yaml sequence
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION(
|
||||
(node.as< // looks structurally somewhat like a map
|
||||
std::vector<std::pair<std::string, std::vector<std::string>>>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION(
|
||||
(node.as< // looks structurally somewhat like a map
|
||||
std::deque<std::pair<std::string, std::deque<std::string>>>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION(
|
||||
(node.as< // looks structurally somewhat like a map
|
||||
std::list<std::pair<std::string, std::list<std::string>>>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION(
|
||||
(node.as< // looks structurally somewhat like a map
|
||||
std::forward_list<
|
||||
std::pair<std::string, std::forward_list<std::string>>>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdPair) {
|
||||
std::pair<int, std::string> p;
|
||||
p.first = 5;
|
||||
@ -251,6 +564,86 @@ TEST(NodeTest, StdPair) {
|
||||
EXPECT_EQ(p, actualP);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdPairFailure) {
|
||||
std::vector<int> triple{1, 2, 3};
|
||||
const auto node = Node(triple);
|
||||
|
||||
// should fail -- an std::pair needs a sequence of length exactly 2, while
|
||||
// the Node holds a sequence of length exactly 3.
|
||||
EXPECT_THROW_REPRESENTATION_EXCEPTION((node.as<std::pair<int, int>>()),
|
||||
ErrorMsg::BAD_CONVERSION);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdTupleSize0) {
|
||||
const auto expected = std::tuple<>();
|
||||
const auto node = Node(expected);
|
||||
const auto actual = node.as<std::tuple<>>();
|
||||
EXPECT_EQ(expected, actual);
|
||||
EXPECT_EQ(true, node.IsNull());
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdTupleSize1) {
|
||||
const auto expected = std::make_tuple(42);
|
||||
const auto node = Node(expected);
|
||||
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
|
||||
EXPECT_EQ(expected, actual);
|
||||
|
||||
// should succeed -- tuple is realized as a yaml sequence
|
||||
const auto vec = node.as<std::vector<int>>();
|
||||
EXPECT_EQ(std::get<0>(expected), vec[0]);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdTupleSize2) {
|
||||
const auto expected = std::make_tuple(42, 3.141592f);
|
||||
const auto node = Node(expected);
|
||||
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
|
||||
EXPECT_EQ(expected, actual);
|
||||
|
||||
// should succeed -- tuple is realized as a yaml sequence
|
||||
const auto vec = node.as<std::vector<float>>();
|
||||
EXPECT_EQ(std::get<0>(expected), vec[0]);
|
||||
EXPECT_EQ(std::get<1>(expected), vec[1]);
|
||||
|
||||
// should succeed -- can also be a pair in this case
|
||||
const auto pair = node.as<std::pair<int, float>>();
|
||||
EXPECT_EQ(std::get<0>(expected), pair.first);
|
||||
EXPECT_EQ(std::get<1>(expected), pair.second);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdTupleSize3) {
|
||||
const auto expected = std::make_tuple(42, 3.141592f, std::string("hello"));
|
||||
const auto node = Node(expected);
|
||||
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
|
||||
EXPECT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdTupleSize4) {
|
||||
const auto expected = std::make_tuple(42, 3.141592f, std::string("hello"),
|
||||
std::string("world"));
|
||||
const auto node = Node(expected);
|
||||
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
|
||||
EXPECT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdTupleSize5) {
|
||||
const auto expected =
|
||||
std::make_tuple(42, 3.141592f, std::string("hello"), std::string("world"),
|
||||
std::vector<int>{2, 3, 5, 7});
|
||||
const auto node = Node(expected);
|
||||
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
|
||||
EXPECT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST(NodeTest, StdTupleSize6) {
|
||||
const auto expected =
|
||||
std::make_tuple(42, 3.141592f, std::string("hello"), std::string("world"),
|
||||
std::vector<int>{2, 3, 5, 7},
|
||||
std::map<int, int>{{1, 1}, {2, 4}, {3, 9}, {4, 16}});
|
||||
const auto node = Node(expected);
|
||||
const auto actual = node.as<std::remove_cv<decltype(expected)>::type>();
|
||||
EXPECT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST(NodeTest, SimpleAlias) {
|
||||
Node node;
|
||||
node["foo"] = "value";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user