From 5224a3563d1af8dbc090c81d15969482f5c0a340 Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Tue, 14 Feb 2017 12:44:18 +0100 Subject: [PATCH] Add more std container overloads for YAML::Emitter yaml sequences: - std vector - std list - std deque - std forward_list - std array - std set - std unordered_set - std pair - std tuple yaml maps: - std map - std unordered_map yaml scalars: - std bitset (zeroes and ones) --- include/yaml-cpp/stlemitter.h | 160 ++++++++++++---- test/integration/emitter_test.cpp | 296 +++++++++++++++++++++++++++--- 2 files changed, 402 insertions(+), 54 deletions(-) diff --git a/include/yaml-cpp/stlemitter.h b/include/yaml-cpp/stlemitter.h index 06780c8..4060f45 100644 --- a/include/yaml-cpp/stlemitter.h +++ b/include/yaml-cpp/stlemitter.h @@ -7,45 +7,139 @@ #pragma once #endif -#include +#include +#include +#include +#include #include -#include #include +#include +#include +#include +#include +#include namespace YAML { -template -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 +Emitter& EmitSeq(Emitter& emit, const SequenceLike& sequence) { + emit << BeginSeq; + for (const auto& item : sequence) + emit << item; + emit << EndSeq; + return emit; +} + +template +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 +typename std::enable_if::type EmitTuple( + Emitter& emit, const std::tuple& /*tup*/) { + return emit; +} + +template +typename std::enable_if::type EmitTuple( + Emitter& emit, const std::tuple& tup) { + emit << std::get(tup); + return EmitTuple(emit, tup); +} + +} // namespace detail + +// std::vector +template +Emitter& operator<<(Emitter& emit, const std::vector& t) { + return detail::EmitSeq(emit, t); +} + +// std::list +template +Emitter& operator<<(Emitter& emit, const std::list& t) { + return detail::EmitSeq(emit, t); +} + +// std::deque +template +Emitter& operator<<(Emitter& emit, const std::deque& t) { + return detail::EmitSeq(emit, t); +} + +// std::forward_list +template +Emitter& operator<<(Emitter& emit, const std::forward_list& t) { + return detail::EmitSeq(emit, t); +} + +// std::array +template +Emitter& operator<<(Emitter& emit, const std::array& t) { + return detail::EmitSeq(emit, t); +} + +// std::map +template +Emitter& operator<<(Emitter& emit, const std::map& t) { + return detail::EmitMap(emit, t); +} + +// std::unordered_map +template +Emitter& operator<<( + Emitter& emit, const std::unordered_map& t) { + return detail::EmitMap(emit, t); +} + +// std::set +template +Emitter& operator<<(Emitter& emit, const std::set& t) { + return detail::EmitSeq(emit, t); +} + +// std::unordered_set +template +Emitter& operator<<(Emitter& emit, + const std::unordered_set& t) { + return detail::EmitSeq(emit, t); +} + +// std::bitset +template +Emitter& operator<<(Emitter& emit, const std::bitset& bits) { + return emit << bits.to_string(); +} + +// std::pair +template +Emitter& operator<<(Emitter& emit, const std::pair& pair) { + emit << BeginSeq << pair.first << pair.second << EndSeq; + return emit; +} + +// std::tuple +template +Emitter& operator<<(Emitter& emit, const std::tuple& 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 -inline Emitter& operator<<(Emitter& emitter, const std::vector& v) { - return EmitSeq(emitter, v); -} - -template -inline Emitter& operator<<(Emitter& emitter, const std::list& v) { - return EmitSeq(emitter, v); -} - -template -inline Emitter& operator<<(Emitter& emitter, const std::set& v) { - return EmitSeq(emitter, v); -} - -template -inline Emitter& operator<<(Emitter& emitter, const std::map& m) { - typedef typename std::map 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 diff --git a/test/integration/emitter_test.cpp b/test/integration/emitter_test.cpp index 2780838..31c1690 100644 --- a/test/integration/emitter_test.cpp +++ b/test/integration/emitter_test.cpp @@ -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 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 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 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> matrix; + matrix.push_back(std::vector{1, 0, 0}); + matrix.push_back(std::vector{0, 1, 0}); + matrix.push_back(std::vector{0, 0, 1}); + out << Block << matrix; + + ExpectEmit(R"(- + - 1 + - 0 + - 0 +- + - 0 + - 1 + - 0 +- + - 0 + - 0 + - 1)"); +} + +TEST_F(EmitterTest, NestedSTLContainersPart2) { + + std::map> data; + data["zero"] = std::map{{"john", 0}, {"doe", 1}}; + data["one"] = std::map{{"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::vector>>> + data; + + std::bitset<2> bits; + bits.set(0, true); + bits.set(1, false); + + std::forward_list> thelist; + thelist.push_front(bits); + + std::deque thedeque; + thedeque.push_back(Foo(0, "hello")); + thedeque.push_back(Foo(1, "world")); + + std::vector> 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 CustomAllocator : public std::allocator {}; + +TEST_F(EmitterTest, StdVectorCustomAllocator) { + std::vector> 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, + CustomAllocator>> + 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{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{2, 3, 5, 7}, + std::map{{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{2, 3, 5, 7}, + std::map{{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");