forked from boostorg/core
Extracted uncaught_exceptions function from Boost.Log.
The uncaught_exceptions function is functionally equivalent to unhandled_exceptions_count in Boost.Log and implements functionality similar to the same named C++17 standard function. Tests and docs are also included. One notable difference from std::uncaught_exceptions is that the return type is unsigned rather than signed. This is deliberate as uncaught_exceptions must never return a negative value and unsigned int better documents that. Theoretically, as a counter, it may also overflow.
This commit is contained in:
@ -57,3 +57,4 @@ criteria for inclusion is that the utility component be:
|
||||
[include scoped_enum.qbk]
|
||||
[include swap.qbk]
|
||||
[include typeinfo.qbk]
|
||||
[include uncaught_exceptions.qbk]
|
||||
|
52
doc/uncaught_exceptions.qbk
Normal file
52
doc/uncaught_exceptions.qbk
Normal file
@ -0,0 +1,52 @@
|
||||
[/
|
||||
/ Copyright (c) 2018 Andrey Semashev
|
||||
/
|
||||
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
/]
|
||||
|
||||
[section:uncaught_exceptions uncaught_exceptions]
|
||||
|
||||
[simplesect Authors]
|
||||
|
||||
* Andrey Semashev
|
||||
|
||||
[endsimplesect]
|
||||
|
||||
[section Header <boost/core/uncaught_exceptions.hpp>]
|
||||
|
||||
The header `<boost/core/uncaught_exceptions.hpp>` defines the `boost::core::uncaught_exceptions` function,
|
||||
which is a more portable implementation of the same named function introduced in C++17. The function
|
||||
returns the number of the currently pending exceptions. When that function returns a value greater than 0,
|
||||
throwing an exception from a destructor can terminate the program.
|
||||
|
||||
Unfortunately, the function cannot be implemented on every pre-C++17 compiler, although the most commonly
|
||||
used compilers are supported. When the compiler does not provide the necessary functionality,
|
||||
`boost::core::uncaught_exceptions` returns a non-zero value if at least one exception is pending (i.e. not
|
||||
necessarily the number of pending exceptions), and `BOOST_CORE_UNCAUGHT_EXCEPTIONS_EMULATED` macro
|
||||
is defined.
|
||||
|
||||
[section Example]
|
||||
``
|
||||
class my_class
|
||||
{
|
||||
private:
|
||||
const unsigned int m_exception_count;
|
||||
|
||||
public:
|
||||
my_class() : m_exception_count(boost::core::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
~my_class() noexcept(false)
|
||||
{
|
||||
if (m_exception_count == boost::core::uncaught_exceptions())
|
||||
do_something_potentially_throwing();
|
||||
}
|
||||
};
|
||||
``
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
93
include/boost/core/uncaught_exceptions.hpp
Normal file
93
include/boost/core/uncaught_exceptions.hpp
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2018.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* https://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
/*!
|
||||
* \file uncaught_exceptions.hpp
|
||||
* \author Andrey Semashev
|
||||
* \date 2018-11-10
|
||||
*
|
||||
* \brief This header provides an `uncaught_exception` function implementation, which was introduced in C++17.
|
||||
*
|
||||
* The code in this file is based on the implementation by Evgeny Panasyuk:
|
||||
*
|
||||
* https://github.com/panaseleus/stack_unwinding/blob/master/boost/exception/uncaught_exception_count.hpp
|
||||
*/
|
||||
|
||||
#include <exception>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#if defined(BOOST_HAS_PRAGMA_ONCE)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
|
||||
namespace core {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// cxxabi.h availability macro
|
||||
#if defined(__has_include) && (!defined(BOOST_GCC) || (__GNUC__ + 0) >= 5)
|
||||
# if __has_include(<cxxabi.h>)
|
||||
# define BOOST_CORE_HAS_CXXABI_H
|
||||
# endif
|
||||
#elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
|
||||
# define BOOST_CORE_HAS_CXXABI_H
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_uncaught_exceptions) && __cpp_lib_uncaught_exceptions >= 201411
|
||||
#define BOOST_CORE_HAS_UNCAUGHT_EXCEPTIONS
|
||||
#elif defined(BOOST_CORE_HAS_CXXABI_H)
|
||||
// MinGW GCC 4.4 seem to not work the same way the newer GCC versions do. As a result, __cxa_get_globals based implementation will always return 0.
|
||||
// Just disable it for now and fall back to std::uncaught_exception().
|
||||
#if !defined(__MINGW32__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)))
|
||||
// Only GCC 4.7 declares __cxa_get_globals() in cxxabi.h, older compilers do not expose this function but it's there
|
||||
#define BOOST_CORE_HAS_CXA_GET_GLOBALS
|
||||
extern "C" void* __cxa_get_globals();
|
||||
#endif
|
||||
#elif defined(_MSC_VER)
|
||||
#if _MSC_VER >= 1900
|
||||
// Visual Studio 14 supports N4152 std::uncaught_exceptions()
|
||||
#define BOOST_CORE_HAS_UNCAUGHT_EXCEPTIONS
|
||||
#elif _MSC_VER >= 1400
|
||||
#define BOOST_CORE_HAS_GETPTD
|
||||
extern "C" void* _getptd();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#if !defined(BOOST_CORE_HAS_UNCAUGHT_EXCEPTIONS) && !defined(BOOST_CORE_HAS_CXA_GET_GLOBALS) && !defined(BOOST_CORE_HAS_GETPTD)
|
||||
//! This macro is defined when `uncaught_exceptions` is not guaranteed to return values greater than 1 if multiple exceptions are pending
|
||||
#define BOOST_CORE_UNCAUGHT_EXCEPTIONS_EMULATED
|
||||
#endif
|
||||
|
||||
//! Returns the number of currently pending exceptions
|
||||
inline unsigned int uncaught_exceptions() BOOST_NOEXCEPT
|
||||
{
|
||||
#if defined(BOOST_CORE_HAS_UNCAUGHT_EXCEPTIONS)
|
||||
// C++17 implementation
|
||||
return static_cast< unsigned int >(std::uncaught_exceptions());
|
||||
#elif defined(BOOST_CORE_HAS_CXA_GET_GLOBALS)
|
||||
// Tested on {clang 3.2,GCC 3.5.6,GCC 4.1.2,GCC 4.4.6,GCC 4.4.7}x{x32,x64}
|
||||
return *(reinterpret_cast< const unsigned int* >(static_cast< const char* >(boost::core::detail::__cxa_get_globals()) + sizeof(void*))); // __cxa_eh_globals::uncaughtExceptions, x32 offset - 0x4, x64 - 0x8
|
||||
#elif defined(BOOST_CORE_HAS_GETPTD)
|
||||
// MSVC specific. Tested on {MSVC2005SP1,MSVC2008SP1,MSVC2010SP1,MSVC2012}x{x32,x64}.
|
||||
return *(reinterpret_cast< const unsigned int* >(static_cast< const char* >(boost::core::detail::_getptd()) + (sizeof(void*) == 8 ? 0x100 : 0x90))); // _tiddata::_ProcessingThrow, x32 offset - 0x90, x64 - 0x100
|
||||
#else
|
||||
// Portable C++03 implementation. Does not allow to detect multiple nested exceptions.
|
||||
return static_cast< unsigned int >(std::uncaught_exception());
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef BOOST_CORE_HAS_CXXABI_H
|
||||
#undef BOOST_CORE_HAS_CXA_GET_GLOBALS
|
||||
#undef BOOST_CORE_HAS_UNCAUGHT_EXCEPTIONS
|
||||
#undef BOOST_CORE_HAS_GETPTD
|
||||
|
||||
} // namespace core
|
||||
|
||||
} // namespace boost
|
@ -138,5 +138,8 @@ run test_lib_typeid.cpp lib_typeid : : : <link>static : test_lib_typeid_static ;
|
||||
run test_lib_typeid.cpp lib_typeid : : : <link>shared <rtti>off : test_lib_typeid_shared_no_rtti ;
|
||||
run test_lib_typeid.cpp lib_typeid : : : <link>static <rtti>off : test_lib_typeid_static_no_rtti ;
|
||||
|
||||
run uncaught_exceptions.cpp : : : <exception-handling>on ;
|
||||
run uncaught_exceptions_np.cpp : : : <exception-handling>on ;
|
||||
|
||||
use-project /boost/core/swap : ./swap ;
|
||||
build-project ./swap ;
|
||||
|
55
test/uncaught_exceptions.cpp
Normal file
55
test/uncaught_exceptions.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2018.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* https://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
/*!
|
||||
* \file uncaught_exceptions.cpp
|
||||
* \author Andrey Semashev
|
||||
* \date 2018-11-10
|
||||
*
|
||||
* \brief This file contains tests for the uncaught_exceptions function.
|
||||
*
|
||||
* This file only contains the very basic checks of functionality that can be portably achieved
|
||||
* through C++03 std::uncaught_exception.
|
||||
*/
|
||||
|
||||
#include <boost/core/uncaught_exceptions.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
struct my_exception {};
|
||||
|
||||
class exception_watcher
|
||||
{
|
||||
unsigned int& m_count;
|
||||
|
||||
public:
|
||||
explicit exception_watcher(unsigned int& count) : m_count(count) {}
|
||||
~exception_watcher() { m_count = boost::core::uncaught_exceptions(); }
|
||||
};
|
||||
|
||||
// Tests for uncaught_exceptions when used in a destructor while an exception propagates
|
||||
void test_in_destructor()
|
||||
{
|
||||
const unsigned int root_count = boost::core::uncaught_exceptions();
|
||||
|
||||
unsigned int level1_count = root_count;
|
||||
try
|
||||
{
|
||||
exception_watcher watcher(level1_count);
|
||||
throw my_exception();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
BOOST_TEST_NE(root_count, level1_count);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_in_destructor();
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
90
test/uncaught_exceptions_np.cpp
Normal file
90
test/uncaught_exceptions_np.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright Andrey Semashev 2018.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* https://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
/*!
|
||||
* \file uncaught_exceptions_np.cpp
|
||||
* \author Andrey Semashev
|
||||
* \date 2018-11-10
|
||||
*
|
||||
* \brief This file contains tests for the uncaught_exceptions function.
|
||||
*
|
||||
* This file contains checks that are compiler specific and not quite portable or require C++17.
|
||||
*/
|
||||
|
||||
#include <boost/core/uncaught_exceptions.hpp>
|
||||
|
||||
#if !defined(BOOST_CORE_UNCAUGHT_EXCEPTIONS_EMULATED)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
struct my_exception1 {};
|
||||
struct my_exception2 {};
|
||||
|
||||
class exception_watcher2
|
||||
{
|
||||
unsigned int& m_count;
|
||||
|
||||
public:
|
||||
explicit exception_watcher2(unsigned int& count) : m_count(count) {}
|
||||
~exception_watcher2() { m_count = boost::core::uncaught_exceptions(); }
|
||||
};
|
||||
|
||||
class exception_watcher1
|
||||
{
|
||||
unsigned int& m_count1;
|
||||
unsigned int& m_count2;
|
||||
|
||||
public:
|
||||
exception_watcher1(unsigned int& count1, unsigned int& count2) : m_count1(count1), m_count2(count2) {}
|
||||
~exception_watcher1()
|
||||
{
|
||||
m_count1 = boost::core::uncaught_exceptions();
|
||||
try
|
||||
{
|
||||
exception_watcher2 watcher2(m_count2);
|
||||
throw my_exception2();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Tests for uncaught_exceptions when used in nested destructors while an exception propagates
|
||||
void test_in_nested_destructors()
|
||||
{
|
||||
const unsigned int root_count = boost::core::uncaught_exceptions();
|
||||
|
||||
unsigned int level1_count = root_count, level2_count = root_count;
|
||||
try
|
||||
{
|
||||
exception_watcher1 watcher1(level1_count, level2_count);
|
||||
throw my_exception1();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
BOOST_TEST_NE(root_count, level1_count);
|
||||
BOOST_TEST_NE(root_count, level2_count);
|
||||
BOOST_TEST_NE(level1_count, level2_count);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_in_nested_destructors();
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user