From c66a0f75242986074e3faa52e3ba48b607a4faaf Mon Sep 17 00:00:00 2001 From: Ted Lyngmo Date: Wed, 22 Jan 2020 11:40:50 +0100 Subject: [PATCH] Make Node move constructor and assignment operator noexcept (#809) Move constructor: * m_isValid (bool) exchange(rhs.m_isValid, true) * m_invalidKey (std::string) std::move() * m_pMemory (shared_memory_holder) std::move() * m_pNode (node*) exchange(rhs.m_pNode, nullptr) This leaves the moved-from Node as if it was just default constructed. Move assignment: A temporary Node is move constructed (using the above) leaving the moved-from Node as if it was just default constructed. *this then assigns the temporary, using AssignNodeDetail() directly to avoid a second self-assignment check. Signed-off-by: Ted Lyngmo --- include/yaml-cpp/node/impl.h | 47 +++++++++++++++++++++++++++++++++++- include/yaml-cpp/node/node.h | 7 +++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/include/yaml-cpp/node/impl.h b/include/yaml-cpp/node/impl.h index 06a4884d9..98a5dd2cd 100644 --- a/include/yaml-cpp/node/impl.h +++ b/include/yaml-cpp/node/impl.h @@ -12,11 +12,28 @@ #include "yaml-cpp/node/detail/node.h" #include "yaml-cpp/node/iterator.h" #include "yaml-cpp/node/node.h" +#include "yaml-cpp/noexcept.h" #include #include +#include namespace YAML { -inline Node::Node() +namespace detail { +#if __cplusplus >= 201402L +using ::std::exchange; +#else +template +T exchange(T& obj, U&& new_value) { + T old_value = std::move(obj); + obj = std::forward(new_value); + return old_value; +} +#endif +} // namespace detail +} // namespace YAML + +namespace YAML { +inline Node::Node() YAML_CPP_NOEXCEPT : m_isValid(true), m_invalidKey{}, m_pMemory(nullptr), m_pNode(nullptr) {} inline Node::Node(NodeType::value type) @@ -44,6 +61,13 @@ inline Node::Node(const detail::iterator_value& rhs) inline Node::Node(const Node& rhs) = default; +inline Node::Node(Node&& rhs) YAML_CPP_NOEXCEPT + : m_isValid(detail::exchange(rhs.m_isValid, true)), + m_invalidKey(std::move(rhs.m_invalidKey)), + m_pMemory(std::move(rhs.m_pMemory)), + m_pNode(detail::exchange(rhs.m_pNode, nullptr)) { +} + inline Node::Node(Zombie) : m_isValid(false), m_invalidKey{}, m_pMemory{}, m_pNode(nullptr) {} @@ -188,6 +212,13 @@ inline void Node::SetStyle(EmitterStyle::value style) { } // assignment +inline bool Node::CheckValid(const Node& rhs) const YAML_CPP_NOEXCEPT { + return + m_isValid && rhs.m_isValid && + m_pNode != nullptr && rhs.m_pNode != nullptr && + !m_pNode->is(*rhs.m_pNode); +} + inline bool Node::is(const Node& rhs) const { if (!m_isValid || !rhs.m_isValid) throw InvalidNode(m_invalidKey); @@ -209,6 +240,16 @@ inline Node& Node::operator=(const Node& rhs) { return *this; } +inline Node& Node::operator=(Node&& rhs) YAML_CPP_NOEXCEPT { + if (!CheckValid(rhs)) + return *this; + + Node tmp(std::move(rhs)); + AssignNodeDetail(tmp); + + return *this; +} + inline void Node::reset(const YAML::Node& rhs) { if (!m_isValid || !rhs.m_isValid) throw InvalidNode(m_invalidKey); @@ -258,6 +299,10 @@ inline void Node::AssignNode(const Node& rhs) { return; } + AssignNodeDetail(rhs); +} + +inline void Node::AssignNodeDetail(const Node& rhs) YAML_CPP_NOEXCEPT { m_pNode->set_ref(*rhs.m_pNode); m_pMemory->merge(*rhs.m_pMemory); m_pNode = rhs.m_pNode; diff --git a/include/yaml-cpp/node/node.h b/include/yaml-cpp/node/node.h index c9e9a0a4b..da437864e 100644 --- a/include/yaml-cpp/node/node.h +++ b/include/yaml-cpp/node/node.h @@ -16,6 +16,7 @@ #include "yaml-cpp/node/detail/iterator_fwd.h" #include "yaml-cpp/node/ptr.h" #include "yaml-cpp/node/type.h" +#include "yaml-cpp/noexcept.h" namespace YAML { namespace detail { @@ -41,12 +42,13 @@ class YAML_CPP_API Node { using iterator = YAML::iterator; using const_iterator = YAML::const_iterator; - Node(); + Node() YAML_CPP_NOEXCEPT; explicit Node(NodeType::value type); template explicit Node(const T& rhs); explicit Node(const detail::iterator_value& rhs); Node(const Node& rhs); + Node(Node&& rhs) YAML_CPP_NOEXCEPT; ~Node(); YAML::Mark Mark() const; @@ -77,10 +79,12 @@ class YAML_CPP_API Node { void SetStyle(EmitterStyle::value style); // assignment + bool CheckValid(const Node& rhs) const YAML_CPP_NOEXCEPT; bool is(const Node& rhs) const; template Node& operator=(const T& rhs); Node& operator=(const Node& rhs); + Node& operator=(Node&& rhs) YAML_CPP_NOEXCEPT; void reset(const Node& rhs = Node()); // size/iterator @@ -128,6 +132,7 @@ class YAML_CPP_API Node { void AssignData(const Node& rhs); void AssignNode(const Node& rhs); + void AssignNodeDetail(const Node& rhs) YAML_CPP_NOEXCEPT; private: bool m_isValid;