Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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}"
Expand Down
28 changes: 25 additions & 3 deletions docs/Tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ public:
node["a"] = a;
return node;
}

int a;
};

Expand Down Expand Up @@ -252,7 +252,7 @@ public:

// Implementation of convert::{encode,decode} for all classes derived from or being A
namespace YAML {
template<typename T>
template<typename T>
struct convert<T, typename std::enable_if<std::is_base_of<A, T>::value>::type> {
static Node encode(const T &rhs) {
Node node = rhs.emit();
Expand All @@ -274,4 +274,26 @@ B b = node.as<B>();
b.a = 12;
b.b = 42;
node = b;
```
```

# 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
...
}
```
2 changes: 1 addition & 1 deletion include/yaml-cpp/depthguard.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<DeepRecursion>(m_depth, mark_, msg_);
}
}

Expand Down
8 changes: 8 additions & 0 deletions include/yaml-cpp/dll.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 39 additions & 0 deletions include/yaml-cpp/exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,50 @@
#include "yaml-cpp/mark.h"
#include "yaml-cpp/noexcept.h"
#include "yaml-cpp/traits.h"
#include <exception>
#include <sstream>
#include <stdexcept>
#include <string>

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<typename Ex, typename... Args>
YAML_CPP_NORETURN void raise(Args&&... args) {
if (get_handle_exception_local()) {
get_handle_exception_local()(Ex(std::forward<Args>(args)...).what());
} else if (get_handle_exception()) {
get_handle_exception()(Ex(std::forward<Args>(args)...).what());
}
#if !defined(YAML_CPP_DISABLE_EXCEPTIONS)
else {
throw Ex(std::forward<Args>(args)...);
}
#endif
std::terminate(); // The handle_exception() call should have terminated
}

// error messages
namespace ErrorMsg {
const char* const YAML_DIRECTIVE_ARGS =
Expand Down
6 changes: 3 additions & 3 deletions include/yaml-cpp/node/detail/impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<BadSubscript>(m_mark, key);
}

auto it = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) {
Expand All @@ -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<BadSubscript>(m_mark, key);
}

auto it = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) {
Expand Down Expand Up @@ -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<BadInsert>();
}

node& k = convert_to_node(key, pMemory);
Expand Down
40 changes: 20 additions & 20 deletions include/yaml-cpp/node/impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ inline Node::~Node() = default;

inline void Node::EnsureNodeExists() const {
if (!m_isValid)
throw InvalidNode(m_invalidKey);
raise<InvalidNode>(m_invalidKey);
if (!m_pNode) {
m_pMemory.reset(new detail::memory_holder);
m_pNode = &m_pMemory->create_node();
Expand All @@ -80,14 +80,14 @@ inline bool Node::IsDefined() const {

inline Mark Node::Mark() const {
if (!m_isValid) {
throw InvalidNode(m_invalidKey);
raise<InvalidNode>(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<InvalidNode>(m_invalidKey);
return m_pNode ? m_pNode->type() : NodeType::Null;
}

Expand Down Expand Up @@ -131,12 +131,12 @@ struct as_if<T, void> {

T operator()() const {
if (!node.m_pNode) // no fallback
throw InvalidNode(node.m_invalidKey);
raise<InvalidNode>(node.m_invalidKey);

T t;
if (convert<T>::decode(node, t))
return t;
throw TypedBadConversion<T>(node.Mark());
raise<TypedBadConversion<T> >(node.Mark());
}
};

Expand All @@ -147,11 +147,11 @@ struct as_if<std::string, void> {

std::string operator()() const {
if (node.Type() == NodeType::Undefined) // no fallback
throw InvalidNode(node.m_invalidKey);
raise<InvalidNode>(node.m_invalidKey);
if (node.Type() == NodeType::Null)
return "null";
if (node.Type() != NodeType::Scalar)
throw TypedBadConversion<std::string>(node.Mark());
raise<TypedBadConversion<std::string> >(node.Mark());
return node.Scalar();
}
};
Expand All @@ -160,7 +160,7 @@ struct as_if<std::string, void> {
template <typename T>
inline T Node::as() const {
if (!m_isValid)
throw InvalidNode(m_invalidKey);
raise<InvalidNode>(m_invalidKey);
return as_if<T, void>(*this)();
}

Expand All @@ -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<InvalidNode>(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<InvalidNode>("use-after-free");
else
throw BadDereference();
raise<BadDereference>();
}

inline const std::string& Node::Tag() const {
if (!m_isValid)
throw InvalidNode(m_invalidKey);
raise<InvalidNode>(m_invalidKey);
return m_pNode ? m_pNode->tag() : detail::node_data::empty_scalar();
}

Expand All @@ -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<InvalidNode>(m_invalidKey);
return m_pNode ? m_pNode->style() : EmitterStyle::Default;
}

Expand All @@ -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<InvalidNode>(m_invalidKey);
if (!m_pNode || !rhs.m_pNode)
return false;
return m_pNode->is(*rhs.m_pNode);
Expand All @@ -231,15 +231,15 @@ 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<InvalidNode>(m_invalidKey);
m_pMemory = rhs.m_pMemory;
m_pNode = rhs.m_pNode;
}

template <typename T>
inline void Node::Assign(const T& rhs) {
if (!m_isValid)
throw InvalidNode(m_invalidKey);
raise<InvalidNode>(m_invalidKey);
AssignData(convert<T>::encode(rhs));
}

Expand Down Expand Up @@ -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<InvalidNode>(m_invalidKey);
rhs.EnsureNodeExists();

if (!m_pNode) {
Expand All @@ -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<InvalidNode>(m_invalidKey);
return m_pNode ? m_pNode->size() : 0;
}

Expand Down Expand Up @@ -335,7 +335,7 @@ inline reverse_iterator Node::rend() {
template <typename T>
inline void Node::push_back(const T& rhs) {
if (!m_isValid)
throw InvalidNode(m_invalidKey);
raise<InvalidNode>(m_invalidKey);
push_back(Node(rhs));
}

Expand Down Expand Up @@ -413,7 +413,7 @@ inline void Node::force_insert(const Key& key, const Value& value) {
template <typename Key>
inline bool Node::contains(const Key& key) const {
if (!m_isValid)
throw InvalidNode(m_invalidKey);
raise<InvalidNode>(m_invalidKey);
if (!m_pNode) return false;
return (static_cast<const detail::node*>(m_pNode))->get(key, m_pMemory) != nullptr;
}
Expand Down
20 changes: 20 additions & 0 deletions src/exceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions src/exp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ParserException>(mark, ErrorMsg::INVALID_HEX);

value = (value << 4) + digit;
}
Expand All @@ -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<ParserException>(in.mark(), msg.str());
}

// now break it up into chars
Expand Down Expand Up @@ -131,7 +131,7 @@ std::string Escape(Stream& in) {
}

std::stringstream msg;
throw ParserException(in.mark(), std::string(ErrorMsg::INVALID_ESCAPE) + ch);
raise<ParserException>(in.mark(), std::string(ErrorMsg::INVALID_ESCAPE) + ch);
}
} // namespace Exp
} // namespace YAML
Loading