This commit is contained in:
Raoul Wols 2018-02-18 20:24:12 +00:00 committed by GitHub
commit cbc64faef3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 402 additions and 54 deletions

View File

@ -7,45 +7,139 @@
#pragma once
#endif
#include <vector>
#include <array>
#include <bitset>
#include <deque>
#include <forward_list>
#include <list>
#include <set>
#include <map>
#include <set>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace YAML {
template <typename Seq>
inline Emitter& EmitSeq(Emitter& emitter, const Seq& seq) {
emitter << BeginSeq;
for (typename Seq::const_iterator it = seq.begin(); it != seq.end(); ++it)
emitter << *it;
emitter << EndSeq;
namespace detail {
template <class SequenceLike>
Emitter& EmitSeq(Emitter& emit, const SequenceLike& sequence) {
emit << BeginSeq;
for (const auto& item : sequence)
emit << item;
emit << EndSeq;
return emit;
}
template <class MapLike>
Emitter& EmitMap(Emitter& emit, const MapLike& map) {
emit << BeginMap;
for (const auto& kv : map)
emit << Key << kv.first << Value << kv.second;
emit << EndMap;
return emit;
}
template <std::size_t Index = 0, typename... Args>
typename std::enable_if<Index == sizeof...(Args), Emitter&>::type EmitTuple(
Emitter& emit, const std::tuple<Args...>& /*tup*/) {
return emit;
}
template <std::size_t Index = 0, typename... Args>
typename std::enable_if<Index != sizeof...(Args), Emitter&>::type EmitTuple(
Emitter& emit, const std::tuple<Args...>& tup) {
emit << std::get<Index>(tup);
return EmitTuple<Index + 1, Args...>(emit, tup);
}
} // namespace detail
// std::vector
template <class T, class Alloc>
Emitter& operator<<(Emitter& emit, const std::vector<T, Alloc>& t) {
return detail::EmitSeq(emit, t);
}
// std::list
template <class T, class Alloc>
Emitter& operator<<(Emitter& emit, const std::list<T, Alloc>& t) {
return detail::EmitSeq(emit, t);
}
// std::deque
template <class T, class Alloc>
Emitter& operator<<(Emitter& emit, const std::deque<T, Alloc>& t) {
return detail::EmitSeq(emit, t);
}
// std::forward_list
template <class T, class Alloc>
Emitter& operator<<(Emitter& emit, const std::forward_list<T, Alloc>& t) {
return detail::EmitSeq(emit, t);
}
// std::array
template <class T, std::size_t N>
Emitter& operator<<(Emitter& emit, const std::array<T, N>& t) {
return detail::EmitSeq(emit, t);
}
// std::map
template <class Key, class T, class Compare, class Alloc>
Emitter& operator<<(Emitter& emit, const std::map<Key, T, Compare, Alloc>& t) {
return detail::EmitMap(emit, t);
}
// std::unordered_map
template <class Key, class T, class Hash, class KeyEqual, class Alloc>
Emitter& operator<<(
Emitter& emit, const std::unordered_map<Key, T, Hash, KeyEqual, Alloc>& t) {
return detail::EmitMap(emit, t);
}
// std::set
template <class Key, class Compare, class Alloc>
Emitter& operator<<(Emitter& emit, const std::set<Key, Compare, Alloc>& t) {
return detail::EmitSeq(emit, t);
}
// std::unordered_set
template <class Key, class Hash, class KeyEqual, class Alloc>
Emitter& operator<<(Emitter& emit,
const std::unordered_set<Key, Hash, KeyEqual, Alloc>& t) {
return detail::EmitSeq(emit, t);
}
// std::bitset
template <std::size_t N>
Emitter& operator<<(Emitter& emit, const std::bitset<N>& bits) {
return emit << bits.to_string();
}
// std::pair
template <class First, class Second>
Emitter& operator<<(Emitter& emit, const std::pair<First, Second>& pair) {
emit << BeginSeq << pair.first << pair.second << EndSeq;
return emit;
}
// std::tuple
template <typename... Args>
Emitter& operator<<(Emitter& emit, const std::tuple<Args...>& tup) {
emit << BeginSeq;
detail::EmitTuple(emit, tup);
emit << EndSeq;
return emit;
}
// std::tuple -- empty
inline Emitter& operator<<(Emitter& emitter, const std::tuple<>& /*tup*/) {
emitter << Null;
return emitter;
}
template <typename T>
inline Emitter& operator<<(Emitter& emitter, const std::vector<T>& v) {
return EmitSeq(emitter, v);
}
template <typename T>
inline Emitter& operator<<(Emitter& emitter, const std::list<T>& v) {
return EmitSeq(emitter, v);
}
template <typename T>
inline Emitter& operator<<(Emitter& emitter, const std::set<T>& v) {
return EmitSeq(emitter, v);
}
template <typename K, typename V>
inline Emitter& operator<<(Emitter& emitter, const std::map<K, V>& m) {
typedef typename std::map<K, V> map;
emitter << BeginMap;
for (typename map::const_iterator it = m.begin(); it != m.end(); ++it)
emitter << Key << it->first << Value << it->second;
emitter << EndMap;
return emitter;
}
}
} // namespace YAML
#endif // STLEMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66

