From 44109d9f6484dad4a02ba5d924756036a013f8f9 Mon Sep 17 00:00:00 2001 From: Karl Twesten Date: Fri, 21 Oct 2022 16:32:00 +0200 Subject: [PATCH 1/3] Support for building with exceptions disabled 1) A new macro YAML_CPP_NORETURN to annotate functions as not returning in dll.h 2) A new function YAML_throw(args...) in exception.h this function will throw an exception unless exceptions are disabled in the compiler, detected by checking the pre-defined macro __cpp_exceptions In this case the exception class will be instantiated, and the user-provided function YAML::handle_exception(const char*) will be called on the exception's what() method 3) if exceptions are disabled,and the library's user does not provide YAML::handle_exception, there will be a linker error 4) all other files have been changed automatedly by running the following sed commands sed -i "s/throw \([A-Za-z]*\)(\(.*\))/YAML_throw<\1>(\2)/g" # throw statements for non-templated exceptions sed -i "s/throw \(.*\)<\(.*\)>(/YAML_throw<\1<\2> >(/g" # throw statements for templated exceptions --- include/yaml-cpp/depthguard.h | 2 +- include/yaml-cpp/dll.h | 8 ++++++ include/yaml-cpp/exceptions.h | 15 +++++++++++ include/yaml-cpp/node/detail/impl.h | 6 ++--- include/yaml-cpp/node/impl.h | 40 ++++++++++++++--------------- src/exp.cpp | 6 ++--- src/node_data.cpp | 8 +++--- src/parse.cpp | 4 +-- src/parser.cpp | 12 ++++----- src/scanner.cpp | 12 ++++----- src/scanscalar.cpp | 6 ++--- src/scantag.cpp | 6 ++--- src/scantoken.cpp | 22 ++++++++-------- src/singledocparser.cpp | 28 ++++++++++---------- src/tag.cpp | 3 ++- 15 files changed, 101 insertions(+), 77 deletions(-) diff --git a/include/yaml-cpp/depthguard.h b/include/yaml-cpp/depthguard.h index 8ca61ac6c..4b102f053 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_}; + YAML_throw(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..0b6ed930d 100644 --- a/include/yaml-cpp/exceptions.h +++ b/include/yaml-cpp/exceptions.h @@ -15,6 +15,21 @@ #include namespace YAML { + +#if defined(__cpp_exceptions) || (defined(_MSC_VER) && defined(_CPPUNWIND)) +template +YAML_CPP_NORETURN void YAML_throw(Args&&... args) { + throw Ex(std::forward(args)...); +} +#else +YAML_CPP_NORETURN void handle_exception(const char* what); + +template +YAML_CPP_NORETURN void YAML_throw(Args&&... args) { + handle_exception(Ex(std::forward(args)...).what()); +} +#endif + // 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..8585d536d 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); + YAML_throw(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); + YAML_throw(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(); + YAML_throw(); } 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..21a50c15f 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); + YAML_throw(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); + YAML_throw(m_invalidKey); } return m_pNode ? m_pNode->mark() : Mark::null_mark(); } inline NodeType::value Node::Type() const { if (!m_isValid) - throw InvalidNode(m_invalidKey); + YAML_throw(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); + YAML_throw(node.m_invalidKey); T t; if (convert::decode(node, t)) return t; - throw TypedBadConversion(node.Mark()); + YAML_throw >(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); + YAML_throw(node.m_invalidKey); if (node.Type() == NodeType::Null) return "null"; if (node.Type() != NodeType::Scalar) - throw TypedBadConversion(node.Mark()); + YAML_throw >(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); + YAML_throw(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); + YAML_throw(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"); + YAML_throw("use-after-free"); else - throw BadDereference(); + YAML_throw(); } inline const std::string& Node::Tag() const { if (!m_isValid) - throw InvalidNode(m_invalidKey); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(m_invalidKey); if (!m_pNode) return false; return (static_cast(m_pNode))->get(key, m_pMemory) != nullptr; } diff --git a/src/exp.cpp b/src/exp.cpp index 067b29e66..bdcfa78de 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); + YAML_throw(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()); + YAML_throw(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); + YAML_throw(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..c75403505 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(); + YAML_throw(); 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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(m_mark, key); m_map.emplace_back(&key, &value); diff --git a/src/parse.cpp b/src/parse.cpp index 262536b85..286762210 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); + YAML_throw(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); + YAML_throw(filename); } return LoadAll(fin); } diff --git a/src/parser.cpp b/src/parser.cpp index 761ffd52e..584429df3 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); + YAML_throw(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS); } if (!m_pDirectives->version.isDefault) { - throw ParserException(token.mark, ErrorMsg::REPEATED_YAML_DIRECTIVE); + YAML_throw(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( + YAML_throw( token.mark, std::string(ErrorMsg::YAML_VERSION) + token.params[0]); } if (m_pDirectives->version.major > 1) { - throw ParserException(token.mark, ErrorMsg::YAML_MAJOR_VERSION); + YAML_throw(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); + YAML_throw(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); + YAML_throw(token.mark, ErrorMsg::REPEATED_TAG_DIRECTIVE); } m_pDirectives->tags[handle] = prefix; diff --git a/src/scanner.cpp b/src/scanner.cpp index f71e72964..06fd9e2ba 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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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"); + YAML_throw("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); + YAML_throw(mark, msg); } } // namespace YAML diff --git a/src/scanscalar.cpp b/src/scanscalar.cpp index 7fa642d3c..62c10d673 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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION); } if (!params.eatLeadingWhitespace) { diff --git a/src/scantag.cpp b/src/scantag.cpp index 63bc1e537..f071065e1 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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(INPUT.mark(), ErrorMsg::TAG_WITH_NO_SUFFIX); return tag; } diff --git a/src/scantoken.cpp b/src/scantoken.cpp index b13d21133..fdcb46c40 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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(INPUT.mark(), ErrorMsg::BLOCK_ENTRY); // can we put it here? if (!m_simpleKeyAllowed) - throw ParserException(INPUT.mark(), ErrorMsg::BLOCK_ENTRY); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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 + YAML_throw(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 + YAML_throw(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); + // YAML_throw(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); + YAML_throw(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); + YAML_throw(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..7a85219cf 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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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); + YAML_throw(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()); + YAML_throw(mark, ss.str()); } return it->second; diff --git a/src/tag.cpp b/src/tag.cpp index 9d36317de..caba1ade6 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"); + YAML_throw("yaml-cpp: internal error, bad tag type"); } } // namespace YAML From 5a49414e1585948007b471bc79a2356beef0f980 Mon Sep 17 00:00:00 2001 From: Simon Gene Gottlieb Date: Wed, 12 Aug 2026 07:15:22 +0200 Subject: [PATCH 2/3] patch: rename YAML_throw to raise - renames the YAML_throw function to raise function - changes handler from linker decision to runtime variables - add a second handle_exception_local handler, allowing to handle errors in threads locally - handle exception are always called if available, throwing an exception is the default fallback case - add 'YAML_CPP_USE_EXCEPTIONS' to deactivate throwing exceptions. - add documentation --- CMakeLists.txt | 7 ++++- docs/Tutorial.md | 28 +++++++++++++++++--- include/yaml-cpp/depthguard.h | 2 +- include/yaml-cpp/exceptions.h | 41 ++++++++++++++++++++++------- include/yaml-cpp/node/detail/impl.h | 6 ++--- include/yaml-cpp/node/impl.h | 40 ++++++++++++++-------------- src/exceptions.cpp | 3 +++ src/exp.cpp | 6 ++--- src/node_data.cpp | 8 +++--- src/parse.cpp | 4 +-- src/parser.cpp | 12 ++++----- src/scanner.cpp | 12 ++++----- src/scanscalar.cpp | 6 ++--- src/scantag.cpp | 6 ++--- src/scantoken.cpp | 22 ++++++++-------- src/singledocparser.cpp | 28 ++++++++++---------- src/tag.cpp | 2 +- test/node/node_test.cpp | 24 +++++++++++++++++ 18 files changed, 166 insertions(+), 91 deletions(-) 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 4b102f053..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 ) { - YAML_throw(m_depth, mark_, msg_); + raise(m_depth, mark_, msg_); } } diff --git a/include/yaml-cpp/exceptions.h b/include/yaml-cpp/exceptions.h index 0b6ed930d..4e8371cec 100644 --- a/include/yaml-cpp/exceptions.h +++ b/include/yaml-cpp/exceptions.h @@ -10,25 +10,46 @@ #include "yaml-cpp/mark.h" #include "yaml-cpp/noexcept.h" #include "yaml-cpp/traits.h" +#include #include #include #include namespace YAML { -#if defined(__cpp_exceptions) || (defined(_MSC_VER) && defined(_CPPUNWIND)) -template -YAML_CPP_NORETURN void YAML_throw(Args&&... args) { - throw Ex(std::forward(args)...); -} -#else -YAML_CPP_NORETURN void handle_exception(const char* what); +YAML_CPP_API extern thread_local void(*handle_exception_local)(const char* what); +YAML_CPP_API extern void(*handle_exception)(const char* what); + +/** 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 YAML_throw(Args&&... args) { - handle_exception(Ex(std::forward(args)...).what()); -} +YAML_CPP_NORETURN void raise(Args&&... args) { + if (handle_exception_local) { + handle_exception_local(Ex(std::forward(args)...).what()); + } else if (handle_exception) { + 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 { diff --git a/include/yaml-cpp/node/detail/impl.h b/include/yaml-cpp/node/detail/impl.h index 8585d536d..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: - YAML_throw(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: - YAML_throw(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: - YAML_throw(); + 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 21a50c15f..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) - YAML_throw(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) { - YAML_throw(m_invalidKey); + raise(m_invalidKey); } return m_pNode ? m_pNode->mark() : Mark::null_mark(); } inline NodeType::value Node::Type() const { if (!m_isValid) - YAML_throw(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 - YAML_throw(node.m_invalidKey); + raise(node.m_invalidKey); T t; if (convert::decode(node, t)) return t; - YAML_throw >(node.Mark()); + raise >(node.Mark()); } }; @@ -147,11 +147,11 @@ struct as_if { std::string operator()() const { if (node.Type() == NodeType::Undefined) // no fallback - YAML_throw(node.m_invalidKey); + raise(node.m_invalidKey); if (node.Type() == NodeType::Null) return "null"; if (node.Type() != NodeType::Scalar) - YAML_throw >(node.Mark()); + raise >(node.Mark()); return node.Scalar(); } }; @@ -160,7 +160,7 @@ struct as_if { template inline T Node::as() const { if (!m_isValid) - YAML_throw(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) - YAML_throw(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) - YAML_throw("use-after-free"); + raise("use-after-free"); else - YAML_throw(); + raise(); } inline const std::string& Node::Tag() const { if (!m_isValid) - YAML_throw(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) - YAML_throw(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) - YAML_throw(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) - YAML_throw(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) - YAML_throw(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) - YAML_throw(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) - YAML_throw(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) - YAML_throw(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) - YAML_throw(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..0fc4e8a68 100644 --- a/src/exceptions.cpp +++ b/src/exceptions.cpp @@ -3,6 +3,9 @@ namespace YAML { +YAML_CPP_API thread_local void(*handle_exception_local)(const char* what) = nullptr; +YAML_CPP_API void(*handle_exception)(const char* what) = nullptr; + // 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 bdcfa78de..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 - YAML_throw(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; - YAML_throw(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; - YAML_throw(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 c75403505..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) - YAML_throw(); + 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: - YAML_throw(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: - YAML_throw(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()) - YAML_throw(m_mark, key); + raise(m_mark, key); m_map.emplace_back(&key, &value); diff --git a/src/parse.cpp b/src/parse.cpp index 286762210..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) { - YAML_throw(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) { - YAML_throw(filename); + raise(filename); } return LoadAll(fin); } diff --git a/src/parser.cpp b/src/parser.cpp index 584429df3..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) { - YAML_throw(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS); + raise(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS); } if (!m_pDirectives->version.isDefault) { - YAML_throw(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) { - YAML_throw( + raise( token.mark, std::string(ErrorMsg::YAML_VERSION) + token.params[0]); } if (m_pDirectives->version.major > 1) { - YAML_throw(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) - YAML_throw(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()) { - YAML_throw(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 06fd9e2ba..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) { - YAML_throw(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) { - YAML_throw(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) { - YAML_throw(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! - YAML_throw(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); - YAML_throw("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; } - YAML_throw(mark, msg); + raise(mark, msg); } } // namespace YAML diff --git a/src/scanscalar.cpp b/src/scanscalar.cpp index 62c10d673..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) { - YAML_throw(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) { - YAML_throw(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) { - YAML_throw(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 f071065e1..16a161966 100644 --- a/src/scantag.cpp +++ b/src/scantag.cpp @@ -26,7 +26,7 @@ std::string ScanVerbatimTag(Stream& INPUT) { tag += INPUT.get(n); } - YAML_throw(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) - YAML_throw(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()) - YAML_throw(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 fdcb46c40..4bebeb7ba 100644 --- a/src/scantoken.cpp +++ b/src/scantoken.cpp @@ -103,7 +103,7 @@ void Scanner::ScanFlowStart() { // FlowEnd void Scanner::ScanFlowEnd() { if (InBlockContext()) - YAML_throw(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) - YAML_throw(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()) - YAML_throw(INPUT.mark(), ErrorMsg::BLOCK_ENTRY); + raise(INPUT.mark(), ErrorMsg::BLOCK_ENTRY); // can we put it here? if (!m_simpleKeyAllowed) - YAML_throw(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) - YAML_throw(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) - YAML_throw(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()) - YAML_throw(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)) - YAML_throw(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)) - // YAML_throw(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') - YAML_throw(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)) - YAML_throw(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 7a85219cf..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) - YAML_throw(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()) - YAML_throw(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) - YAML_throw(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()) - YAML_throw(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()) - YAML_throw(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) - YAML_throw(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()) - YAML_throw(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) - YAML_throw(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()) - YAML_throw(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()) - YAML_throw(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) - YAML_throw(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()) - YAML_throw(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) - YAML_throw(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; - YAML_throw(mark, ss.str()); + raise(mark, ss.str()); } return it->second; diff --git a/src/tag.cpp b/src/tag.cpp index caba1ade6..b39c19737 100644 --- a/src/tag.cpp +++ b/src/tag.cpp @@ -46,6 +46,6 @@ std::string Tag::Translate(const Directives& directives) const { default: assert(false); } - YAML_throw("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..3931910bd 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::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::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::handle_exception = nullptr; + YAML::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) { From 7e9cd48a28debcfd35e845dc3eae1e1f101e4868 Mon Sep 17 00:00:00 2001 From: Simon Gene Gottlieb Date: Wed, 12 Aug 2026 08:50:35 +0200 Subject: [PATCH 3/3] windows does not support thread_local for dll? --- include/yaml-cpp/exceptions.h | 15 +++++++++------ src/exceptions.cpp | 21 +++++++++++++++++++-- test/node/node_test.cpp | 12 ++++++------ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/include/yaml-cpp/exceptions.h b/include/yaml-cpp/exceptions.h index 4e8371cec..c5ae9982f 100644 --- a/include/yaml-cpp/exceptions.h +++ b/include/yaml-cpp/exceptions.h @@ -17,9 +17,12 @@ 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(); -YAML_CPP_API extern thread_local void(*handle_exception_local)(const char* what); -YAML_CPP_API extern void(*handle_exception)(const char* what); /** Function to trigger an exception state * @@ -38,10 +41,10 @@ YAML_CPP_API extern void(*handle_exception)(const char* what); */ template YAML_CPP_NORETURN void raise(Args&&... args) { - if (handle_exception_local) { - handle_exception_local(Ex(std::forward(args)...).what()); - } else if (handle_exception) { - handle_exception(Ex(std::forward(args)...).what()); + 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 { diff --git a/src/exceptions.cpp b/src/exceptions.cpp index 0fc4e8a68..515080a69 100644 --- a/src/exceptions.cpp +++ b/src/exceptions.cpp @@ -3,8 +3,25 @@ namespace YAML { -YAML_CPP_API thread_local void(*handle_exception_local)(const char* what) = nullptr; -YAML_CPP_API void(*handle_exception)(const char* what) = nullptr; +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; diff --git a/test/node/node_test.cpp b/test/node/node_test.cpp index 3931910bd..d43034c78 100644 --- a/test/node/node_test.cpp +++ b/test/node/node_test.cpp @@ -917,21 +917,21 @@ TEST(NodeTest, CallExceptionHandler) { EXPECT_TRUE(!node["bar"]); // Check if global exception handler was called - YAML::handle_exception = [](const char* what) { + 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::handle_exception_local = [](const char* what) { + 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::handle_exception = nullptr; - YAML::handle_exception_local = nullptr; + YAML::set_handle_exception(nullptr); + YAML::set_handle_exception_local(nullptr); EXPECT_THROW(node["bar"].as(), InvalidNode); }