diff --git a/include/yaml-cpp/node/detail/iterator.h b/include/yaml-cpp/node/detail/iterator.h index 24866c2d8..98e5d2c45 100644 --- a/include/yaml-cpp/node/detail/iterator.h +++ b/include/yaml-cpp/node/detail/iterator.h @@ -23,7 +23,7 @@ #include "yaml-cpp/node/ptr.h" #include #include - +#include namespace YAML { namespace detail { @@ -52,7 +52,7 @@ class iterator_base { public: using iterator_category = std::bidirectional_iterator_tag; - using value_type = V; + using value_type = typename std::remove_cv::type; using difference_type = std::ptrdiff_t; using pointer = V*; using reference = V&; @@ -100,13 +100,13 @@ class iterator_base { return m_iterator != rhs.m_iterator; } - value_type operator*() const YAML_ATTRIBUTE_LIFETIME_BOUND { + V operator*() const YAML_ATTRIBUTE_LIFETIME_BOUND { const typename base_type::value_type& v = *m_iterator; if (v.pNode) - return value_type(Node(*v, m_pMemory)); + return V(Node(*v, m_pMemory)); if (v.first && v.second) - return value_type(Node(*v.first, m_pMemory), Node(*v.second, m_pMemory)); - return value_type(); + return V(Node(*v.first, m_pMemory), Node(*v.second, m_pMemory)); + return V(); } proxy operator->() const YAML_ATTRIBUTE_LIFETIME_BOUND { diff --git a/include/yaml-cpp/node/detail/reverse_iterator.h b/include/yaml-cpp/node/detail/reverse_iterator.h index b8c87ae3c..768a2b766 100644 --- a/include/yaml-cpp/node/detail/reverse_iterator.h +++ b/include/yaml-cpp/node/detail/reverse_iterator.h @@ -20,6 +20,7 @@ #include "yaml-cpp/node/ptr.h" #include #include +#include namespace YAML { namespace detail { @@ -90,7 +91,8 @@ class reverse_iterator_base { return current; } - value_type operator*() const YAML_ATTRIBUTE_LIFETIME_BOUND { + typename std::remove_reference::type operator*() const + YAML_ATTRIBUTE_LIFETIME_BOUND { Iter _tmp = current; return *(--_tmp); } diff --git a/test/node/iterator_test.cpp b/test/node/iterator_test.cpp new file mode 100644 index 000000000..12120ece4 --- /dev/null +++ b/test/node/iterator_test.cpp @@ -0,0 +1,121 @@ +#include "yaml-cpp/yaml.h" + +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include + +namespace YAML { +namespace { + +template +void CheckIteratorTraits() { + using Traits = std::iterator_traits; + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same()), V>::value)); + EXPECT_TRUE((std::is_same::value)); +} + +template +std::vector::value_type> Collect( + Iterator first, Iterator last) { + return std::vector::value_type>(first, + last); +} + +template +void CheckSequence(Iterator first, Iterator last, + const std::vector& expected) { + auto values = Collect(first, last); + ASSERT_EQ(expected.size(), values.size()); + for (std::size_t i = 0; i < values.size(); ++i) { + EXPECT_EQ(expected[i], values[i].template as()); + } +} + +template +void CheckMap(Iterator first, Iterator last, + const std::vector>& expected) { + auto values = Collect(first, last); + ASSERT_EQ(expected.size(), values.size()); + for (std::size_t i = 0; i < values.size(); ++i) { + EXPECT_EQ(expected[i].first, values[i].first.template as()); + EXPECT_EQ(expected[i].second, values[i].second.template as()); + } +} + +TEST(IteratorTest, ValueTypeDoesNotChangeDereferenceConstness) { + CheckIteratorTraits(); + CheckIteratorTraits(); + CheckIteratorTraits(); + CheckIteratorTraits(); +} + +TEST(IteratorTest, CollectSequenceUsingIteratorValueType) { + Node node = Load("[11, 22, 33]"); + const Node& cn = node; + const std::vector forward{11, 22, 33}; + const std::vector backward{33, 22, 11}; + + CheckSequence(node.begin(), node.end(), forward); + CheckSequence(cn.begin(), cn.end(), forward); + CheckSequence(node.rbegin(), node.rend(), backward); + CheckSequence(cn.rbegin(), cn.rend(), backward); + EXPECT_EQ(forward, node.as>()); +} + +TEST(IteratorTest, CollectMapUsingIteratorValueType) { + Node node = Load("{first: 11, second: 22, third: 33}"); + const Node& cn = node; + const std::vector> forward{ + {"first", 11}, {"second", 22}, {"third", 33}}; + const std::vector> backward{ + {"third", 33}, {"second", 22}, {"first", 11}}; + + CheckMap(node.begin(), node.end(), forward); + CheckMap(cn.begin(), cn.end(), forward); + CheckMap(node.rbegin(), node.rend(), backward); + CheckMap(cn.rbegin(), cn.rend(), backward); + EXPECT_EQ(3, node.size()); + EXPECT_EQ(11, node["first"].as()); + EXPECT_EQ(33, node["third"].as()); +} + +TEST(IteratorTest, CollectEmptyRangesUsingIteratorValueType) { + for (NodeType::value type : {NodeType::Undefined, NodeType::Null, + NodeType::Sequence, NodeType::Map}) { + Node node(type); + const Node& cn = node; + EXPECT_TRUE(Collect(node.begin(), node.end()).empty()); + EXPECT_TRUE(Collect(cn.begin(), cn.end()).empty()); + EXPECT_TRUE(Collect(node.rbegin(), node.rend()).empty()); + EXPECT_TRUE(Collect(cn.rbegin(), cn.rend()).empty()); + } +} + +TEST(IteratorTest, CollectedValuesKeepNodeIdentity) { + Node sequence = Load("[11, 22]"); + const Node& cn = sequence; + auto values = Collect(cn.begin(), cn.end()); + ASSERT_EQ(2, values.size()); + EXPECT_TRUE(values[0].is(sequence[0])); + EXPECT_TRUE(values[1].is(sequence[1])); + + Node map = Load("{first: 11, second: 22}"); + const Node& cm = map; + auto pairs = Collect(cm.rbegin(), cm.rend()); + ASSERT_EQ(2, pairs.size()); + EXPECT_TRUE(pairs[0].first.is(map.rbegin()->first)); + EXPECT_TRUE(pairs[0].second.is(map["second"])); + EXPECT_TRUE(pairs[1].second.is(map["first"])); +} + +} // namespace +} // namespace YAML