Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(draft) exception_info implementation (P0640)
- Loading branch information
Showing
1 changed file
with
391 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,391 @@ | ||
// Copyright (c) 2017 Agustin Berge | ||
// | ||
// 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) | ||
|
||
#ifndef HPX_EXCEPTION_INFO_HPP | ||
#define HPX_EXCEPTION_INFO_HPP | ||
|
||
#include <hpx/config.hpp> | ||
#include <hpx/util/detail/pack.hpp> | ||
#include <hpx/util/tuple.hpp> | ||
|
||
#include <algorithm> | ||
#include <cstddef> | ||
#include <exception> | ||
#include <iosfwd> | ||
#include <memory> | ||
#include <sstream> | ||
#include <string> | ||
#include <type_traits> | ||
#include <typeinfo> | ||
#include <utility> | ||
|
||
#if defined(HPX_WINDOWS) | ||
# include <excpt.h> | ||
# undef exception_info | ||
#endif | ||
|
||
namespace hpx | ||
{ | ||
/////////////////////////////////////////////////////////////////////////// | ||
template <typename Tag, typename Type> | ||
struct exception_info_tag | ||
{ | ||
using tag = Tag; | ||
using type = Type; | ||
|
||
explicit exception_info_tag(Type const& value) | ||
: _value(value) | ||
{} | ||
|
||
explicit exception_info_tag(Type&& value) | ||
: _value(std::forward<Type>(value)) | ||
{} | ||
|
||
Type _value; | ||
}; | ||
|
||
#define HPX_DEFINE_EXCEPTION_INFO_TAG(NAME, TYPE) \ | ||
struct NAME : ::hpx::exception_info_tag<NAME, TYPE> \ | ||
{ \ | ||
explicit NAME(TYPE const& value) \ | ||
: exception_info_tag(value) \ | ||
{} \ | ||
\ | ||
explicit NAME(TYPE&& value) \ | ||
: exception_info_tag(std::forward<TYPE>(value)) \ | ||
{} \ | ||
}; \ | ||
/**/ | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
namespace detail | ||
{ | ||
inline void diagnostic_info_hexdump( | ||
std::ostream& stream, | ||
void const* data, std::size_t size) | ||
{ | ||
static constexpr char hex_lut[] = { | ||
'0', '1', '2', '3', '4', '5', '6', '7', | ||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' | ||
}; | ||
|
||
unsigned char const* const begin = | ||
static_cast<unsigned char const*>(data); | ||
unsigned char const* const end = begin + size; | ||
for (unsigned char const* iter = begin; iter != end; ++iter) | ||
{ | ||
if (iter != begin) stream << ' '; | ||
stream << hex_lut[*iter / 16] << hex_lut[*iter % 16]; | ||
} | ||
} | ||
|
||
template <typename Tag, typename Type> | ||
auto diagnostic_info(std::ostream& stream, Type const& value, int) | ||
-> decltype((stream << value), void()) | ||
{ | ||
stream << '[' << typeid(Tag).name() << "]: " << value << "\n"; | ||
} | ||
|
||
template <typename Tag, typename Type> | ||
void diagnostic_info(std::ostream& stream, Type const& value, ...) | ||
{ | ||
stream << '[' << typeid(Tag).name() << "]: ["; | ||
diagnostic_info_hexdump(stream, | ||
std::addressof(value), std::min<std::size_t>(sizeof(value), 10u)); | ||
if (sizeof(value) > 10) stream << " ..."; | ||
stream << "]\n"; | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////// | ||
class exception_info_node_base | ||
{ | ||
public: | ||
virtual ~exception_info_node_base() = default; | ||
virtual void const* lookup(std::type_info const& tag) const noexcept = 0; | ||
virtual void diagnostic_info(std::ostream& stream) const = 0; | ||
|
||
std::shared_ptr<exception_info_node_base> next; | ||
}; | ||
|
||
template <typename ...Ts> | ||
class exception_info_node | ||
: public exception_info_node_base | ||
{ | ||
public: | ||
template <typename ...Tags, typename ...Types> | ||
explicit exception_info_node( | ||
exception_info_tag<Tags, Types>&&... tagged_values) | ||
: data(std::forward<Types>(tagged_values._value)...) | ||
{} | ||
|
||
template <std::size_t ...Is> | ||
void const* _lookup( | ||
util::detail::pack_c<std::size_t, Is...>, | ||
std::type_info const& tag) const noexcept | ||
{ | ||
using entry_type = std::pair<std::type_info const*, void const*>; | ||
entry_type const entries[] = { | ||
{ &typeid(typename Ts::tag), std::addressof(util::get<Is>(data))}... | ||
}; | ||
|
||
for (auto const& entry : entries) | ||
{ | ||
if (*entry.first == tag) | ||
return entry.second; | ||
} | ||
return nullptr; | ||
} | ||
|
||
void const* lookup(std::type_info const& tag) const noexcept override | ||
{ | ||
using indices_pack = | ||
typename util::detail::make_index_pack<sizeof...(Ts)>::type; | ||
if (void const* value = _lookup(indices_pack(), tag)) | ||
return value; | ||
|
||
return next ? next->lookup(tag) : nullptr; | ||
} | ||
|
||
template <std::size_t ...Is> | ||
void _diagnostic_info( | ||
util::detail::pack_c<std::size_t, Is...>, | ||
std::ostream& stream) const noexcept | ||
{ | ||
int _sequencer[] = {( | ||
detail::diagnostic_info<typename Ts::tag>( | ||
stream, util::get<Is>(data), 0) | ||
, 0)...}; | ||
(void)_sequencer; | ||
} | ||
|
||
void diagnostic_info(std::ostream& stream) const override | ||
{ | ||
using indices_pack = | ||
typename util::detail::make_index_pack<sizeof...(Ts)>::type; | ||
_diagnostic_info(indices_pack(), stream); | ||
|
||
return next ? next->diagnostic_info(stream) : void(); | ||
} | ||
|
||
using exception_info_node_base::next; | ||
util::tuple<typename Ts::type...> data; | ||
}; | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
class exception_info | ||
{ | ||
using node_ptr = std::shared_ptr<detail::exception_info_node_base>; | ||
|
||
public: | ||
exception_info() noexcept | ||
: _file(nullptr), _line(0), _function(nullptr) | ||
, _data(nullptr) | ||
{} | ||
|
||
exception_info(char const* file, int line) noexcept | ||
: _file(file), _line(line), _function(nullptr) | ||
, _data(nullptr) | ||
{} | ||
|
||
exception_info(char const* file, int line, char const* function) noexcept | ||
: _file(file), _line(line), _function(function) | ||
, _data(nullptr) | ||
{} | ||
|
||
exception_info(exception_info const& other) noexcept = default; | ||
exception_info(exception_info&& other) noexcept = default; | ||
|
||
exception_info& operator=(exception_info const& other) noexcept = default; | ||
exception_info& operator=(exception_info&& other) noexcept = default; | ||
|
||
virtual ~exception_info() = default; | ||
|
||
char const* file_name() const noexcept | ||
{ | ||
return _file; | ||
} | ||
|
||
int line() const noexcept | ||
{ | ||
return _line; | ||
} | ||
|
||
char const* function_name() const noexcept | ||
{ | ||
return _function; | ||
} | ||
|
||
template <typename ...Tags, typename ...Types> | ||
void set(exception_info_tag<Tags, Types>&&... tagged_values) | ||
{ | ||
using node_type = detail::exception_info_node< | ||
exception_info_tag<Tags, Types>...>; | ||
|
||
node_ptr node = std::make_shared<node_type>(std::move(tagged_values)...); | ||
node->next = std::move(_data); | ||
_data = std::move(node); | ||
} | ||
|
||
template <typename Tag> | ||
typename Tag::type const* get() const noexcept | ||
{ | ||
auto const* data = _data.get(); | ||
return static_cast<typename Tag::type const*>( | ||
data ? data->lookup(typeid(typename Tag::tag)) : nullptr); | ||
} | ||
|
||
std::string diagnostic_info() const | ||
{ | ||
std::ostringstream stream; | ||
stream << (_file ? _file : "<unknown-file>"); | ||
if (_line) stream << '(' << _line << ')'; | ||
stream << ": throw_with_info in function " | ||
<< (_function ? _function : "<unknown>") << '\n'; | ||
if (auto const* data = _data.get()) | ||
data->diagnostic_info(stream); | ||
return stream.str(); | ||
} | ||
|
||
private: | ||
char const* _file; | ||
unsigned int _line; | ||
char const* _function; | ||
node_ptr _data; | ||
}; | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
namespace detail | ||
{ | ||
struct exception_with_info_base | ||
: public exception_info | ||
{ | ||
exception_with_info_base(std::type_info const& type, exception_info xi) | ||
: exception_info(std::move(xi)) | ||
, type(type) | ||
{} | ||
|
||
std::type_info const& type; | ||
}; | ||
|
||
template <typename E> | ||
struct exception_with_info | ||
: public E | ||
, public exception_with_info_base | ||
{ | ||
explicit exception_with_info(E const& e, exception_info xi) | ||
: E(e) | ||
, exception_with_info_base(typeid(E), std::move(xi)) | ||
{} | ||
|
||
explicit exception_with_info(E&& e, exception_info xi) | ||
: E(std::move(e)) | ||
, exception_with_info_base(typeid(E), std::move(xi)) | ||
{} | ||
}; | ||
} | ||
|
||
template <typename E> [[noreturn]] | ||
void throw_with_info(E&& e, exception_info&& xi = exception_info()) | ||
{ | ||
using ED = typename std::decay<E>::type; | ||
static_assert( | ||
#if defined(HPX_HAVE_CXX14_STD_IS_FINAL) | ||
std::is_class<ED>::value && !std::is_final<ED>::value, | ||
#else | ||
std::is_class<ED>::value, | ||
#endif | ||
"E shall be a valid base class"); | ||
static_assert( | ||
!std::is_base_of<exception_info, ED>::value, | ||
"E shall not derive from exception_info"); | ||
|
||
throw detail::exception_with_info<E>(std::forward<E>(e), std::move(xi)); | ||
} | ||
|
||
template <typename E> [[noreturn]] | ||
void throw_with_info(E&& e, exception_info const& xi) | ||
{ | ||
throw_with_info(std::forward<E>(e), exception_info(xi)); | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
template <typename E> | ||
exception_info const* get_exception_info(E const& e) | ||
{ | ||
return dynamic_cast<exception_info const*>(std::addressof(e)); | ||
} | ||
|
||
template <typename E> | ||
exception_info* get_exception_info(E& e) | ||
{ | ||
return dynamic_cast<exception_info*>(std::addressof(e)); | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
namespace detail | ||
{ | ||
template <typename E> | ||
std::string exception_diagnostic_info( | ||
E const* ep, /*is_polymorphic=*/std::true_type) | ||
{ | ||
std::ostringstream stream; | ||
|
||
if (auto const* ewip = dynamic_cast<exception_with_info_base const*>(ep)) | ||
stream << "Dynamic exception type: " << ewip->type.name() << '\n'; | ||
else | ||
stream << "Dynamic exception type: " << typeid(*ep).name() << '\n'; | ||
|
||
if (auto const* sep = dynamic_cast<std::exception const*>(ep)) | ||
stream << "what: " << sep->what() << '\n'; | ||
|
||
if (auto const* xip = get_exception_info(*ep)) | ||
stream << xip->diagnostic_info(); | ||
|
||
return stream.str(); | ||
} | ||
|
||
template <typename E> | ||
std::string exception_diagnostic_info( | ||
E const*, /*is_polymorphic=*/std::false_type) | ||
{ | ||
std::ostringstream stream; | ||
stream << "Dynamic exception type: " << typeid(E).name(); | ||
return stream.str(); | ||
} | ||
} | ||
|
||
template <typename E> | ||
std::string exception_diagnostic_info(E const& e) | ||
{ | ||
return detail::exception_diagnostic_info( | ||
std::addressof(e), std::is_polymorphic<E>()); | ||
} | ||
|
||
inline std::string exception_diagnostic_info(std::exception_ptr const& p) | ||
{ | ||
try | ||
{ | ||
if (p) std::rethrow_exception(p); | ||
} catch (exception_info const& xi) { | ||
return detail::exception_diagnostic_info( | ||
std::addressof(xi), /*is_polymorphic=*/std::true_type()); | ||
} catch (std::exception const& se) { | ||
return detail::exception_diagnostic_info( | ||
std::addressof(se), /*is_polymorphic=*/std::true_type()); | ||
} catch (...) { | ||
return "Dynamic exception type: <unknown>"; | ||
} | ||
return std::string(); | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////// | ||
inline std::string current_exception_diagnostic_info() | ||
{ | ||
return exception_diagnostic_info(std::current_exception()); | ||
} | ||
} | ||
|
||
#endif /*HPX_EXCEPTION_INFO_HPP*/ |