View File

@ -3,6 +3,24 @@
#include "yaml-cpp/yaml.h" // IWYU pragma: keep
#include "gtest/gtest.h"
// user-defined type for emitter
struct Foo {
Foo() : x(0) {}
Foo(int x_, const std::string& bar_) : x(x_), bar(bar_) {}
int x;
std::string bar;
};
// Provide overload for user-defined type.
YAML::Emitter& operator<<(YAML::Emitter& out, const Foo& foo) {
out << YAML::BeginMap;
out << YAML::Key << "x" << YAML::Value << foo.x;
out << YAML::Key << "bar" << YAML::Value << foo.bar;
out << YAML::EndMap;
return out;
}
namespace YAML {
namespace {
@ -497,7 +515,6 @@ TEST_F(EmitterTest, ComplexDoc) {
}
TEST_F(EmitterTest, STLContainers) {
out << BeginSeq;
std::vector<int> primes;
primes.push_back(2);
primes.push_back(3);
@ -505,14 +522,267 @@ TEST_F(EmitterTest, STLContainers) {
primes.push_back(7);
primes.push_back(11);
primes.push_back(13);
out << Flow << primes;
std::map<std::string, int> ages;
ages["Daniel"] = 26;
ages["Jesse"] = 24;
out << ages;
out << EndSeq;
ExpectEmit("- [2, 3, 5, 7, 11, 13]\n- Daniel: 26\n Jesse: 24");
// out << BeginSeq << Flow << primes << ages << EndSeq;
// ExpectEmit("- [2, 3, 5, 7, 11, 13]\n- Daniel: 26\n Jesse: 24");
const auto primesAndAges = std::make_pair(primes, ages);
out << Flow << primesAndAges; // <-- note recursiveness of `<<`
ExpectEmit("[[2, 3, 5, 7, 11, 13], {Daniel: 26, Jesse: 24}]");
}
TEST_F(EmitterTest, StdSet) {
std::set<int> primes;
primes.insert(2);
primes.insert(3);
primes.insert(5);
primes.insert(7);
primes.insert(11);
primes.insert(13);
out << Flow << primes;
ExpectEmit("[2, 3, 5, 7, 11, 13]");
}
TEST_F(EmitterTest, NestedSTLContainersPart1) {
std::vector<std::vector<int>> matrix;
matrix.push_back(std::vector<int>{1, 0, 0});
matrix.push_back(std::vector<int>{0, 1, 0});
matrix.push_back(std::vector<int>{0, 0, 1});
out << Block << matrix;
ExpectEmit(R"(-
- 1
- 0
- 0
-
- 0
- 1
- 0
-
- 0
- 0
- 1)");
}
TEST_F(EmitterTest, NestedSTLContainersPart2) {
std::map<std::string, std::map<std::string, int>> data;
data["zero"] = std::map<std::string, int>{{"john", 0}, {"doe", 1}};
data["one"] = std::map<std::string, int>{{"hello", 2}, {"world", 3}};
out << data;
// when we iterate over a map we iterate over the sorted keys.
ExpectEmit("one:\n hello: 2\n world: 3\nzero:\n doe: 1\n john: 0");
}
TEST_F(EmitterTest, NestedSTLContainersPart3) {
std::map<std::string, std::pair<std::forward_list<std::bitset<2>>,
std::vector<std::deque<Foo>>>>
data;
std::bitset<2> bits;
bits.set(0, true);
bits.set(1, false);
std::forward_list<std::bitset<2>> thelist;
thelist.push_front(bits);
std::deque<Foo> thedeque;
thedeque.push_back(Foo(0, "hello"));
thedeque.push_back(Foo(1, "world"));
std::vector<std::deque<Foo>> thevector;
thevector.push_back(thedeque);
thedeque[1] = Foo(2, "john");
thedeque.push_back(Foo(3, "kate"));
thevector.push_back(thedeque);
data["one"] = std::make_pair(thelist, thevector);
thelist.clear();
bits.set(1, false);
thelist.push_front(bits);
bits.set(1, true);
thelist.push_front(bits);
thedeque.push_front(Foo(4, "mary"));
thedeque.push_front(Foo(5, "bob"));
thedeque.push_back(Foo(6, "ellis"));
thevector.push_back(thedeque);
data["two"] = std::make_pair(thelist, thevector);
out << data;
// something crazy that hopefully nobody will ever use in practise but at
// least this works.
// NOTE: output seems overly verbose (what with the redundant newlines)
ExpectEmit(R"(one:
-
- 01
-
-
- x: 0
bar: hello
- x: 1
bar: world
-
- x: 0
bar: hello
- x: 2
bar: john
- x: 3
bar: kate
two:
-
- 11
- 01
-
-
- x: 0
bar: hello
- x: 1
bar: world
-
- x: 0
bar: hello
- x: 2
bar: john
- x: 3
bar: kate
-
- x: 5
bar: bob
- x: 4
bar: mary
- x: 0
bar: hello
- x: 2
bar: john
- x: 3
bar: kate
- x: 6
bar: ellis)");
}
template <class T>
class CustomAllocator : public std::allocator<T> {};
TEST_F(EmitterTest, StdVectorCustomAllocator) {
std::vector<int, CustomAllocator<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);
out << primes;
ExpectEmit("- 2\n- 3\n- 5\n- 7\n- 11\n- 13");
}
TEST_F(EmitterTest, StdMapCustomAllocator) {
std::map<std::string, int, std::less<std::string>,
CustomAllocator<std::pair<const std::string, int>>>
primes;
primes["two"] = 2;
primes["three"] = 3;
primes["five"] = 5;
primes["seven"] = 7;
primes["eleven"] = 11;
primes["thirteen"] = 13;
out << primes;
// NOTE: map runs through its element sorted.
ExpectEmit("eleven: 11\nfive: 5\nseven: 7\nthirteen: 13\nthree: 3\ntwo: 2");
}
TEST_F(EmitterTest, StdTupleSize0) {
out << std::tuple<>();
ExpectEmit("~");
}
TEST_F(EmitterTest, StdTupleSize1) {
out << std::make_tuple(42);
ExpectEmit("- 42");
}
TEST_F(EmitterTest, StdTupleSize2) {
out << std::make_tuple(42, 3.141592f);
ExpectEmit("- 42\n- 3.141592");
}
TEST_F(EmitterTest, StdTupleSize3) {
out << std::make_tuple(42, 3.141592f, "hello");
ExpectEmit("- 42\n- 3.141592\n- hello");
}
TEST_F(EmitterTest, StdTupleSize4) {
out << std::make_tuple(42, 3.141592f, "hello", "world");
ExpectEmit("- 42\n- 3.141592\n- hello\n- world");
}
TEST_F(EmitterTest, StdTupleSize5) {
out << std::make_tuple(42, 3.141592f, "hello", "world",
std::vector<int>{2, 3, 5, 7});
ExpectEmit(
R"(- 42
- 3.141592
- hello
- world
-
- 2
- 3
- 5
- 7)");
}
TEST_F(EmitterTest, StdTupleSize6) {
out << std::make_tuple(42, 3.141592f, "hello", "world",
std::vector<int>{2, 3, 5, 7},
std::map<int, int>{{1, 1}, {2, 4}, {3, 9}, {4, 16}});
ExpectEmit(R"(- 42
- 3.141592
- hello
- world
-
- 2
- 3
- 5
- 7
- 1: 1
2: 4
3: 9
4: 16)");
}
TEST_F(EmitterTest, StdTupleSize7) {
out << std::make_tuple(
42, 3.141592f, "hello", "world", std::vector<int>{2, 3, 5, 7},
std::map<int, int>{{1, 1}, {2, 4}, {3, 9}, {4, 16}}, Foo(10, "foo"));
ExpectEmit(R"(- 42
- 3.141592
- hello
- world
-
- 2
- 3
- 5
- 7
- 1: 1
2: 4
3: 9
4: 16
- x: 10
bar: foo)");
}
TEST_F(EmitterTest, SimpleComment) {
@ -658,22 +928,6 @@ TEST_F(EmitterTest, DoubleQuotedUnicode) {
ExpectEmit("\"\x24 \xC2\xA2 \xE2\x82\xAC \xF0\xA4\xAD\xA2\"");
}
struct Foo {
Foo() : x(0) {}
Foo(int x_, const std::string& bar_) : x(x_), bar(bar_) {}
int x;
std::string bar;
};
Emitter& operator<<(Emitter& out, const Foo& foo) {
out << BeginMap;
out << Key << "x" << Value << foo.x;
out << Key << "bar" << Value << foo.bar;
out << EndMap;
return out;
}
TEST_F(EmitterTest, UserType) {
out << BeginSeq;
out << Foo(5, "hello");