diff --git a/CMakeLists.txt b/CMakeLists.txt index c1eee6ad5..61591433f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ option(YAML_CPP_DISABLE_UNINSTALL "Disable uninstallation of yaml-cpp" OFF) option(YAML_USE_SYSTEM_GTEST "Use system googletest if found" OFF) option(YAML_ENABLE_PIC "Use Position-Independent Code " ON) option(YAML_CPP_USE_STRICT_FLAGS "Uses strict compilation flags e.g.: -Wall" ${YAML_CPP_MAIN_PROJECT}) +option(YAML_CPP_DISABLE_EXCEPTIONS "Disable use of exceptions see YAML::handle_exception(_local)." OFF) cmake_dependent_option(YAML_CPP_BUILD_TESTS "Enable yaml-cpp tests" OFF @@ -31,7 +32,7 @@ cmake_dependent_option(YAML_MSVC_SHARED_RT "CMAKE_SYSTEM_NAME MATCHES Windows" OFF) set(YAML_CPP_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/yaml-cpp" CACHE STRING "Path to install the CMake package to") - + if (YAML_CPP_FORMAT_SOURCE) find_program(YAML_CPP_CLANG_FORMAT_EXE NAMES clang-format) endif() @@ -123,6 +124,10 @@ if (NOT DEFINED CMAKE_DEBUG_POSTFIX) set(CMAKE_DEBUG_POSTFIX "d") endif() +if (YAML_CPP_DISABLE_EXCEPTIONS) + target_compile_definitions(yaml-cpp PUBLIC -DYAML_CPP_DISABLE_EXCEPTIONS) +endif() + set_target_properties(yaml-cpp PROPERTIES VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" diff --git a/docs/Tutorial.md b/docs/Tutorial.md index ec1c7cb31..f94558bea 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -224,7 +224,7 @@ public: node["a"] = a; return node; } - + int a; }; @@ -252,7 +252,7 @@ public: // Implementation of convert::{encode,decode} for all classes derived from or being A namespace YAML { - template + template struct convert::value>::type> { static Node encode(const T &rhs) { Node node = rhs.emit(); @@ -274,4 +274,26 @@ B b = node.as(); b.a = 12; b.b = 42; node = b; -``` \ No newline at end of file +``` + +# Handle Exceptions +Yaml-cpp uses a lot of exceptions. If this is not desired use the `handle_exception_local` and `handle_exception` handlers. +Both are function pointers expecting to be pointed to function that accepts a `const char*` as argument. +The `handle_exception_local` has to be registered for every thread (it is `thread_local`, while +`handle_exception` is truly global variable. +`handle_exception_local` is considered before calling `handle_exception`. These functions are expected to not return. +If non of the handlers is set a exception will be thrown. If a truly exception free version is desired set `YAML_CPP_DISABLE_EXCEPTIONS` to `OFF`. + +Usage example: +``` +void my_custom_exception_handler(const char* what) { + std::cout << "some exception occurred: " << what << "\n"; + std::terminate(); +} +... +int main() { + YAML::handle_exception = &my_custom_exception_handler; + // from now on handle_exception will be called instead of an exception + ... +} +``` diff --git a/include/yaml-cpp/depthguard.h b/include/yaml-cpp/depthguard.h index 8ca61ac6c..ca49acc8a 100644 --- a/include/yaml-cpp/depthguard.h +++ b/include/yaml-cpp/depthguard.h @@ -51,7 +51,7 @@ class DepthGuard final { DepthGuard(int & depth_, const Mark& mark_, const std::string& msg_) : m_depth(depth_) { ++m_depth; if ( max_depth <= m_depth ) { - throw DeepRecursion{m_depth, mark_, msg_}; + raise(m_depth, mark_, msg_); } } diff --git a/include/yaml-cpp/dll.h b/include/yaml-cpp/dll.h index 4e55ab8c2..1d1470df1 100644 --- a/include/yaml-cpp/dll.h +++ b/include/yaml-cpp/dll.h @@ -48,6 +48,14 @@ # endif #endif +#ifndef YAML_CPP_NORETURN +# ifdef _MSC_VER +# define YAML_CPP_NORETURN __declspec(noreturn) +# else +# define YAML_CPP_NORETURN __attribute__ ((noreturn)) +# endif +#endif + #ifndef YAML_CPP_DEPRECATED_EXPORT # define YAML_CPP_DEPRECATED_EXPORT YAML_CPP_API YAML_CPP_DEPRECATED #endif diff --git a/include/yaml-cpp/exceptions.h b/include/yaml-cpp/exceptions.h index 83a65d3b4..c5ae9982f 100644 --- a/include/yaml-cpp/exceptions.h +++ b/include/yaml-cpp/exceptions.h @@ -10,11 +10,50 @@ #include "yaml-cpp/mark.h" #include "yaml-cpp/noexcept.h" #include "yaml-cpp/traits.h" +#include #include #include #include namespace YAML { + +using ExceptionHandle = void(*)(const char*); +YAML_CPP_API void set_handle_exception_local(ExceptionHandle handle); +YAML_CPP_API void set_handle_exception(ExceptionHandle handle); +YAML_CPP_API ExceptionHandle get_handle_exception_local(); +YAML_CPP_API ExceptionHandle get_handle_exception(); + + +/** Function to trigger an exception state + * + * Instead of throwing an exception directly, it first tries + * to call handle_exception_local, if no handle is registered + * it will call handle_exception, if no handle is registered + * it will throw an exception. (Asumming YAML_CPP_USE_EXCEPTIONS is not turned off) + * + * - handle_exception_local is a 'thread_local' variable, allowing to register + * handlers when running multi threaded processes but wishing for a different + * handlers in each thread. Set to 'nullptr' to deactivate this handler. + * - handle_exception is a global handler which will be considered if + * 'handle_exception_local == nullptr'. Set to 'nullptr' to deactivate handler. + * + * Note: Handlers are expected to not return, if they return std::terminate() is being called. + */ +template +YAML_CPP_NORETURN void raise(Args&&... args) { + if (get_handle_exception_local()) { + get_handle_exception_local()(Ex(std::forward(args)...).what()); + } else if (get_handle_exception()) { + get_handle_exception()(Ex(std::forward(args)...).what()); + } +#if !defined(YAML_CPP_DISABLE_EXCEPTIONS) + else { + throw Ex(std::forward(args)...); + } +#endif + std::terminate(); // The handle_exception() call should have terminated +} + // error messages namespace ErrorMsg { const char* const YAML_DIRECTIVE_ARGS = diff --git a/include/yaml-cpp/node/detail/impl.h b/include/yaml-cpp/node/detail/impl.h index 6ba9d14e5..5df144bbf 100644 --- a/include/yaml-cpp/node/detail/impl.h +++ b/include/yaml-cpp/node/detail/impl.h @@ -128,7 +128,7 @@ inline node* node_data::get(const Key& key, return pNode; return nullptr; case NodeType::Scalar: - throw BadSubscript(m_mark, key); + raise(m_mark, key); } auto it = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) { @@ -154,7 +154,7 @@ inline node& node_data::get(const Key& key, shared_memory_holder pMemory) { convert_to_map(pMemory); break; case NodeType::Scalar: - throw BadSubscript(m_mark, key); + raise(m_mark, key); } auto it = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) { @@ -213,7 +213,7 @@ inline void node_data::force_insert(const Key& key, const Value& value, convert_to_map(pMemory); break; case NodeType::Scalar: - throw BadInsert(); + raise(); } node& k = convert_to_node(key, pMemory); diff --git a/include/yaml-cpp/node/impl.h b/include/yaml-cpp/node/impl.h index 4da522fa3..06d9436db 100644 --- a/include/yaml-cpp/node/impl.h +++ b/include/yaml-cpp/node/impl.h @@ -57,7 +57,7 @@ inline Node::~Node() = default; inline void Node::EnsureNodeExists() const { if (!m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); if (!m_pNode) { m_pMemory.reset(new detail::memory_holder); m_pNode = &m_pMemory->create_node(); @@ -80,14 +80,14 @@ inline bool Node::IsDefined() const { inline Mark Node::Mark() const { if (!m_isValid) { - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); } return m_pNode ? m_pNode->mark() : Mark::null_mark(); } inline NodeType::value Node::Type() const { if (!m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); return m_pNode ? m_pNode->type() : NodeType::Null; } @@ -131,12 +131,12 @@ struct as_if { T operator()() const { if (!node.m_pNode) // no fallback - throw InvalidNode(node.m_invalidKey); + raise(node.m_invalidKey); T t; if (convert::decode(node, t)) return t; - throw TypedBadConversion(node.Mark()); + raise >(node.Mark()); } }; @@ -147,11 +147,11 @@ struct as_if { std::string operator()() const { if (node.Type() == NodeType::Undefined) // no fallback - throw InvalidNode(node.m_invalidKey); + raise(node.m_invalidKey); if (node.Type() == NodeType::Null) return "null"; if (node.Type() != NodeType::Scalar) - throw TypedBadConversion(node.Mark()); + raise >(node.Mark()); return node.Scalar(); } }; @@ -160,7 +160,7 @@ struct as_if { template inline T Node::as() const { if (!m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); return as_if(*this)(); } @@ -173,21 +173,21 @@ inline T Node::as(const S& fallback) const { inline const std::string& Node::Scalar() const { if (!m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); return m_pNode ? m_pNode->scalar() : detail::node_data::empty_scalar(); } YAML_ATTRIBUTE_NO_SANITIZE_ADDRESS inline const std::string& Node::UninstrumentedScalarForTesting() const { if (m_isValid && m_pMemory != nullptr && m_pNode != nullptr) - throw InvalidNode("use-after-free"); + raise("use-after-free"); else - throw BadDereference(); + raise(); } inline const std::string& Node::Tag() const { if (!m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); return m_pNode ? m_pNode->tag() : detail::node_data::empty_scalar(); } @@ -198,7 +198,7 @@ inline void Node::SetTag(const std::string& tag) { inline EmitterStyle::value Node::Style() const { if (!m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); return m_pNode ? m_pNode->style() : EmitterStyle::Default; } @@ -210,7 +210,7 @@ inline void Node::SetStyle(EmitterStyle::value style) { // assignment inline bool Node::is(const Node& rhs) const { if (!m_isValid || !rhs.m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); if (!m_pNode || !rhs.m_pNode) return false; return m_pNode->is(*rhs.m_pNode); @@ -231,7 +231,7 @@ inline Node& Node::operator=(const Node& rhs) { inline void Node::reset(const YAML::Node& rhs) { if (!m_isValid || !rhs.m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); m_pMemory = rhs.m_pMemory; m_pNode = rhs.m_pNode; } @@ -239,7 +239,7 @@ inline void Node::reset(const YAML::Node& rhs) { template inline void Node::Assign(const T& rhs) { if (!m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); AssignData(convert::encode(rhs)); } @@ -269,7 +269,7 @@ inline void Node::AssignData(const Node& rhs) { inline void Node::AssignNode(const Node& rhs) { if (!m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); rhs.EnsureNodeExists(); if (!m_pNode) { @@ -286,7 +286,7 @@ inline void Node::AssignNode(const Node& rhs) { // size/iterator inline std::size_t Node::size() const { if (!m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); return m_pNode ? m_pNode->size() : 0; } @@ -335,7 +335,7 @@ inline reverse_iterator Node::rend() { template inline void Node::push_back(const T& rhs) { if (!m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); push_back(Node(rhs)); } @@ -413,7 +413,7 @@ inline void Node::force_insert(const Key& key, const Value& value) { template inline bool Node::contains(const Key& key) const { if (!m_isValid) - throw InvalidNode(m_invalidKey); + raise(m_invalidKey); if (!m_pNode) return false; return (static_cast(m_pNode))->get(key, m_pMemory) != nullptr; } diff --git a/src/exceptions.cpp b/src/exceptions.cpp index af99fd6b7..515080a69 100644 --- a/src/exceptions.cpp +++ b/src/exceptions.cpp @@ -3,6 +3,26 @@ namespace YAML { +namespace { + thread_local ExceptionHandle handle_exception_local = nullptr; + ExceptionHandle handle_exception = nullptr; +} +YAML_CPP_API void set_handle_exception_local(ExceptionHandle handle) { + handle_exception_local = handle; +} + +YAML_CPP_API void set_handle_exception(ExceptionHandle handle) { + handle_exception = handle; +} + +YAML_CPP_API ExceptionHandle get_handle_exception_local() { + return handle_exception_local; +} +YAML_CPP_API ExceptionHandle get_handle_exception() { + return handle_exception; +} + + // These destructors are defined out-of-line so the vtable is only emitted once. Exception::~Exception() YAML_CPP_NOEXCEPT = default; ParserException::~ParserException() YAML_CPP_NOEXCEPT = default; diff --git a/src/exp.cpp b/src/exp.cpp index 067b29e66..4f1a1e7b9 100644 --- a/src/exp.cpp +++ b/src/exp.cpp @@ -21,7 +21,7 @@ unsigned ParseHex(const std::string& str, const Mark& mark) { else if ('0' <= ch && ch <= '9') digit = ch - '0'; else - throw ParserException(mark, ErrorMsg::INVALID_HEX); + raise(mark, ErrorMsg::INVALID_HEX); value = (value << 4) + digit; } @@ -48,7 +48,7 @@ std::string Escape(Stream& in, int codeLength) { if ((value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF) { std::stringstream msg; msg << ErrorMsg::INVALID_UNICODE << value; - throw ParserException(in.mark(), msg.str()); + raise(in.mark(), msg.str()); } // now break it up into chars @@ -131,7 +131,7 @@ std::string Escape(Stream& in) { } std::stringstream msg; - throw ParserException(in.mark(), std::string(ErrorMsg::INVALID_ESCAPE) + ch); + raise(in.mark(), std::string(ErrorMsg::INVALID_ESCAPE) + ch); } } // namespace Exp } // namespace YAML diff --git a/src/node_data.cpp b/src/node_data.cpp index dbfb94191..40b802171 100644 --- a/src/node_data.cpp +++ b/src/node_data.cpp @@ -184,7 +184,7 @@ void node_data::push_back(node& node, } if (m_type != NodeType::Sequence) - throw BadPushback(); + raise(); m_sequence.push_back(&node); } @@ -200,7 +200,7 @@ void node_data::insert(node& key, node& value, convert_to_map(pMemory); break; case NodeType::Scalar: - throw BadSubscript(m_mark, key); + raise(m_mark, key); } insert_map_pair(key, value); @@ -231,7 +231,7 @@ node& node_data::get(node& key, const shared_memory_holder& pMemory) { convert_to_map(pMemory); break; case NodeType::Scalar: - throw BadSubscript(m_mark, key); + raise(m_mark, key); } for (const auto& it : m_map) { @@ -283,7 +283,7 @@ void node_data::insert_map_pair(node& key, node& value, bool force) { if (!force && !key.scalar().empty()) for (const auto& mapEntry : m_map) if (mapEntry.first->scalar() == key.scalar()) - throw NonUniqueMapKey(m_mark, key); + raise(m_mark, key); m_map.emplace_back(&key, &value); diff --git a/src/parse.cpp b/src/parse.cpp index 262536b85..01b70d096 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -32,7 +32,7 @@ Node Load(std::istream& input) { Node LoadFile(const std::string& filename) { std::ifstream fin(filename); if (!fin) { - throw BadFile(filename); + raise(filename); } return Load(fin); } @@ -65,7 +65,7 @@ std::vector LoadAll(std::istream& input) { std::vector LoadAllFromFile(const std::string& filename) { std::ifstream fin(filename); if (!fin) { - throw BadFile(filename); + raise(filename); } return LoadAll(fin); } diff --git a/src/parser.cpp b/src/parser.cpp index 761ffd52e..244776385 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -84,11 +84,11 @@ void Parser::HandleDirective(const Token& token) { void Parser::HandleYamlDirective(const Token& token) { if (token.params.size() != 1) { - throw ParserException(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS); + raise(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS); } if (!m_pDirectives->version.isDefault) { - throw ParserException(token.mark, ErrorMsg::REPEATED_YAML_DIRECTIVE); + raise(token.mark, ErrorMsg::REPEATED_YAML_DIRECTIVE); } std::stringstream str(token.params[0]); @@ -97,12 +97,12 @@ void Parser::HandleYamlDirective(const Token& token) { str.get(); str >> m_pDirectives->version.minor; if (!str || str.peek() != EOF) { - throw ParserException( + raise( token.mark, std::string(ErrorMsg::YAML_VERSION) + token.params[0]); } if (m_pDirectives->version.major > 1) { - throw ParserException(token.mark, ErrorMsg::YAML_MAJOR_VERSION); + raise(token.mark, ErrorMsg::YAML_MAJOR_VERSION); } m_pDirectives->version.isDefault = false; @@ -111,12 +111,12 @@ void Parser::HandleYamlDirective(const Token& token) { void Parser::HandleTagDirective(const Token& token) { if (token.params.size() != 2) - throw ParserException(token.mark, ErrorMsg::TAG_DIRECTIVE_ARGS); + raise(token.mark, ErrorMsg::TAG_DIRECTIVE_ARGS); const std::string& handle = token.params[0]; const std::string& prefix = token.params[1]; if (m_pDirectives->tags.find(handle) != m_pDirectives->tags.end()) { - throw ParserException(token.mark, ErrorMsg::REPEATED_TAG_DIRECTIVE); + raise(token.mark, ErrorMsg::REPEATED_TAG_DIRECTIVE); } m_pDirectives->tags[handle] = prefix; diff --git a/src/scanner.cpp b/src/scanner.cpp index f71e72964..dd70b27d3 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -144,13 +144,13 @@ void Scanner::ScanNextToken() { // values starting with `,` are not allowed. // eg: reject `,foo` if (INPUT.column() == 0) { - throw ParserException(INPUT.mark(), ErrorMsg::UNEXPECTED_FLOW); + raise(INPUT.mark(), ErrorMsg::UNEXPECTED_FLOW); } // if we already parsed a quoted scalar value and we are not in a flow, // then `,` is not a valid character. // eg: reject `"foo",` if (!m_scalarValueAllowed) { - throw ParserException(INPUT.mark(), ErrorMsg::UNEXPECTED_SCALAR); + raise(INPUT.mark(), ErrorMsg::UNEXPECTED_SCALAR); } return ScanFlowEntry(); } @@ -207,7 +207,7 @@ void Scanner::ScanNextToken() { // another scalar value is an error. // eg: reject `"foo" "bar"` if (!m_scalarValueAllowed) { - throw ParserException(INPUT.mark(), ErrorMsg::UNEXPECTED_SCALAR); + raise(INPUT.mark(), ErrorMsg::UNEXPECTED_SCALAR); } if (INPUT.peek() == '\'' || INPUT.peek() == '\"') { @@ -221,7 +221,7 @@ void Scanner::ScanNextToken() { } // don't know what it is! - throw ParserException(INPUT.mark(), ErrorMsg::UNKNOWN_TOKEN); + raise(INPUT.mark(), ErrorMsg::UNKNOWN_TOKEN); } void Scanner::ScanToNextToken() { @@ -315,7 +315,7 @@ Token::TYPE Scanner::GetStartTokenFor(IndentMarker::INDENT_TYPE type) { break; } assert(false); - throw std::runtime_error("yaml-cpp: internal error, invalid indent type"); + raise("yaml-cpp: internal error, invalid indent type"); } Scanner::IndentMarker* Scanner::PushIndentTo(int column, @@ -424,6 +424,6 @@ void Scanner::ThrowParserException(const std::string& msg) const { const Token& token = m_tokens.front(); mark = token.mark; } - throw ParserException(mark, msg); + raise(mark, msg); } } // namespace YAML diff --git a/src/scanscalar.cpp b/src/scanscalar.cpp index 7fa642d3c..403e6306e 100644 --- a/src/scanscalar.cpp +++ b/src/scanscalar.cpp @@ -49,7 +49,7 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { break; } if (params.onDocIndicator == THROW) { - throw ParserException(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR); + raise(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR); } } @@ -85,7 +85,7 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { // eof? if we're looking to eat something, then we throw if (!INPUT) { if (params.eatEnd) { - throw ParserException(INPUT.mark(), ErrorMsg::EOF_IN_SCALAR); + raise(INPUT.mark(), ErrorMsg::EOF_IN_SCALAR); } break; } @@ -135,7 +135,7 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { // we check for tabs that masquerade as indentation if (INPUT.peek() == '\t' && INPUT.column() < params.indent && params.onTabInIndentation == THROW) { - throw ParserException(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION); + raise(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION); } if (!params.eatLeadingWhitespace) { diff --git a/src/scantag.cpp b/src/scantag.cpp index 63bc1e537..16a161966 100644 --- a/src/scantag.cpp +++ b/src/scantag.cpp @@ -26,7 +26,7 @@ std::string ScanVerbatimTag(Stream& INPUT) { tag += INPUT.get(n); } - throw ParserException(INPUT.mark(), ErrorMsg::END_OF_VERBATIM_TAG); + raise(INPUT.mark(), ErrorMsg::END_OF_VERBATIM_TAG); } std::string ScanTagHandle(Stream& INPUT, bool& canBeHandle) { @@ -37,7 +37,7 @@ std::string ScanTagHandle(Stream& INPUT, bool& canBeHandle) { while (INPUT) { if (INPUT.peek() == Keys::Tag) { if (!canBeHandle) - throw ParserException(firstNonWordChar, ErrorMsg::CHAR_IN_TAG_HANDLE); + raise(firstNonWordChar, ErrorMsg::CHAR_IN_TAG_HANDLE); break; } @@ -74,7 +74,7 @@ std::string ScanTagSuffix(Stream& INPUT) { } if (tag.empty()) - throw ParserException(INPUT.mark(), ErrorMsg::TAG_WITH_NO_SUFFIX); + raise(INPUT.mark(), ErrorMsg::TAG_WITH_NO_SUFFIX); return tag; } diff --git a/src/scantoken.cpp b/src/scantoken.cpp index b13d21133..4bebeb7ba 100644 --- a/src/scantoken.cpp +++ b/src/scantoken.cpp @@ -103,7 +103,7 @@ void Scanner::ScanFlowStart() { // FlowEnd void Scanner::ScanFlowEnd() { if (InBlockContext()) - throw ParserException(INPUT.mark(), ErrorMsg::FLOW_END); + raise(INPUT.mark(), ErrorMsg::FLOW_END); // we might have a solo entry in the flow context if (InFlowContext()) { @@ -123,7 +123,7 @@ void Scanner::ScanFlowEnd() { // check that it matches the start FLOW_MARKER flowType = (ch == Keys::FlowSeqEnd ? FLOW_SEQ : FLOW_MAP); if (m_flows.top() != flowType) - throw ParserException(mark, ErrorMsg::FLOW_END); + raise(mark, ErrorMsg::FLOW_END); m_flows.pop(); Token::TYPE type = (flowType ? Token::FLOW_SEQ_END : Token::FLOW_MAP_END); @@ -153,11 +153,11 @@ void Scanner::ScanFlowEntry() { void Scanner::ScanBlockEntry() { // we better be in the block context! if (InFlowContext()) - throw ParserException(INPUT.mark(), ErrorMsg::BLOCK_ENTRY); + raise(INPUT.mark(), ErrorMsg::BLOCK_ENTRY); // can we put it here? if (!m_simpleKeyAllowed) - throw ParserException(INPUT.mark(), ErrorMsg::BLOCK_ENTRY); + raise(INPUT.mark(), ErrorMsg::BLOCK_ENTRY); PushIndentTo(INPUT.column(), IndentMarker::SEQ); m_simpleKeyAllowed = true; @@ -174,7 +174,7 @@ void Scanner::ScanKey() { // handle keys differently in the block context (and manage indents) if (InBlockContext()) { if (!m_simpleKeyAllowed) - throw ParserException(INPUT.mark(), ErrorMsg::MAP_KEY); + raise(INPUT.mark(), ErrorMsg::MAP_KEY); PushIndentTo(INPUT.column(), IndentMarker::MAP); } @@ -202,7 +202,7 @@ void Scanner::ScanValue() { // handle values differently in the block context (and manage indents) if (InBlockContext()) { if (!m_simpleKeyAllowed) - throw ParserException(INPUT.mark(), ErrorMsg::MAP_VALUE); + raise(INPUT.mark(), ErrorMsg::MAP_VALUE); PushIndentTo(INPUT.column(), IndentMarker::MAP); } @@ -241,12 +241,12 @@ void Scanner::ScanAnchorOrAlias() { // we need to have read SOMETHING! if (name.empty()) - throw ParserException(INPUT.mark(), alias ? ErrorMsg::ALIAS_NOT_FOUND + raise(INPUT.mark(), alias ? ErrorMsg::ALIAS_NOT_FOUND : ErrorMsg::ANCHOR_NOT_FOUND); // and needs to end correctly if (INPUT && !Exp::AnchorEnd().Matches(INPUT)) - throw ParserException(INPUT.mark(), alias ? ErrorMsg::CHAR_IN_ALIAS + raise(INPUT.mark(), alias ? ErrorMsg::CHAR_IN_ALIAS : ErrorMsg::CHAR_IN_ANCHOR); // and we're done @@ -323,7 +323,7 @@ void Scanner::ScanPlainScalar() { // finally, check and see if we ended on an illegal character // if(Exp::IllegalCharInScalar.Matches(INPUT)) - // throw ParserException(INPUT.mark(), ErrorMsg::CHAR_IN_SCALAR); + // raise(INPUT.mark(), ErrorMsg::CHAR_IN_SCALAR); Token token(Token::PLAIN_SCALAR, mark); token.value = scalar; @@ -402,7 +402,7 @@ void Scanner::ScanBlockScalar() { params.chomp = STRIP; else if (Exp::Digit().Matches(ch)) { if (ch == '0') - throw ParserException(INPUT.mark(), ErrorMsg::ZERO_INDENT_IN_BLOCK); + raise(INPUT.mark(), ErrorMsg::ZERO_INDENT_IN_BLOCK); params.indent = ch - '0'; params.detectIndent = false; @@ -420,7 +420,7 @@ void Scanner::ScanBlockScalar() { // if it's not a line break, then we ran into a bad character inline if (INPUT && !Exp::Break().Matches(INPUT)) - throw ParserException(INPUT.mark(), ErrorMsg::CHAR_IN_BLOCK); + raise(INPUT.mark(), ErrorMsg::CHAR_IN_BLOCK); // set the initial indentation if (GetTopIndent() >= 0) diff --git a/src/singledocparser.cpp b/src/singledocparser.cpp index ca80bea2c..0a596f841 100644 --- a/src/singledocparser.cpp +++ b/src/singledocparser.cpp @@ -44,7 +44,7 @@ void SingleDocParser::HandleDocument(EventHandler& eventHandler) { // check if any tokens left after the text if (!m_scanner.empty() && m_scanner.peek().type != Token::DOC_END && m_scanner.peek().type != Token::DOC_START) - throw ParserException(m_scanner.mark(), ErrorMsg::UNEXPECTED_TOKEN_AFTER_DOC); + raise(m_scanner.mark(), ErrorMsg::UNEXPECTED_TOKEN_AFTER_DOC); // and finally eat any doc ends we see if (!m_scanner.empty() && m_scanner.peek().type == Token::DOC_END) @@ -173,11 +173,11 @@ void SingleDocParser::HandleBlockSequence(EventHandler& eventHandler) { while (true) { if (m_scanner.empty()) - throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ); + raise(m_scanner.mark(), ErrorMsg::END_OF_SEQ); Token token = m_scanner.peek(); if (token.type != Token::BLOCK_ENTRY && token.type != Token::BLOCK_SEQ_END) - throw ParserException(token.mark, ErrorMsg::END_OF_SEQ); + raise(token.mark, ErrorMsg::END_OF_SEQ); m_scanner.pop(); if (token.type == Token::BLOCK_SEQ_END) @@ -206,7 +206,7 @@ void SingleDocParser::HandleFlowSequence(EventHandler& eventHandler) { while (true) { if (m_scanner.empty()) - throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ_FLOW); + raise(m_scanner.mark(), ErrorMsg::END_OF_SEQ_FLOW); // first check for end if (m_scanner.peek().type == Token::FLOW_SEQ_END) { @@ -218,7 +218,7 @@ void SingleDocParser::HandleFlowSequence(EventHandler& eventHandler) { HandleNode(eventHandler); if (m_scanner.empty()) - throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ_FLOW); + raise(m_scanner.mark(), ErrorMsg::END_OF_SEQ_FLOW); // now eat the separator (or could be a sequence end, which we ignore - but // if it's neither, then it's a bad node) @@ -226,7 +226,7 @@ void SingleDocParser::HandleFlowSequence(EventHandler& eventHandler) { if (token.type == Token::FLOW_ENTRY) m_scanner.pop(); else if (token.type != Token::FLOW_SEQ_END) - throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW); + raise(token.mark, ErrorMsg::END_OF_SEQ_FLOW); } m_pCollectionStack->PopCollectionType(CollectionType::FlowSeq); @@ -259,12 +259,12 @@ void SingleDocParser::HandleBlockMap(EventHandler& eventHandler) { while (true) { if (m_scanner.empty()) - throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP); + raise(m_scanner.mark(), ErrorMsg::END_OF_MAP); Token token = m_scanner.peek(); if (token.type != Token::KEY && token.type != Token::VALUE && token.type != Token::BLOCK_MAP_END) - throw ParserException(token.mark, ErrorMsg::END_OF_MAP); + raise(token.mark, ErrorMsg::END_OF_MAP); if (token.type == Token::BLOCK_MAP_END) { m_scanner.pop(); @@ -298,7 +298,7 @@ void SingleDocParser::HandleFlowMap(EventHandler& eventHandler) { while (true) { if (m_scanner.empty()) - throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW); + raise(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW); Token& token = m_scanner.peek(); const Mark mark = token.mark; @@ -325,7 +325,7 @@ void SingleDocParser::HandleFlowMap(EventHandler& eventHandler) { } if (m_scanner.empty()) - throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW); + raise(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW); // now eat the separator (or could be a map end, which we ignore - but if // it's neither, then it's a bad node) @@ -333,7 +333,7 @@ void SingleDocParser::HandleFlowMap(EventHandler& eventHandler) { if (nextToken.type == Token::FLOW_ENTRY) m_scanner.pop(); else if (nextToken.type != Token::FLOW_MAP_END) - throw ParserException(nextToken.mark, ErrorMsg::END_OF_MAP_FLOW); + raise(nextToken.mark, ErrorMsg::END_OF_MAP_FLOW); } m_pCollectionStack->PopCollectionType(CollectionType::FlowMap); @@ -401,7 +401,7 @@ void SingleDocParser::ParseProperties(std::string& tag, anchor_t& anchor, void SingleDocParser::ParseTag(std::string& tag) { Token& token = m_scanner.peek(); if (!tag.empty()) - throw ParserException(token.mark, ErrorMsg::MULTIPLE_TAGS); + raise(token.mark, ErrorMsg::MULTIPLE_TAGS); Tag tagInfo(token); tag = tagInfo.Translate(m_directives); @@ -411,7 +411,7 @@ void SingleDocParser::ParseTag(std::string& tag) { void SingleDocParser::ParseAnchor(anchor_t& anchor, std::string& anchor_name) { Token& token = m_scanner.peek(); if (anchor) - throw ParserException(token.mark, ErrorMsg::MULTIPLE_ANCHORS); + raise(token.mark, ErrorMsg::MULTIPLE_ANCHORS); anchor_name = token.value; anchor = RegisterAnchor(token.value); @@ -431,7 +431,7 @@ anchor_t SingleDocParser::LookupAnchor(const Mark& mark, if (it == m_anchors.end()) { std::stringstream ss; ss << ErrorMsg::UNKNOWN_ANCHOR << name; - throw ParserException(mark, ss.str()); + raise(mark, ss.str()); } return it->second; diff --git a/src/tag.cpp b/src/tag.cpp index 9d36317de..b39c19737 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -4,6 +4,7 @@ #include "directives.h" // IWYU pragma: keep #include "tag.h" #include "token.h" +#include "yaml-cpp/exceptions.h" namespace YAML { Tag::Tag(const Token& token) @@ -45,6 +46,6 @@ std::string Tag::Translate(const Directives& directives) const { default: assert(false); } - throw std::runtime_error("yaml-cpp: internal error, bad tag type"); + raise("yaml-cpp: internal error, bad tag type"); } } // namespace YAML diff --git a/test/node/node_test.cpp b/test/node/node_test.cpp index 75ac3db00..d43034c78 100644 --- a/test/node/node_test.cpp +++ b/test/node/node_test.cpp @@ -911,6 +911,30 @@ TEST(NodeTest, CreateMapWithFloatingPoint0Key) { EXPECT_TRUE(node.IsMap()); } +TEST(NodeTest, CallExceptionHandler) { + Node node; + node["foo"] = "value"; + EXPECT_TRUE(!node["bar"]); + + // Check if global exception handler was called + YAML::set_handle_exception([](const char* what) { + throw std::runtime_error("error"); + }); + EXPECT_THROW(node["bar"].as(), std::runtime_error); + + // Check if local exception handler was called + // and if it takes presedence over global handler + YAML::set_handle_exception_local([](const char* what) { + throw std::domain_error("error"); + }); + EXPECT_THROW(node["bar"].as(), std::domain_error); + + // Check if exception was thrown + YAML::set_handle_exception(nullptr); + YAML::set_handle_exception_local(nullptr); + EXPECT_THROW(node["bar"].as(), InvalidNode); +} + class NodeEmitterTest : public ::testing::Test { protected: void ExpectOutput(const std::string& output, const Node& node) {