From 67ae33d90882690a9149dc7a5e8fe5d8946c4085 Mon Sep 17 00:00:00 2001 From: Fredrik Appelros Date: Thu, 7 Jun 2018 17:37:53 +0200 Subject: [PATCH] Make nodes hashable Implement a specialization of `std::hash` so that nodes can be used in unordered associative containers. --- include/yaml-cpp/node/detail/node.h | 2 ++ include/yaml-cpp/node/hash.h | 23 +++++++++++++++++++++++ include/yaml-cpp/node/node.h | 1 + include/yaml-cpp/yaml.h | 1 + test/node/node_test.cpp | 12 ++++++++++++ 5 files changed, 39 insertions(+) create mode 100644 include/yaml-cpp/node/hash.h diff --git a/include/yaml-cpp/node/detail/node.h b/include/yaml-cpp/node/detail/node.h index a2cc52b..48fb571 100644 --- a/include/yaml-cpp/node/detail/node.h +++ b/include/yaml-cpp/node/detail/node.h @@ -18,6 +18,8 @@ namespace YAML { namespace detail { class node { public: + friend struct std::hash; + node() : m_pRef(new node_ref), m_dependencies{} {} node(const node&) = delete; node& operator=(const node&) = delete; diff --git a/include/yaml-cpp/node/hash.h b/include/yaml-cpp/node/hash.h new file mode 100644 index 0000000..8d5f9b0 --- /dev/null +++ b/include/yaml-cpp/node/hash.h @@ -0,0 +1,23 @@ +#ifndef NODE_HASH_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#define NODE_HASH_H_62B23520_7C8E_11DE_8A39_0800200C9A66 + +#include "yaml-cpp/node/detail/node.h" +#include "yaml-cpp/node/node.h" + +namespace std { +template <> +struct hash { + size_t operator()(const YAML::detail::node& key) const noexcept { + return hash()(key.m_pRef); + } +}; + +template <> +struct hash { + size_t operator()(const YAML::Node& key) const noexcept { + return hash()(*key.m_pNode); + } +}; +} // namespace std + +#endif // NODE_HASH_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/node.h b/include/yaml-cpp/node/node.h index 49af58e..e813cf7 100644 --- a/include/yaml-cpp/node/node.h +++ b/include/yaml-cpp/node/node.h @@ -38,6 +38,7 @@ class YAML_CPP_API Node { friend class detail::iterator_base; template friend struct as_if; + friend struct std::hash; typedef YAML::iterator iterator; typedef YAML::const_iterator const_iterator; diff --git a/include/yaml-cpp/yaml.h b/include/yaml-cpp/yaml.h index 7f515ef..6a754ec 100644 --- a/include/yaml-cpp/yaml.h +++ b/include/yaml-cpp/yaml.h @@ -20,5 +20,6 @@ #include "yaml-cpp/node/detail/impl.h" #include "yaml-cpp/node/parse.h" #include "yaml-cpp/node/emit.h" +#include "yaml-cpp/node/hash.h" #endif // YAML_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/test/node/node_test.cpp b/test/node/node_test.cpp index 18234db..47df6cf 100644 --- a/test/node/node_test.cpp +++ b/test/node/node_test.cpp @@ -3,6 +3,7 @@ #include "yaml-cpp/node/convert.h" #include "yaml-cpp/node/detail/impl.h" #include "yaml-cpp/node/emit.h" +#include "yaml-cpp/node/hash.h" #include "yaml-cpp/node/impl.h" #include "yaml-cpp/node/iterator.h" @@ -437,6 +438,17 @@ TEST(NodeTest, AccessNonexistentKeyOnConstNode) { ASSERT_FALSE(other["5"]); } +TEST(NodeTest, NodeIsHashable) { + YAML::Node node1(YAML::NodeType::value::Null); + YAML::Node node2(YAML::NodeType::value::Null); + YAML::Node node3 = node1; + std::hash hash_func; + auto node1_digest = hash_func(node1); + ASSERT_EQ(hash_func(node1), node1_digest); + ASSERT_NE(hash_func(node2), node1_digest); + ASSERT_EQ(hash_func(node3), node1_digest); +} + class NodeEmitterTest : public ::testing::Test { protected: void ExpectOutput(const std::string& output, const Node& node) {