diff --git a/include/yaml-cpp/emitter.h b/include/yaml-cpp/emitter.h index 6f142b011..531400e4c 100644 --- a/include/yaml-cpp/emitter.h +++ b/include/yaml-cpp/emitter.h @@ -270,10 +270,16 @@ inline Emitter& operator<<(Emitter& emitter, EMITTER_MANIP value) { inline Emitter& operator<<(Emitter& emitter, _Indent indent) { return emitter.SetLocalIndent(indent); } - inline Emitter& operator<<(Emitter& emitter, _Precision precision) { return emitter.SetLocalPrecision(precision); } + +inline Emitter& operator<<(Emitter& emitter, std::nullptr_t) { + return emitter << _Null{}; +} +inline Emitter& operator<<(Emitter& emitter, const void* v) { + return emitter.WriteIntegralType(v); +} } // namespace YAML #endif // EMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/stlemitter.h b/include/yaml-cpp/stlemitter.h index 06780c861..d1e82a655 100644 --- a/include/yaml-cpp/stlemitter.h +++ b/include/yaml-cpp/stlemitter.h @@ -7,45 +7,188 @@ #pragma once #endif -#include -#include -#include -#include +#include +#include +#include +#include +#include + +#include "emitter.h" 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; - return emitter; -} + +namespace detail { + +template +using enable_if_t = typename std::enable_if::type; + +template +struct make_void { + using type = void; +}; + +template +using void_t = typename make_void::type; + +template +struct is_std_iterable : std::false_type {}; + +template +struct is_std_iterable< + T, enable_if_t())), + decltype(std::end(std::declval()))>::value>> + : std::true_type {}; + +#define TRAITS_DECL_CLASS_HAS_TYPE(name) \ + template \ + struct has_type_##name : std::false_type {}; \ + template \ + struct has_type_##name> : std::true_type {}; + +TRAITS_DECL_CLASS_HAS_TYPE(element_type) +TRAITS_DECL_CLASS_HAS_TYPE(mapped_type) + +#undef TRAITS_DECL_CLASS_HAS_TYPE template -inline Emitter& operator<<(Emitter& emitter, const std::vector& v) { - return EmitSeq(emitter, v); +inline enable_if_t< + std::is_same::value || std::is_same::value, int> + as_numeric(const T& value) { + return static_cast(value); } template -inline Emitter& operator<<(Emitter& emitter, const std::list& v) { - return EmitSeq(emitter, v); +inline enable_if_t< + !(std::is_same::value || std::is_same::value), T> + as_numeric(const T& value) { + return value; } +template +struct sequential_printer { + template + inline static void print(S& stream, const T& value) { + const auto i = N - 1; + + sequential_printer::print(stream, value); + stream << std::get(value); + } +}; + template -inline Emitter& operator<<(Emitter& emitter, const std::set& v) { - return EmitSeq(emitter, v); +struct sequential_printer { + template + inline static void print(S& stream, const T& value) { + stream << std::get<0>(value); + } +}; + +template +inline E& emit_sequence(E& emitter, const T& value) { + emitter << BeginSeq; + for (const auto& item : value) { + emitter << item; + } + return emitter << EndSeq; } -template -inline Emitter& operator<<(Emitter& emitter, const std::map& m) { - typedef typename std::map map; +template +inline E& emit_mapping(E& emitter, const T& value) { emitter << BeginMap; - for (typename map::const_iterator it = m.begin(); it != m.end(); ++it) - emitter << Key << it->first << Value << it->second; - emitter << EndMap; + for (const auto& key_value : value) { + emitter << Key << std::get<0>(key_value) << Value << std::get<1>(key_value); + } + return emitter << EndMap; +} + +template +inline Emitter& emit_streamable(Emitter& emitter, const T& value, + std::stringstream* stream = nullptr) { + std::stringstream stream_fallback; + if (stream == nullptr) { + stream = &stream_fallback; + } else { + stream->str(""); + } + + *stream << value; + return emitter << stream->str(); +} + +template +inline Emitter& emit_complex(Emitter& emitter, const T& real, const T& imag) { +#ifndef YAML_EMITTER_NO_COMPLEX + + std::stringstream ss; + ss << as_numeric(real) << '+' << as_numeric(imag) << 'j'; + emitter << LocalTag("complex") << ss.str(); return emitter; + +#else + + return emitter << Flow << BeginSeq << as_numeric(real) << as_numeric(imag) + << EndSeq; + +#endif +} + +} // namespace detail + +//// std::complex + +template +inline Emitter& operator<<(Emitter& emitter, const std::complex& value) { + // return detail::emit_streamable(emitter << LocalTag("complex"), value); + //// stl style + return detail::emit_complex(emitter, value.real(), value.imag()); +} + +//// std::pair + +template +inline Emitter& operator<<(Emitter& emitter, const std::pair& value) { + return emitter << Flow << BeginSeq << value.first << value.second << EndSeq; +} + +//// std::tuple + +template +inline Emitter& operator<<(Emitter& emitter, const std::tuple& value) { + emitter << Flow << BeginSeq; + detail::sequential_printer, sizeof...(Args)>::print( + emitter, value); + return emitter << EndSeq; } + +//// std::array, std::vector, std::deque, std::list, std::forward_list +//// std::set, std::multiset, std::unordered_set + +template +inline detail::enable_if_t::value && + !detail::has_type_mapped_type::value, + Emitter&> + operator<<(Emitter& emitter, const T& value) { + return detail::emit_sequence(emitter, value); } +//// std::map, std::unordered_map + +template +inline detail::enable_if_t::value && + detail::has_type_mapped_type::value, + Emitter&> + operator<<(Emitter& emitter, const T& value) { + return detail::emit_mapping(emitter, value); +} + +//// std::unique_ptr, std::shared_ptr + +template +inline detail::enable_if_t::value, Emitter&> + operator<<(Emitter& emitter, const T& value) { + return emitter << value.get(); +} + +} // namespace YAML + #endif // STLEMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/emitter.cpp b/src/emitter.cpp index 06f633c17..c360fece7 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -198,6 +198,7 @@ void Emitter::EmitEndSeq() { if (!good()) return; + bool need_left_brace = !m_pState->HasBegunNode() || m_stream.comment(); if (m_pState->CurGroupChildCount() == 0) m_pState->ForceFlow(); @@ -205,7 +206,7 @@ void Emitter::EmitEndSeq() { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(m_pState->CurIndent()); - if (m_pState->CurGroupChildCount() == 0) + if (m_pState->CurGroupChildCount() == 0 && need_left_brace) m_stream << "["; m_stream << "]"; } @@ -228,6 +229,7 @@ void Emitter::EmitEndMap() { if (!good()) return; + bool need_left_brace = !m_pState->HasBegunNode() || m_stream.comment(); if (m_pState->CurGroupChildCount() == 0) m_pState->ForceFlow(); @@ -235,7 +237,7 @@ void Emitter::EmitEndMap() { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(m_pState->CurIndent()); - if (m_pState->CurGroupChildCount() == 0) + if (m_pState->CurGroupChildCount() == 0 && need_left_brace) m_stream << "{"; m_stream << "}"; } diff --git a/src/emitterstate.cpp b/src/emitterstate.cpp index 5c76226a5..8ef55a411 100644 --- a/src/emitterstate.cpp +++ b/src/emitterstate.cpp @@ -211,11 +211,15 @@ bool EmitterState::CurGroupLongKey() const { } std::size_t EmitterState::LastIndent() const { - if (m_groups.size() <= 1) { - return 0; - } + if (HasBegunNode()) { + return m_curIndent + (m_groups.empty() ? m_indent.get() : m_groups.back()->indent); + } else { + if (m_groups.size() <= 1) { + return 0; + } - return m_curIndent - m_groups[m_groups.size() - 2]->indent; + return m_curIndent - m_groups[m_groups.size() - 2]->indent; + } } void EmitterState::ClearModifiedSettings() { m_modifiedSettings.clear(); } diff --git a/test/integration/emitter_test.cpp b/test/integration/emitter_test.cpp index 3a5783bff..9bcd5e053 100644 --- a/test/integration/emitter_test.cpp +++ b/test/integration/emitter_test.cpp @@ -566,7 +566,7 @@ TEST_F(EmitterTest, InitialCommentWithDocIndicator) { TEST_F(EmitterTest, CommentInFlowSeq) { out << Flow << BeginSeq << "foo" << Comment("foo!") << "bar" << EndSeq; - ExpectEmit("[foo, # foo!\nbar]"); + ExpectEmit("[foo, # foo!\n bar]"); } TEST_F(EmitterTest, CommentInFlowMap) { @@ -577,7 +577,7 @@ TEST_F(EmitterTest, CommentInFlowMap) { out << EndMap; ExpectEmit( - "{foo: foo value, bar: bar value, # bar!\nbaz: baz value, # baz!\n}"); + "{foo: foo value, bar: bar value, # bar!\n baz: baz value, # baz!\n}"); } TEST_F(EmitterTest, Indentation) { @@ -743,7 +743,7 @@ TEST_F(EmitterTest, NewlineInFlowSequence) { out << "a" << Newline << "b" << "c" << Newline << "d"; out << EndSeq; - ExpectEmit("[a,\nb, c,\nd]"); + ExpectEmit("[a,\n b, c,\n d]"); } TEST_F(EmitterTest, NewlineInBlockMap) { @@ -760,7 +760,7 @@ TEST_F(EmitterTest, NewlineInFlowMap) { out << Key << "a" << Value << "foo" << Newline; out << Key << "b" << Value << "bar"; out << EndMap; - ExpectEmit("{a: foo,\nb: bar}"); + ExpectEmit("{a: foo,\n b: bar}"); } TEST_F(EmitterTest, LotsOfNewlines) { @@ -971,28 +971,28 @@ TEST_F(EmitterTest, QuoteNull) { } TEST_F(EmitterTest, ValueOfDoubleQuote) { - out << YAML::BeginMap; - out << YAML::Key << "foo" << YAML::Value << '"'; - out << YAML::EndMap; + out << BeginMap; + out << Key << "foo" << Value << '"'; + out << EndMap; ExpectEmit("foo: \"\\\"\""); } TEST_F(EmitterTest, ValueOfBackslash) { - out << YAML::BeginMap; - out << YAML::Key << "foo" << YAML::Value << '\\'; - out << YAML::EndMap; + out << BeginMap; + out << Key << "foo" << Value << '\\'; + out << EndMap; ExpectEmit("foo: \"\\\\\""); } TEST_F(EmitterTest, Infinity) { - out << YAML::BeginMap; - out << YAML::Key << "foo" << YAML::Value + out << BeginMap; + out << Key << "foo" << Value << std::numeric_limits::infinity(); - out << YAML::Key << "bar" << YAML::Value + out << Key << "bar" << Value << std::numeric_limits::infinity(); - out << YAML::EndMap; + out << EndMap; ExpectEmit( "foo: .inf\n" @@ -1000,12 +1000,12 @@ TEST_F(EmitterTest, Infinity) { } TEST_F(EmitterTest, NegInfinity) { - out << YAML::BeginMap; - out << YAML::Key << "foo" << YAML::Value + out << BeginMap; + out << Key << "foo" << Value << -std::numeric_limits::infinity(); - out << YAML::Key << "bar" << YAML::Value + out << Key << "bar" << Value << -std::numeric_limits::infinity(); - out << YAML::EndMap; + out << EndMap; ExpectEmit( "foo: -.inf\n" @@ -1013,12 +1013,12 @@ TEST_F(EmitterTest, NegInfinity) { } TEST_F(EmitterTest, NaN) { - out << YAML::BeginMap; - out << YAML::Key << "foo" << YAML::Value + out << BeginMap; + out << Key << "foo" << Value << std::numeric_limits::quiet_NaN(); - out << YAML::Key << "bar" << YAML::Value + out << Key << "bar" << Value << std::numeric_limits::quiet_NaN(); - out << YAML::EndMap; + out << EndMap; ExpectEmit( "foo: .nan\n"