Skip to content

Commit

Permalink
(draft) exception_info implementation (P0640)
Browse files Browse the repository at this point in the history
  • Loading branch information
K-ballo committed Jun 6, 2017
1 parent 46373fe commit 3790289
Showing 1 changed file with 391 additions and 0 deletions.
391 changes: 391 additions & 0 deletions hpx/exception_info.hpp
@@ -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*/

0 comments on commit 3790289

Please sign in to comment.