diff --git a/docs/Tutorial.md b/docs/Tutorial.md index a7b0e21..ba44d67 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -178,11 +178,12 @@ struct convert { return node; } - static bool decode(const Node& node, Vec3& rhs) { + static Vec3 decode(const Node& node) { if(!node.IsSequence() || node.size() != 3) { - return false; + throw YAML::conversion::DecodeException(""); } + Vec3 rhs; rhs.x = node[0].as(); rhs.y = node[1].as(); rhs.z = node[2].as(); diff --git a/include/yaml-cpp/exceptions.h b/include/yaml-cpp/exceptions.h index f6b2602..4788418 100644 --- a/include/yaml-cpp/exceptions.h +++ b/include/yaml-cpp/exceptions.h @@ -298,6 +298,15 @@ class YAML_CPP_API BadFile : public Exception { BadFile(const BadFile&) = default; ~BadFile() YAML_CPP_NOEXCEPT override; }; + + +namespace conversion{ + class DecodeException : public std::runtime_error { + public: + DecodeException(const std::string& s="") : std::runtime_error(s) {}; + }; +} + } // namespace YAML #endif // EXCEPTIONS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/include/yaml-cpp/node/convert.h b/include/yaml-cpp/node/convert.h index 21619a4..4182bd9 100644 --- a/include/yaml-cpp/node/convert.h +++ b/include/yaml-cpp/node/convert.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "yaml-cpp/binary.h" #include "yaml-cpp/node/impl.h" @@ -28,10 +29,14 @@ namespace YAML { class Binary; struct _Null; + template struct convert; } // namespace YAML +#define BAD_DECODE_EXCEPTION throw YAML::conversion::DecodeException(); + + namespace YAML { namespace conversion { inline bool IsInfinity(const std::string& input) { @@ -53,9 +58,11 @@ template <> struct convert { static Node encode(const Node& rhs) { return rhs; } - static bool decode(const Node& node, Node& rhs) { + static Node decode(const Node& node) { //FIXME, this is dangerous + throw std::runtime_error("this should not have been encountered"); + Node rhs; rhs.reset(node); - return true; + return rhs; } }; @@ -64,12 +71,12 @@ template <> struct convert { static Node encode(const std::string& rhs) { return Node(rhs); } - static bool decode(const Node& node, std::string& rhs) { + static std::string decode(const Node& node) { if (!node.IsScalar()) - return false; - rhs = node.Scalar(); - return true; + BAD_DECODE_EXCEPTION + return node.Scalar(); } + }; // C-strings can only be encoded @@ -92,8 +99,10 @@ template <> struct convert<_Null> { static Node encode(const _Null& /* rhs */) { return Node(); } - static bool decode(const Node& node, _Null& /* rhs */) { - return node.IsNull(); + static _Null decode(const Node& node) { + if (!node.IsNull()) + BAD_DECODE_EXCEPTION + return _Null(); } }; @@ -157,37 +166,35 @@ ConvertStreamTo(std::stringstream& stream, T& rhs) { return Node(stream.str()); \ } \ \ - static bool decode(const Node& node, type& rhs) { \ + static type decode(const Node& node) { \ if (node.Type() != NodeType::Scalar) { \ - return false; \ + BAD_DECODE_EXCEPTION; \ } \ const std::string& input = node.Scalar(); \ std::stringstream stream(input); \ stream.unsetf(std::ios::dec); \ if ((stream.peek() == '-') && std::is_unsigned::value) { \ - return false; \ + BAD_DECODE_EXCEPTION \ } \ + type rhs; \ if (conversion::ConvertStreamTo(stream, rhs)) { \ - return true; \ + return rhs; \ } \ if (std::numeric_limits::has_infinity) { \ if (conversion::IsInfinity(input)) { \ - rhs = std::numeric_limits::infinity(); \ - return true; \ + return std::numeric_limits::infinity(); \ } else if (conversion::IsNegativeInfinity(input)) { \ - rhs = negative_op std::numeric_limits::infinity(); \ - return true; \ + return negative_op std::numeric_limits::infinity(); \ } \ } \ \ if (std::numeric_limits::has_quiet_NaN) { \ if (conversion::IsNaN(input)) { \ - rhs = std::numeric_limits::quiet_NaN(); \ - return true; \ + return std::numeric_limits::quiet_NaN(); \ } \ } \ \ - return false; \ + BAD_DECODE_EXCEPTION \ } \ } @@ -223,7 +230,7 @@ template <> struct convert { static Node encode(bool rhs) { return rhs ? Node("true") : Node("false"); } - YAML_CPP_API static bool decode(const Node& node, bool& rhs); + YAML_CPP_API static bool decode(const Node& node); }; // std::map @@ -236,11 +243,11 @@ struct convert> { return node; } - static bool decode(const Node& node, std::map& rhs) { + static std::map decode(const Node& node) { if (!node.IsMap()) - return false; + BAD_DECODE_EXCEPTION - rhs.clear(); + std::map rhs; for (const auto& element : node) #if defined(__GNUC__) && __GNUC__ < 4 // workaround for GCC 3: @@ -248,7 +255,7 @@ struct convert> { #else rhs[element.first.as()] = element.second.as(); #endif - return true; + return rhs; } }; @@ -288,11 +295,11 @@ struct convert> { return node; } - static bool decode(const Node& node, std::vector& rhs) { + static std::vector decode(const Node& node) { if (!node.IsSequence()) - return false; + BAD_DECODE_EXCEPTION - rhs.clear(); + std::vector rhs; for (const auto& element : node) #if defined(__GNUC__) && __GNUC__ < 4 // workaround for GCC 3: @@ -300,7 +307,7 @@ struct convert> { #else rhs.push_back(element.as()); #endif - return true; + return rhs; } }; @@ -314,11 +321,11 @@ struct convert> { return node; } - static bool decode(const Node& node, std::list& rhs) { + static std::list decode(const Node& node) { if (!node.IsSequence()) - return false; + BAD_DECODE_EXCEPTION - rhs.clear(); + std::list rhs; for (const auto& element : node) #if defined(__GNUC__) && __GNUC__ < 4 // workaround for GCC 3: @@ -326,7 +333,7 @@ struct convert> { #else rhs.push_back(element.as()); #endif - return true; + return rhs; } }; @@ -341,11 +348,11 @@ struct convert> { return node; } - static bool decode(const Node& node, std::array& rhs) { - if (!isNodeValid(node)) { - return false; - } + static std::array decode(const Node& node) { + if (!isNodeValid(node)) + BAD_DECODE_EXCEPTION + std::array rhs; for (auto i = 0u; i < node.size(); ++i) { #if defined(__GNUC__) && __GNUC__ < 4 // workaround for GCC 3: @@ -354,7 +361,7 @@ struct convert> { rhs[i] = node[i].as(); #endif } - return true; + return rhs; } private: @@ -373,12 +380,11 @@ struct convert> { return node; } - static bool decode(const Node& node, std::pair& rhs) { - if (!node.IsSequence()) - return false; - if (node.size() != 2) - return false; + static std::pair decode(const Node& node) { + if (!node.IsSequence() or node.size() != 2) + BAD_DECODE_EXCEPTION + std::pair rhs; #if defined(__GNUC__) && __GNUC__ < 4 // workaround for GCC 3: rhs.first = node[0].template as(); @@ -391,7 +397,7 @@ struct convert> { #else rhs.second = node[1].as(); #endif - return true; + return rhs; } }; @@ -402,16 +408,17 @@ struct convert { return Node(EncodeBase64(rhs.data(), rhs.size())); } - static bool decode(const Node& node, Binary& rhs) { + static Binary decode(const Node& node) { if (!node.IsScalar()) - return false; + BAD_DECODE_EXCEPTION std::vector data = DecodeBase64(node.Scalar()); if (data.empty() && !node.Scalar().empty()) - return false; + BAD_DECODE_EXCEPTION + Binary rhs; rhs.swap(data); - return true; + return rhs; } }; } diff --git a/include/yaml-cpp/node/detail/impl.h b/include/yaml-cpp/node/detail/impl.h index b38038d..19dc426 100644 --- a/include/yaml-cpp/node/detail/impl.h +++ b/include/yaml-cpp/node/detail/impl.h @@ -98,19 +98,26 @@ struct remove_idx inline bool node::equals(const T& rhs, shared_memory_holder pMemory) { - T lhs; - if (convert::decode(Node(*this, pMemory), lhs)) { - return lhs == rhs; + try { + const auto rslt = convert::decode(Node(*this, pMemory)); + return rslt == rhs; + } catch (const conversion::DecodeException& e) { + return false; + } catch(...) { + std::rethrow_exception(std::current_exception()); } - return false; } inline bool node::equals(const char* rhs, shared_memory_holder pMemory) { - std::string lhs; - if (convert::decode(Node(*this, std::move(pMemory)), lhs)) { - return lhs == rhs; + try { + const auto rslt = + convert::decode(Node(*this, std::move(pMemory))); + return rslt == rhs; + } catch (const conversion::DecodeException& e) { + return false; + } catch(...) { + std::rethrow_exception(std::current_exception()); } - return false; } // indexing diff --git a/include/yaml-cpp/node/impl.h b/include/yaml-cpp/node/impl.h index 312281f..587f4c5 100644 --- a/include/yaml-cpp/node/impl.h +++ b/include/yaml-cpp/node/impl.h @@ -15,6 +15,7 @@ #include #include + namespace YAML { inline Node::Node() : m_isValid(true), m_invalidKey{}, m_pMemory(nullptr), m_pNode(nullptr) {} @@ -97,10 +98,35 @@ struct as_if { if (!node.m_pNode) return fallback; - T t; - if (convert::decode(node, t)) - return t; - return fallback; + try { + return convert::decode(node); + } catch (const conversion::DecodeException& e) { + return fallback; + } catch (...) { + std::rethrow_exception(std::current_exception()); + } + } +}; + +//specialize for Node +template +struct as_if { + explicit as_if(const Node& node_) : node(node_) {} + const Node& node; + + Node operator()(const S& fallback) const { + if (!node.m_pNode) + return fallback; + + try { + Node n; + n.reset(node); + return node; + } catch (const conversion::DecodeException& e) { + return fallback; + } catch (...) { + std::rethrow_exception(std::current_exception()); + } } }; @@ -127,11 +153,14 @@ struct as_if { if (!node.m_pNode) throw TypedBadConversion(node.Mark()); - T t; - if (convert::decode(node, t)) - return t; - throw TypedBadConversion(node.Mark()); - } + try { + return convert::decode(node); + } catch(const conversion::DecodeException& e) { + throw TypedBadConversion(node.Mark()); + } catch (...) { + std::rethrow_exception(std::current_exception()); + } + }; }; template <> @@ -321,6 +350,16 @@ std::string key_to_string(const Key& key) { } // indexing +template +inline bool Node::ContainsKey(const Key& key) const { + EnsureNodeExists(); + if (! IsMap()) + return false; + detail::node* value = + static_cast(*m_pNode).get(key, m_pMemory); + return (bool)value; +} + template inline const Node Node::operator[](const Key& key) const { EnsureNodeExists(); diff --git a/include/yaml-cpp/node/node.h b/include/yaml-cpp/node/node.h index c9e9a0a..634f09d 100644 --- a/include/yaml-cpp/node/node.h +++ b/include/yaml-cpp/node/node.h @@ -99,6 +99,8 @@ class YAML_CPP_API Node { // indexing template + bool ContainsKey(const Key& key) const; + template const Node operator[](const Key& key) const; template Node operator[](const Key& key); diff --git a/src/convert.cpp b/src/convert.cpp index 9658b36..7dda819 100644 --- a/src/convert.cpp +++ b/src/convert.cpp @@ -38,9 +38,9 @@ bool IsFlexibleCase(const std::string& str) { } // namespace namespace YAML { -bool convert::decode(const Node& node, bool& rhs) { +bool convert::decode(const Node& node) { if (!node.IsScalar()) - return false; + BAD_DECODE_EXCEPTION // we can't use iostream bool extraction operators as they don't // recognize all possible values in the table below (taken from @@ -55,20 +55,18 @@ bool convert::decode(const Node& node, bool& rhs) { }; if (!IsFlexibleCase(node.Scalar())) - return false; + BAD_DECODE_EXCEPTION for (const auto& name : names) { if (name.truename == tolower(node.Scalar())) { - rhs = true; return true; } if (name.falsename == tolower(node.Scalar())) { - rhs = false; - return true; + return false; } } - return false; + BAD_DECODE_EXCEPTION } } // namespace YAML