3502 lines
86 KiB
C++
3502 lines
86 KiB
C++
|
|
#ifndef STDX_COMPILER_HPP
|
||
|
|
#define STDX_COMPILER_HPP
|
||
|
|
|
||
|
|
// Check compiler macros. Note that Clang defines __GNUC__ and other GNU macros as well,
|
||
|
|
// but GNU does not define Clang macros, so we must check for Clang first.
|
||
|
|
|
||
|
|
#if defined(__llvm__) || defined(__clang__)
|
||
|
|
|
||
|
|
// -------- LLVM/Clang
|
||
|
|
|
||
|
|
#define STDX_CLANG_COMPILER 1
|
||
|
|
|
||
|
|
#if defined(__cpp_variable_templates) && (__cplusplus >= 201703L)
|
||
|
|
#define STDX_VARIABLE_TEMPLATES 1
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
|
||
|
|
|
||
|
|
// -------- GNU G++
|
||
|
|
|
||
|
|
#include <version>
|
||
|
|
|
||
|
|
#define STDX_GCC_COMPILER 1
|
||
|
|
|
||
|
|
#if (__GNUC__ >= 5) && (__cplusplus >= 201703L)
|
||
|
|
#define STDX_VARIABLE_TEMPLATES 1
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if ((__GNUC__ > 7) || ((__GNUC__ == 7) && (__GNUC_MINOR__ >= 1)))
|
||
|
|
#define STDX_TRIVIALLY_MOVE_CONSTRUCTIBLE 1
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(STDX_GCC_COMPILER)
|
||
|
|
#if (__GNUC__ == 7) && ((__GNUC_MINOR__ >= 1) && (__GNUC_MINOR__ <= 3))
|
||
|
|
#define STDX_GCC7_WORKAROUND_CONSTEXPR
|
||
|
|
#else
|
||
|
|
#define STDX_GCC7_WORKAROUND_CONSTEXPR constexpr
|
||
|
|
#endif
|
||
|
|
#else
|
||
|
|
#define STDX_GCC7_WORKAROUND_CONSTEXPR constexpr
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// Add a legacy constexpr macro for cases where GCC < 5 incorrectly applies the const
|
||
|
|
// qualifier to constexpr member functions or does not support relaxed constexpr functions
|
||
|
|
//
|
||
|
|
#if defined(STDX_GCC_COMPILER) && (__GNUC__ < 5)
|
||
|
|
#define STDX_LEGACY_CONSTEXPR
|
||
|
|
#else
|
||
|
|
#define STDX_LEGACY_CONSTEXPR constexpr
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1910)
|
||
|
|
#define STDX_MSVC_EMPTY_BASE_CLASSES __declspec(empty_bases)
|
||
|
|
#else
|
||
|
|
#define STDX_MSVC_EMPTY_BASE_CLASSES
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(__cpp_impl_trivially_relocatable)
|
||
|
|
#define STDX_TRIVIALLY_RELOCATABLE [[trivially_relocatable]]
|
||
|
|
#else
|
||
|
|
#define STDX_TRIVIALLY_RELOCATABLE
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#endif // STDX_COMPILER_HPP
|
||
|
|
|
||
|
|
|
||
|
|
#ifndef STDX_TYPE_TRAITS_HPP
|
||
|
|
#define STDX_TYPE_TRAITS_HPP
|
||
|
|
|
||
|
|
#include <type_traits>
|
||
|
|
|
||
|
|
namespace stdx {
|
||
|
|
|
||
|
|
// Implementation of std::void_t for use with pre-C++17 compilers.
|
||
|
|
//
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
template <class... T>
|
||
|
|
struct void_t_impl
|
||
|
|
{
|
||
|
|
using type = void;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
template <class... T>
|
||
|
|
using void_t = typename detail::void_t_impl<T...>::type;
|
||
|
|
|
||
|
|
template <class T, class U = T>
|
||
|
|
struct dependent_type
|
||
|
|
{
|
||
|
|
using type = U;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <class T, class U = T>
|
||
|
|
using dependent_type_t = typename dependent_type<T, U>::type;
|
||
|
|
|
||
|
|
template <class... B>
|
||
|
|
struct disjunction : std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class B1>
|
||
|
|
struct disjunction<B1> : B1
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template<class B1, class... Bn>
|
||
|
|
struct disjunction<B1, Bn...>
|
||
|
|
:
|
||
|
|
std::conditional_t<
|
||
|
|
bool(B1::value),
|
||
|
|
B1,
|
||
|
|
disjunction<Bn...>
|
||
|
|
>
|
||
|
|
{ };
|
||
|
|
|
||
|
|
struct sentinel_type
|
||
|
|
{
|
||
|
|
static constexpr bool value = true;
|
||
|
|
using type = void;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <bool B>
|
||
|
|
struct bool_constant : std::integral_constant<bool, B>
|
||
|
|
{ };
|
||
|
|
|
||
|
|
// Implementation of std::remove_cvref for use with pre-C++20 compilers.
|
||
|
|
//
|
||
|
|
template <class T>
|
||
|
|
struct remove_cvref
|
||
|
|
{
|
||
|
|
using type = std::remove_cv_t<std::remove_reference_t<T>>;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
using remove_cvref_t = typename remove_cvref<T>::type;
|
||
|
|
|
||
|
|
#if defined(STDX_GCC_COMPILER)
|
||
|
|
// Support missing functionality on older compilers (<= gcc 4.7)
|
||
|
|
//
|
||
|
|
#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 7)
|
||
|
|
// Map the old incorrect type-trait names to the newer correct ones
|
||
|
|
template <class T>
|
||
|
|
using is_trivially_copyable = std::is_trivial<T>;
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
using is_trivially_copy_constructible = std::has_trivial_copy_constructor<T>;
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
using is_trivially_destructible = std::has_trivial_destructor<T>;
|
||
|
|
#elif (__GNUC__ < 5)
|
||
|
|
template <class T>
|
||
|
|
using is_trivially_copyable = std::is_trivial<T>;
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
using is_trivially_copy_constructible = std::has_trivial_copy_constructor<T>;
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
using is_trivially_destructible = std::is_trivially_destructible<T>;
|
||
|
|
#else
|
||
|
|
using std::is_trivially_destructible;
|
||
|
|
using std::is_trivially_copyable;
|
||
|
|
using std::is_trivially_copy_constructible;
|
||
|
|
#endif
|
||
|
|
#else
|
||
|
|
using std::is_trivially_destructible;
|
||
|
|
using std::is_trivially_copyable;
|
||
|
|
using std::is_trivially_copy_constructible;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(STDX_TRIVIALLY_MOVE_CONSTRUCTIBLE)
|
||
|
|
template <class T>
|
||
|
|
using is_trivially_move_constructible = std::is_trivially_move_constructible<T>;
|
||
|
|
#else
|
||
|
|
template <class T>
|
||
|
|
using is_trivially_move_constructible = is_trivially_copyable<T>;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(__cpp_lib_trivially_relocatable)
|
||
|
|
using std::is_trivially_relocatable;
|
||
|
|
#elif defined(__has_builtin)
|
||
|
|
#if __has_builtin(__is_trivially_relocatable)
|
||
|
|
template <class T>
|
||
|
|
struct is_trivially_relocatable : std::bool_constant<__is_trivially_relocatable(T)> { };
|
||
|
|
#define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE
|
||
|
|
#else
|
||
|
|
template <class T>
|
||
|
|
struct is_trivially_relocatable : is_trivially_copyable<T> { };
|
||
|
|
#define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE
|
||
|
|
#endif
|
||
|
|
#else
|
||
|
|
template <class T>
|
||
|
|
struct is_trivially_relocatable : is_trivially_copyable<T> { };
|
||
|
|
#define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if __cplusplus >= 201703L
|
||
|
|
#define STDX_LEGACY_INLINE_CONSTEXPR inline constexpr
|
||
|
|
#else
|
||
|
|
#define STDX_LEGACY_INLINE_CONSTEXPR constexpr
|
||
|
|
#endif
|
||
|
|
|
||
|
|
} // end namespace stdx
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#ifndef STDX_BIT_CAST_HPP
|
||
|
|
#define STDX_BIT_CAST_HPP
|
||
|
|
|
||
|
|
#include <cstdint>
|
||
|
|
#include <cstring>
|
||
|
|
|
||
|
|
|
||
|
|
namespace stdx {
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
template <class To, class From>
|
||
|
|
using use_static_cast = bool_constant<
|
||
|
|
((std::is_integral<To>::value || std::is_enum<To>::value)
|
||
|
|
&& (std::is_integral<From>::value || std::is_enum<From>::value))
|
||
|
|
|| (std::is_same<To, From>::value && std::is_copy_constructible<To>::value)
|
||
|
|
>;
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
using is_integral_ptr_t = bool_constant<
|
||
|
|
std::is_same<T, std::intptr_t>::value
|
||
|
|
|| std::is_same<T, std::uintptr_t>::value
|
||
|
|
>;
|
||
|
|
|
||
|
|
template <class To, class From>
|
||
|
|
using use_reinterpret_cast = bool_constant<
|
||
|
|
!std::is_same<To, From>::value
|
||
|
|
&& ((
|
||
|
|
std::is_pointer<To>::value
|
||
|
|
&& std::is_pointer<From>::value
|
||
|
|
&& std::is_convertible<From, To>::value
|
||
|
|
)
|
||
|
|
|| (std::is_pointer<To>::value && is_integral_ptr_t<From>::value)
|
||
|
|
|| (std::is_pointer<From>::value && is_integral_ptr_t<To>::value)
|
||
|
|
)
|
||
|
|
>;
|
||
|
|
|
||
|
|
#if defined(STDX_GCC_COMPILER)
|
||
|
|
template <class To, class From>
|
||
|
|
using use_union_type_punning = bool_constant<
|
||
|
|
!use_static_cast<To, From>::value
|
||
|
|
&& !use_reinterpret_cast<To, From>::value
|
||
|
|
&& !std::is_array<To>::value
|
||
|
|
&& !std::is_array<From>::value
|
||
|
|
>;
|
||
|
|
|
||
|
|
template <class To, class From>
|
||
|
|
union bit_cast_union
|
||
|
|
{
|
||
|
|
From from;
|
||
|
|
To to;
|
||
|
|
};
|
||
|
|
#else
|
||
|
|
template <class To, class From>
|
||
|
|
using use_union_type_punning = std::false_type;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
template <class To, class From>
|
||
|
|
using can_bit_cast = bool_constant<
|
||
|
|
(sizeof(To) == sizeof(From))
|
||
|
|
&& is_trivially_copyable<From>::value
|
||
|
|
&& is_trivially_copyable<To>::value
|
||
|
|
>;
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
template <
|
||
|
|
class To,
|
||
|
|
class From,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
detail::can_bit_cast<To, From>::value
|
||
|
|
&& detail::use_static_cast<To, From>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr To bit_cast(const From& from) noexcept
|
||
|
|
{
|
||
|
|
return static_cast<To>(from);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <
|
||
|
|
class To,
|
||
|
|
class From,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
detail::can_bit_cast<To, From>::value
|
||
|
|
&& detail::use_reinterpret_cast<To, From>::value
|
||
|
|
>,
|
||
|
|
int = 0
|
||
|
|
>
|
||
|
|
constexpr To bit_cast(const From& from) noexcept
|
||
|
|
{
|
||
|
|
return reinterpret_cast<To>(from);
|
||
|
|
}
|
||
|
|
|
||
|
|
#if defined(STDX_GCC_COMPILER) // GCC allows union type punning
|
||
|
|
template <
|
||
|
|
class To,
|
||
|
|
class From,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
detail::can_bit_cast<To, From>::value
|
||
|
|
&& detail::use_union_type_punning<To, From>::value
|
||
|
|
>,
|
||
|
|
class = void
|
||
|
|
>
|
||
|
|
constexpr To bit_cast(const From& from) noexcept
|
||
|
|
{
|
||
|
|
return detail::bit_cast_union<To, From>{from}.to;
|
||
|
|
}
|
||
|
|
#elif defined(STDX_CLANG_COMPILER)
|
||
|
|
#if __has_builtin(__builtin_bit_cast)
|
||
|
|
template <
|
||
|
|
class To,
|
||
|
|
class From,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
detail::can_bit_cast<To, From>::value
|
||
|
|
&& !detail::use_static_cast<To, From>::value
|
||
|
|
&& !detail::use_reinterpret_cast<To, From>::value
|
||
|
|
>,
|
||
|
|
class = void
|
||
|
|
>
|
||
|
|
constexpr To bit_cast(const From& from) noexcept
|
||
|
|
{
|
||
|
|
return __builtin_bit_cast(To, from);
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
template <
|
||
|
|
class To,
|
||
|
|
class From,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
detail::can_bit_cast<To, From>::value
|
||
|
|
&& !detail::use_static_cast<To, From>::value
|
||
|
|
&& !detail::use_reinterpret_cast<To, From>::value
|
||
|
|
>,
|
||
|
|
class = void
|
||
|
|
>
|
||
|
|
To bit_cast(const From& from) noexcept
|
||
|
|
{
|
||
|
|
To to;
|
||
|
|
std::memcpy(&to, &from, sizeof(To));
|
||
|
|
return to;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
#endif // STDX_CLANG_COMPILER
|
||
|
|
|
||
|
|
template <class To, class From>
|
||
|
|
struct is_bit_castable
|
||
|
|
:
|
||
|
|
bool_constant<
|
||
|
|
(sizeof(To) == sizeof(From))
|
||
|
|
&& is_trivially_copyable<From>::value
|
||
|
|
&& is_trivially_copyable<To>::value
|
||
|
|
>
|
||
|
|
{ };
|
||
|
|
|
||
|
|
} // end namespace stdx
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#ifndef STDX_INTRUSIVE_POINTER_HPP
|
||
|
|
#define STDX_INTRUSIVE_POINTER_HPP
|
||
|
|
|
||
|
|
|
||
|
|
#include <cstdint>
|
||
|
|
#include <atomic>
|
||
|
|
#include <memory>
|
||
|
|
|
||
|
|
namespace stdx {
|
||
|
|
|
||
|
|
struct default_intrusive_reference_count;
|
||
|
|
class default_intrusive_reference_control;
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class RefCountAccessor = default_intrusive_reference_count,
|
||
|
|
class Deleter = std::default_delete<T>,
|
||
|
|
class Pointer = T*
|
||
|
|
>
|
||
|
|
class intrusive_ptr;
|
||
|
|
|
||
|
|
using ref_count_t = std::size_t;
|
||
|
|
|
||
|
|
struct enable_reference_count
|
||
|
|
{
|
||
|
|
protected:
|
||
|
|
|
||
|
|
constexpr enable_reference_count() noexcept : m_reference_count(1)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
std::atomic<ref_count_t>& shared_reference_count() noexcept
|
||
|
|
{
|
||
|
|
return m_reference_count;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
|
||
|
|
std::atomic<ref_count_t> m_reference_count;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct default_intrusive_reference_count
|
||
|
|
{
|
||
|
|
template <class Pointer>
|
||
|
|
std::atomic<ref_count_t>& operator()(Pointer p) const noexcept
|
||
|
|
{
|
||
|
|
return p->shared_reference_count();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
template <class Pointer>
|
||
|
|
struct pointer_wrapper
|
||
|
|
{
|
||
|
|
constexpr pointer_wrapper() noexcept : shared_object(nullptr)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr explicit pointer_wrapper(Pointer p) noexcept : shared_object(p)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr pointer_wrapper(const pointer_wrapper& p) noexcept = default;
|
||
|
|
|
||
|
|
pointer_wrapper(pointer_wrapper&& p) noexcept
|
||
|
|
: shared_object(p.shared_object)
|
||
|
|
{
|
||
|
|
p.shared_object = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
pointer_wrapper& operator = (const pointer_wrapper&) noexcept = default;
|
||
|
|
|
||
|
|
pointer_wrapper& operator = (pointer_wrapper&& p) noexcept
|
||
|
|
{
|
||
|
|
shared_object = p.shared_object;
|
||
|
|
p.shared_object = nullptr;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
void assign(Pointer ptr) noexcept
|
||
|
|
{
|
||
|
|
shared_object = ptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
Pointer shared_object;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class RefCountAccessor,
|
||
|
|
class Deleter,
|
||
|
|
class Pointer,
|
||
|
|
class PointerImplementation
|
||
|
|
>
|
||
|
|
class intrusive_ptr_base
|
||
|
|
{
|
||
|
|
protected:
|
||
|
|
|
||
|
|
using pointer = Pointer;
|
||
|
|
using count_type = ref_count_t;
|
||
|
|
|
||
|
|
template <class, class, class, class>
|
||
|
|
friend class reference_count_base;
|
||
|
|
|
||
|
|
constexpr intrusive_ptr_base() = default;
|
||
|
|
|
||
|
|
constexpr intrusive_ptr_base(pointer p) noexcept
|
||
|
|
: m_impl(p)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <class RefCountAccessorForwardingReference>
|
||
|
|
constexpr explicit intrusive_ptr_base(
|
||
|
|
RefCountAccessorForwardingReference&& f,
|
||
|
|
std::enable_if_t<
|
||
|
|
std::is_constructible<
|
||
|
|
RefCountAccessor,
|
||
|
|
RefCountAccessorForwardingReference&&
|
||
|
|
>::value
|
||
|
|
>* = nullptr
|
||
|
|
)
|
||
|
|
: m_impl(std::forward<RefCountAccessorForwardingReference>(f))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <class RefCountAccessorForwardingReference>
|
||
|
|
explicit intrusive_ptr_base(
|
||
|
|
pointer ptr,
|
||
|
|
RefCountAccessorForwardingReference&& f,
|
||
|
|
std::enable_if_t<
|
||
|
|
std::is_constructible<
|
||
|
|
RefCountAccessor,
|
||
|
|
RefCountAccessorForwardingReference&&
|
||
|
|
>::value
|
||
|
|
>* = nullptr
|
||
|
|
)
|
||
|
|
: m_impl(ptr, std::forward<RefCountAccessorForwardingReference>(f))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class RefCountAccessorForwardingReference,
|
||
|
|
class DeleterForwardingReference
|
||
|
|
>
|
||
|
|
constexpr explicit intrusive_ptr_base(
|
||
|
|
RefCountAccessorForwardingReference&& f,
|
||
|
|
DeleterForwardingReference&& d,
|
||
|
|
typename std::enable_if<
|
||
|
|
std::is_constructible<
|
||
|
|
RefCountAccessor,
|
||
|
|
RefCountAccessorForwardingReference&&
|
||
|
|
>::value
|
||
|
|
&& std::is_constructible<
|
||
|
|
Deleter,
|
||
|
|
DeleterForwardingReference&&
|
||
|
|
>::value
|
||
|
|
>::type* = nullptr
|
||
|
|
)
|
||
|
|
:
|
||
|
|
m_impl(
|
||
|
|
std::forward<RefCountAccessorForwardingReference>(f),
|
||
|
|
std::forward<DeleterForwardingReference>(d)
|
||
|
|
)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class RefCountAccessorForwardingReference,
|
||
|
|
class DeleterForwardingReference
|
||
|
|
>
|
||
|
|
explicit intrusive_ptr_base(
|
||
|
|
pointer ptr,
|
||
|
|
RefCountAccessorForwardingReference&& f,
|
||
|
|
DeleterForwardingReference&& d,
|
||
|
|
std::enable_if_t<
|
||
|
|
std::is_constructible<
|
||
|
|
RefCountAccessor,
|
||
|
|
RefCountAccessorForwardingReference&&
|
||
|
|
>::value
|
||
|
|
&& std::is_constructible<
|
||
|
|
Deleter,
|
||
|
|
DeleterForwardingReference&&
|
||
|
|
>::value
|
||
|
|
>* = nullptr
|
||
|
|
)
|
||
|
|
:
|
||
|
|
m_impl(
|
||
|
|
ptr,
|
||
|
|
std::forward<RefCountAccessorForwardingReference>(f),
|
||
|
|
std::forward<DeleterForwardingReference>(d)
|
||
|
|
)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
void assign(pointer ptr) noexcept
|
||
|
|
{
|
||
|
|
m_impl.assign(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <
|
||
|
|
class RefCountAccessorForwardingReference,
|
||
|
|
class DeleterForwardingReference
|
||
|
|
>
|
||
|
|
void assign(
|
||
|
|
pointer ptr,
|
||
|
|
RefCountAccessorForwardingReference&& f,
|
||
|
|
DeleterForwardingReference&& d
|
||
|
|
)
|
||
|
|
{
|
||
|
|
m_impl.assign(
|
||
|
|
ptr,
|
||
|
|
std::forward<RefCountAccessorForwardingReference>(f),
|
||
|
|
std::forward<DeleterForwardingReference>(d)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
void swap(intrusive_ptr_base& other)
|
||
|
|
{
|
||
|
|
m_impl.swap(other.m_impl);
|
||
|
|
}
|
||
|
|
|
||
|
|
struct STDX_MSVC_EMPTY_BASE_CLASSES impl
|
||
|
|
:
|
||
|
|
PointerImplementation,
|
||
|
|
RefCountAccessor,
|
||
|
|
Deleter
|
||
|
|
{
|
||
|
|
constexpr impl() = default;
|
||
|
|
|
||
|
|
explicit impl(pointer ptr) noexcept : PointerImplementation(ptr)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class RefCountAccess,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
std::is_constructible<RefCountAccessor, RefCountAccess&&>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr explicit impl(RefCountAccess&& f)
|
||
|
|
: RefCountAccessor(std::forward<RefCountAccess>(f))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class RefCountAccess,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
std::is_constructible<RefCountAccessor, RefCountAccess&&>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
impl(pointer ptr, RefCountAccess&& f)
|
||
|
|
:
|
||
|
|
PointerImplementation(ptr),
|
||
|
|
RefCountAccessor(std::forward<RefCountAccessor>(f))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class RefCountAccess,
|
||
|
|
class D,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
std::is_constructible<RefCountAccessor, RefCountAccess&&>::value
|
||
|
|
&& std::is_constructible<Deleter, D&&>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr impl(RefCountAccess&& f, D&& d)
|
||
|
|
:
|
||
|
|
RefCountAccessor(std::forward<RefCountAccess>(f)),
|
||
|
|
Deleter(std::forward<D>(d))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class RefCountAccess,
|
||
|
|
class D,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
std::is_constructible<RefCountAccessor, RefCountAccess&&>::value
|
||
|
|
&& std::is_constructible<Deleter, D&&>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
impl(pointer ptr, RefCountAccess&& f, D&& d)
|
||
|
|
:
|
||
|
|
PointerImplementation(ptr),
|
||
|
|
RefCountAccessor(std::forward<RefCountAccess>(f)),
|
||
|
|
Deleter(std::forward<D>(d))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
impl(const impl&) = default;
|
||
|
|
impl& operator = (const impl&) = default;
|
||
|
|
impl(impl&&) = default;
|
||
|
|
impl& operator = (impl&&) = default;
|
||
|
|
|
||
|
|
Deleter& get_deleter() noexcept
|
||
|
|
{
|
||
|
|
return static_cast<Deleter&>(*this);
|
||
|
|
}
|
||
|
|
|
||
|
|
const Deleter& get_deleter() const noexcept
|
||
|
|
{
|
||
|
|
return static_cast<const Deleter&>(*this);
|
||
|
|
}
|
||
|
|
|
||
|
|
void assign(pointer ptr) noexcept
|
||
|
|
{
|
||
|
|
static_cast<PointerImplementation&>(*this).assign(ptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
void assign(std::nullptr_t) noexcept
|
||
|
|
{
|
||
|
|
static_cast<PointerImplementation&>(*this).assign(nullptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
void swap(impl& other)
|
||
|
|
{
|
||
|
|
std::swap(
|
||
|
|
static_cast<PointerImplementation&>(*this),
|
||
|
|
static_cast<PointerImplementation&>(other)
|
||
|
|
);
|
||
|
|
|
||
|
|
std::swap(static_cast<RefCountAccessor&>(*this), static_cast<RefCountAccessor&>(other));
|
||
|
|
std::swap(
|
||
|
|
static_cast<Deleter&>(this->get_deleter()),
|
||
|
|
static_cast<Deleter&>(other.get_deleter())
|
||
|
|
);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
impl m_impl;
|
||
|
|
|
||
|
|
void increment_shared_reference_count(
|
||
|
|
std::memory_order order = std::memory_order_relaxed
|
||
|
|
) const noexcept
|
||
|
|
{
|
||
|
|
if (ptr()) ref_count_func()(ptr()).fetch_add(1, order);
|
||
|
|
}
|
||
|
|
|
||
|
|
void decrement_shared_reference_count() noexcept
|
||
|
|
{
|
||
|
|
if (ptr())
|
||
|
|
{
|
||
|
|
if (ref_count_func()(ptr()).fetch_sub(1, std::memory_order_release) == 1)
|
||
|
|
{
|
||
|
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||
|
|
invoke_deleter(ptr());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ----- accessors and modifiers
|
||
|
|
|
||
|
|
pointer& ptr() noexcept
|
||
|
|
{
|
||
|
|
return static_cast<PointerImplementation&>(m_impl).shared_object;
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr pointer ptr() const noexcept
|
||
|
|
{
|
||
|
|
return static_cast<const PointerImplementation&>(m_impl).shared_object;
|
||
|
|
}
|
||
|
|
|
||
|
|
RefCountAccessor& ref_count_func() noexcept
|
||
|
|
{
|
||
|
|
return static_cast<RefCountAccessor&>(m_impl);
|
||
|
|
}
|
||
|
|
|
||
|
|
const RefCountAccessor& ref_count_func() const noexcept
|
||
|
|
{
|
||
|
|
return static_cast<const RefCountAccessor&>(m_impl);
|
||
|
|
}
|
||
|
|
|
||
|
|
Deleter& deleter() noexcept
|
||
|
|
{
|
||
|
|
return m_impl.get_deleter();
|
||
|
|
}
|
||
|
|
|
||
|
|
const Deleter& deleter() const noexcept
|
||
|
|
{
|
||
|
|
return m_impl.get_deleter();
|
||
|
|
}
|
||
|
|
|
||
|
|
intrusive_ptr<
|
||
|
|
T,
|
||
|
|
RefCountAccessor,
|
||
|
|
Deleter,
|
||
|
|
Pointer
|
||
|
|
> make_intrusive_pointer(pointer p) const noexcept
|
||
|
|
{
|
||
|
|
return intrusive_ptr<T, RefCountAccessor, Deleter, Pointer>{
|
||
|
|
p,
|
||
|
|
ref_count_func(),
|
||
|
|
deleter()
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
|
||
|
|
void invoke_deleter(pointer p)
|
||
|
|
{
|
||
|
|
m_impl.get_deleter()(p);
|
||
|
|
}
|
||
|
|
|
||
|
|
void invoke_deleter(pointer p) const
|
||
|
|
{
|
||
|
|
m_impl.get_deleter()(p);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class WeakReferenceCountDescriptor>
|
||
|
|
void invoke_deleter(pointer p, WeakReferenceCountDescriptor* d)
|
||
|
|
{
|
||
|
|
maybe_delete_shared_object(p, m_impl.get_deleter(), d);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class WeakReferenceCountDescriptor>
|
||
|
|
void invoke_deleter(pointer p, WeakReferenceCountDescriptor* d) const
|
||
|
|
{
|
||
|
|
maybe_delete_shared_object(p, m_impl.get_deleter(), d);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class RefCountAccessor,
|
||
|
|
class Deleter,
|
||
|
|
class Pointer
|
||
|
|
>
|
||
|
|
class STDX_TRIVIALLY_RELOCATABLE intrusive_ptr
|
||
|
|
:
|
||
|
|
public detail::intrusive_ptr_base<
|
||
|
|
T,
|
||
|
|
RefCountAccessor,
|
||
|
|
Deleter,
|
||
|
|
Pointer,
|
||
|
|
detail::pointer_wrapper<Pointer>
|
||
|
|
>
|
||
|
|
{
|
||
|
|
using base_type = detail::intrusive_ptr_base<
|
||
|
|
T,
|
||
|
|
RefCountAccessor,
|
||
|
|
Deleter,
|
||
|
|
Pointer,
|
||
|
|
detail::pointer_wrapper<Pointer>
|
||
|
|
>;
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
using pointer = Pointer;
|
||
|
|
using element_type = T;
|
||
|
|
using ref_count_accessor = RefCountAccessor;
|
||
|
|
using deleter_type = Deleter;
|
||
|
|
using count_type = typename base_type::count_type;
|
||
|
|
|
||
|
|
constexpr intrusive_ptr() noexcept : base_type()
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr intrusive_ptr(std::nullptr_t) noexcept : base_type()
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <class RefCountAccess>
|
||
|
|
constexpr intrusive_ptr(std::nullptr_t, RefCountAccess&& f)
|
||
|
|
: base_type(std::forward<RefCountAccess>(f))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <class RefCountAccess, class D>
|
||
|
|
constexpr intrusive_ptr(std::nullptr_t, RefCountAccess&& f, D&& d)
|
||
|
|
: base_type(std::forward<RefCountAccess>(f), std::forward<D>(d))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr explicit intrusive_ptr(Pointer ptr) noexcept
|
||
|
|
: base_type(ptr)
|
||
|
|
{
|
||
|
|
// reference count must initially be >= 1
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class RefCountAccess>
|
||
|
|
intrusive_ptr(Pointer ptr, RefCountAccess&& f) noexcept
|
||
|
|
: base_type(ptr, std::forward<RefCountAccess>(f))
|
||
|
|
{
|
||
|
|
// reference count must initially be >= 1
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class RefCountAccess, class D>
|
||
|
|
intrusive_ptr(Pointer ptr, RefCountAccess&& f, D&& d) noexcept
|
||
|
|
:
|
||
|
|
base_type(
|
||
|
|
ptr,
|
||
|
|
std::forward<RefCountAccess>(f),
|
||
|
|
std::forward<D>(d)
|
||
|
|
)
|
||
|
|
{
|
||
|
|
// reference count must initially be >= 1
|
||
|
|
}
|
||
|
|
|
||
|
|
// Copy constructor
|
||
|
|
//
|
||
|
|
intrusive_ptr(const intrusive_ptr& rhs) noexcept
|
||
|
|
: base_type(rhs)
|
||
|
|
{
|
||
|
|
this->increment_shared_reference_count();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Converting copy-constructor
|
||
|
|
//
|
||
|
|
template <
|
||
|
|
class Y,
|
||
|
|
class Ptr,
|
||
|
|
class = std::enable_if_t<std::is_convertible<Ptr, pointer>::value>
|
||
|
|
>
|
||
|
|
intrusive_ptr(const intrusive_ptr<Y, RefCountAccessor, Deleter, Ptr>& rhs) noexcept
|
||
|
|
:
|
||
|
|
base_type(
|
||
|
|
rhs.get(),
|
||
|
|
rhs.ref_count_func(),
|
||
|
|
rhs.get_deleter()
|
||
|
|
)
|
||
|
|
{
|
||
|
|
this->increment_shared_reference_count();
|
||
|
|
}
|
||
|
|
|
||
|
|
// Move constructor
|
||
|
|
//
|
||
|
|
intrusive_ptr(intrusive_ptr&& rhs) noexcept
|
||
|
|
: base_type(std::move(rhs))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
// Copy assignment
|
||
|
|
//
|
||
|
|
intrusive_ptr& operator = (const intrusive_ptr& rhs) noexcept
|
||
|
|
{
|
||
|
|
rhs.increment_shared_reference_count();
|
||
|
|
this->decrement_shared_reference_count();
|
||
|
|
|
||
|
|
static_cast<base_type&>(*this) = static_cast<const base_type&>(rhs);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Move assignment
|
||
|
|
//
|
||
|
|
intrusive_ptr& operator = (intrusive_ptr&& rhs) noexcept
|
||
|
|
{
|
||
|
|
if (this != std::addressof(rhs))
|
||
|
|
{
|
||
|
|
this->decrement_shared_reference_count();
|
||
|
|
static_cast<base_type&>(*this) = std::move(static_cast<base_type&>(rhs));
|
||
|
|
}
|
||
|
|
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
~intrusive_ptr() noexcept
|
||
|
|
{
|
||
|
|
this->decrement_shared_reference_count();
|
||
|
|
}
|
||
|
|
|
||
|
|
void reset() noexcept
|
||
|
|
{
|
||
|
|
this->decrement_shared_reference_count();
|
||
|
|
this->assign(nullptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
void reset(std::nullptr_t) noexcept
|
||
|
|
{
|
||
|
|
reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
void reset(Pointer ptr) noexcept
|
||
|
|
{
|
||
|
|
if (this->ptr() != ptr)
|
||
|
|
{
|
||
|
|
this->decrement_shared_reference_count();
|
||
|
|
this->assign(ptr);
|
||
|
|
this->increment_shared_reference_count();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void swap(intrusive_ptr& other) noexcept
|
||
|
|
{
|
||
|
|
if (this->get() != other.get())
|
||
|
|
{
|
||
|
|
base_type::swap(other);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pointer get() const noexcept
|
||
|
|
{
|
||
|
|
return this->ptr();
|
||
|
|
}
|
||
|
|
|
||
|
|
element_type& operator * () const noexcept
|
||
|
|
{
|
||
|
|
return *this->get();
|
||
|
|
}
|
||
|
|
|
||
|
|
pointer operator -> () const noexcept
|
||
|
|
{
|
||
|
|
return this->get();
|
||
|
|
}
|
||
|
|
|
||
|
|
count_type use_count() const noexcept
|
||
|
|
{
|
||
|
|
return this->ref_count_func()(get()).load(std::memory_order_acquire);
|
||
|
|
}
|
||
|
|
|
||
|
|
explicit operator bool() const noexcept
|
||
|
|
{
|
||
|
|
return static_cast<bool>(this->get());
|
||
|
|
}
|
||
|
|
|
||
|
|
Deleter get_deleter() noexcept
|
||
|
|
{
|
||
|
|
return this->deleter();
|
||
|
|
}
|
||
|
|
|
||
|
|
const Deleter& get_deleter() const noexcept
|
||
|
|
{
|
||
|
|
return this->deleter();
|
||
|
|
}
|
||
|
|
|
||
|
|
RefCountAccessor ref_count_access() noexcept
|
||
|
|
{
|
||
|
|
return this->ref_count_func();
|
||
|
|
}
|
||
|
|
|
||
|
|
const RefCountAccessor& ref_count_access() const noexcept
|
||
|
|
{
|
||
|
|
return this->ref_count_func();
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
|
||
|
|
template <class Y, class G, class D, class P>
|
||
|
|
friend class intrusive_ptr;
|
||
|
|
};
|
||
|
|
|
||
|
|
// -------------- Global equality operators
|
||
|
|
//
|
||
|
|
template <class T, class G1, class D1, class P1, class U, class G2, class D2, class P2>
|
||
|
|
bool operator == (
|
||
|
|
const intrusive_ptr<T, G1, D1, P1>& lhs,
|
||
|
|
const intrusive_ptr<U, G2, D2, P2>& rhs
|
||
|
|
) noexcept
|
||
|
|
{
|
||
|
|
return lhs.get() == rhs.get();
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1, class U, class G2, class D2, class P2>
|
||
|
|
bool operator != (
|
||
|
|
const intrusive_ptr<T, G1, D1, P1>& lhs,
|
||
|
|
const intrusive_ptr<U, G2, D2, P2>& rhs
|
||
|
|
) noexcept
|
||
|
|
{
|
||
|
|
return !(lhs == rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1, class U, class G2, class D2, class P2>
|
||
|
|
bool operator < (
|
||
|
|
const intrusive_ptr<T, G1, D1, P1>& lhs,
|
||
|
|
const intrusive_ptr<U, G2, D2, P2>& rhs
|
||
|
|
) noexcept
|
||
|
|
{
|
||
|
|
using pointer1 = typename intrusive_ptr<T, G1, D1, P1>::pointer;
|
||
|
|
using pointer2 = typename intrusive_ptr<U, G2, D2, P2>::pointer;
|
||
|
|
using common_type = typename std::common_type<pointer1, pointer2>::type;
|
||
|
|
|
||
|
|
return std::less<common_type>{}(lhs.get(), rhs.get());
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1, class U, class G2, class D2, class P2>
|
||
|
|
bool operator > (
|
||
|
|
const intrusive_ptr<T, G1, D1, P1>& lhs,
|
||
|
|
const intrusive_ptr<U, G2, D2, P2>& rhs
|
||
|
|
) noexcept
|
||
|
|
{
|
||
|
|
return rhs < lhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1, class U, class G2, class D2, class P2>
|
||
|
|
bool operator <= (
|
||
|
|
const intrusive_ptr<T, G1, D1, P1>& lhs,
|
||
|
|
const intrusive_ptr<U, G2, D2, P2>& rhs
|
||
|
|
) noexcept
|
||
|
|
{
|
||
|
|
return !(rhs < lhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1, class U, class G2, class D2, class P2>
|
||
|
|
bool operator >= (
|
||
|
|
const intrusive_ptr<T, G1, D1, P1>& lhs,
|
||
|
|
const intrusive_ptr<U, G2, D2, P2>& rhs
|
||
|
|
) noexcept
|
||
|
|
{
|
||
|
|
return !(lhs < rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator == (const intrusive_ptr<T, G1, D1, P1>& lhs, std::nullptr_t) noexcept
|
||
|
|
{
|
||
|
|
return !lhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator == (std::nullptr_t, const intrusive_ptr<T, G1, D1, P1>& rhs) noexcept
|
||
|
|
{
|
||
|
|
return !rhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator != (const intrusive_ptr<T, G1, D1, P1>& lhs, std::nullptr_t) noexcept
|
||
|
|
{
|
||
|
|
return bool(lhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator != (std::nullptr_t, const intrusive_ptr<T, G1, D1, P1>& rhs) noexcept
|
||
|
|
{
|
||
|
|
return bool(rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator < (const intrusive_ptr<T, G1, D1, P1>& lhs, std::nullptr_t) noexcept
|
||
|
|
{
|
||
|
|
using pointer = typename intrusive_ptr<T, G1, D1, P1>::pointer;
|
||
|
|
return std::less<pointer>{}(lhs.get(), nullptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator < (std::nullptr_t, const intrusive_ptr<T, G1, D1, P1>& rhs) noexcept
|
||
|
|
{
|
||
|
|
using pointer = typename intrusive_ptr<T, G1, D1, P1>::pointer;
|
||
|
|
return std::less<pointer>{}(nullptr, rhs.get());
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator > (const intrusive_ptr<T, G1, D1, P1>& lhs, std::nullptr_t) noexcept
|
||
|
|
{
|
||
|
|
return (nullptr < lhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator > (std::nullptr_t lhs, const intrusive_ptr<T, G1, D1, P1>& rhs) noexcept
|
||
|
|
{
|
||
|
|
return (rhs < nullptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator <= (const intrusive_ptr<T, G1, D1, P1>& lhs, std::nullptr_t rhs) noexcept
|
||
|
|
{
|
||
|
|
return !(nullptr < lhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator <= (std::nullptr_t lhs, const intrusive_ptr<T, G1, D1, P1>& rhs) noexcept
|
||
|
|
{
|
||
|
|
return !(rhs < nullptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator >= (const intrusive_ptr<T, G1, D1, P1>& lhs, std::nullptr_t rhs) noexcept
|
||
|
|
{
|
||
|
|
return !(lhs < nullptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G1, class D1, class P1>
|
||
|
|
bool operator >= (std::nullptr_t lhs, const intrusive_ptr<T, G1, D1, P1>& rhs) noexcept
|
||
|
|
{
|
||
|
|
return !(nullptr < rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T, class G, class D, class P>
|
||
|
|
void swap(intrusive_ptr<T, G, D, P>& lhs, intrusive_ptr<T, G, D, P>& rhs) noexcept
|
||
|
|
{
|
||
|
|
lhs.swap(rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE
|
||
|
|
template <class Y, class G, class D, class P>
|
||
|
|
struct is_trivially_relocatable<intrusive_ptr<Y,G,D,P>> : std::true_type
|
||
|
|
{ };
|
||
|
|
#endif
|
||
|
|
|
||
|
|
} // end namespace stdx
|
||
|
|
|
||
|
|
#endif // include guard
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#ifndef STDX_STRING_REF_HPP
|
||
|
|
#define STDX_STRING_REF_HPP
|
||
|
|
|
||
|
|
#include <cstring>
|
||
|
|
#include <cstddef>
|
||
|
|
#include <atomic>
|
||
|
|
|
||
|
|
namespace stdx {
|
||
|
|
|
||
|
|
class string_ref;
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
constexpr const char* cstring_null_scan(const char* s) noexcept
|
||
|
|
{
|
||
|
|
return *s ? cstring_null_scan(s + 1) : s;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
class string_ref
|
||
|
|
{
|
||
|
|
protected:
|
||
|
|
|
||
|
|
struct state_type;
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
using value_type = const char;
|
||
|
|
using size_type = std::size_t;
|
||
|
|
using pointer = const char*;
|
||
|
|
using const_pointer = const char*;
|
||
|
|
using iterator = const char*;
|
||
|
|
using const_iterator = const char*;
|
||
|
|
|
||
|
|
struct resource_management
|
||
|
|
{
|
||
|
|
using copy_constructor = state_type(*)(const string_ref&);
|
||
|
|
using move_constructor = state_type(*)(string_ref&&);
|
||
|
|
using destructor = void(*)(string_ref&);
|
||
|
|
|
||
|
|
constexpr resource_management() noexcept
|
||
|
|
: copy{nullptr}, move{nullptr}, destroy{nullptr}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr resource_management(
|
||
|
|
copy_constructor cctor,
|
||
|
|
move_constructor mctor,
|
||
|
|
destructor dtor
|
||
|
|
) noexcept
|
||
|
|
: copy{cctor}, move{mctor}, destroy{dtor}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
copy_constructor copy;
|
||
|
|
move_constructor move;
|
||
|
|
destructor destroy;
|
||
|
|
};
|
||
|
|
|
||
|
|
constexpr string_ref() noexcept : m_begin(nullptr), m_end(nullptr), context{}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr string_ref(const char* beg) noexcept
|
||
|
|
: m_begin(beg), m_end(detail::cstring_null_scan(beg)), context{}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr string_ref(const char* beg, const char* e) noexcept
|
||
|
|
: m_begin(beg), m_end(e), context{}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr string_ref(const char* beg, resource_management rm) noexcept
|
||
|
|
:
|
||
|
|
m_begin(beg),
|
||
|
|
m_end(detail::cstring_null_scan(beg)),
|
||
|
|
m_resource_management(rm),
|
||
|
|
context{}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr string_ref(const char* beg, const char* e, resource_management rm) noexcept
|
||
|
|
: m_begin(beg), m_end(e), m_resource_management(rm), context{}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr string_ref(
|
||
|
|
const char* beg,
|
||
|
|
const char* e,
|
||
|
|
resource_management rm,
|
||
|
|
void* ctx
|
||
|
|
) noexcept
|
||
|
|
: m_begin(beg), m_end(e), m_resource_management(rm), context{ctx}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
STDX_GCC7_WORKAROUND_CONSTEXPR string_ref(const string_ref& s)
|
||
|
|
: string_ref{s.m_resource_management.copy ? s.m_resource_management.copy(s) : s.state()}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
STDX_GCC7_WORKAROUND_CONSTEXPR string_ref(string_ref&& s)
|
||
|
|
:
|
||
|
|
string_ref{
|
||
|
|
s.m_resource_management.move ?
|
||
|
|
s.m_resource_management.move(std::move(s)) : s.state()
|
||
|
|
}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
string_ref& operator = (const string_ref& s)
|
||
|
|
{
|
||
|
|
string_ref tmp = s;
|
||
|
|
*this = std::move(tmp);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
string_ref& operator = (string_ref&& s)
|
||
|
|
{
|
||
|
|
if (this != &s)
|
||
|
|
{
|
||
|
|
if (m_resource_management.destroy) m_resource_management.destroy(*this);
|
||
|
|
|
||
|
|
// This is legal because of the common initial sequence and the fact
|
||
|
|
// that any type erased object must be trivially relocatable.
|
||
|
|
*this = string_ref_state_union{std::move(s)}.state;
|
||
|
|
}
|
||
|
|
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
~string_ref() noexcept
|
||
|
|
{
|
||
|
|
if (m_resource_management.destroy) m_resource_management.destroy(*this);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool empty() const noexcept { return m_begin == m_end; }
|
||
|
|
|
||
|
|
size_type size() const noexcept { return m_end - m_begin; }
|
||
|
|
|
||
|
|
const_pointer data() const noexcept { return m_begin; }
|
||
|
|
|
||
|
|
iterator begin() noexcept { return m_begin; }
|
||
|
|
|
||
|
|
iterator end() noexcept { return m_end; }
|
||
|
|
|
||
|
|
const_iterator begin() const noexcept { return m_begin; }
|
||
|
|
|
||
|
|
const_iterator end() const noexcept { return m_end; }
|
||
|
|
|
||
|
|
const_iterator cbegin() const noexcept { return m_begin; }
|
||
|
|
|
||
|
|
const_iterator cend() const noexcept { return m_end; }
|
||
|
|
|
||
|
|
protected:
|
||
|
|
|
||
|
|
struct state_type
|
||
|
|
{
|
||
|
|
pointer m_begin;
|
||
|
|
pointer m_end;
|
||
|
|
resource_management m_resource_management;
|
||
|
|
void* context;
|
||
|
|
};
|
||
|
|
|
||
|
|
state_type state() const noexcept
|
||
|
|
{
|
||
|
|
return state_type{m_begin, m_end, m_resource_management, context};
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr explicit string_ref(const state_type& s) noexcept
|
||
|
|
:
|
||
|
|
m_begin(s.m_begin),
|
||
|
|
m_end(s.m_end),
|
||
|
|
m_resource_management(s.m_resource_management),
|
||
|
|
context(s.context)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
void clear() noexcept
|
||
|
|
{
|
||
|
|
m_begin = nullptr;
|
||
|
|
m_end = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class StringRef>
|
||
|
|
union string_ref_state_union_type
|
||
|
|
{
|
||
|
|
explicit string_ref_state_union_type(StringRef&& s) : str(std::move(s))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
~string_ref_state_union_type() noexcept {}
|
||
|
|
|
||
|
|
StringRef str;
|
||
|
|
state_type state;
|
||
|
|
};
|
||
|
|
|
||
|
|
using string_ref_state_union = string_ref_state_union_type<string_ref>;
|
||
|
|
|
||
|
|
void operator = (const state_type& s) noexcept
|
||
|
|
{
|
||
|
|
m_begin = s.m_begin;
|
||
|
|
m_end = s.m_end;
|
||
|
|
m_resource_management = s.m_resource_management;
|
||
|
|
context = s.context;
|
||
|
|
}
|
||
|
|
|
||
|
|
pointer m_begin;
|
||
|
|
pointer m_end;
|
||
|
|
resource_management m_resource_management;
|
||
|
|
void* context;
|
||
|
|
};
|
||
|
|
|
||
|
|
inline bool operator == (const string_ref& lhs, const string_ref& rhs) noexcept
|
||
|
|
{
|
||
|
|
return (lhs.size() == rhs.size()) && (std::memcmp(lhs.data(), rhs.data(), lhs.size()) == 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
inline bool operator != (const string_ref& lhs, const string_ref& rhs) noexcept
|
||
|
|
{
|
||
|
|
return !(lhs == rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
inline bool operator < (const string_ref& lhs, const string_ref& rhs) noexcept
|
||
|
|
{
|
||
|
|
const std::size_t sz = (lhs.size() < rhs.size()) ? lhs.size() : rhs.size();
|
||
|
|
int result = std::memcmp(lhs.data(), rhs.data(), sz);
|
||
|
|
if (result == 0) return lhs.size() < rhs.size();
|
||
|
|
return result < 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline bool operator > (const string_ref& lhs, const string_ref& rhs) noexcept
|
||
|
|
{
|
||
|
|
return rhs < lhs;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline bool operator <= (const string_ref& lhs, const string_ref& rhs) noexcept
|
||
|
|
{
|
||
|
|
return !(lhs > rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
inline bool operator >= (const string_ref& lhs, const string_ref& rhs) noexcept
|
||
|
|
{
|
||
|
|
return !(lhs < rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reference-counted allocated string
|
||
|
|
//
|
||
|
|
class shared_string_ref : public string_ref
|
||
|
|
{
|
||
|
|
struct string_arena_base
|
||
|
|
{
|
||
|
|
mutable std::atomic<std::size_t> ref_count;
|
||
|
|
std::size_t length;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct string_arena : string_arena_base
|
||
|
|
{
|
||
|
|
constexpr explicit string_arena(std::size_t length) noexcept
|
||
|
|
: string_arena_base{{1}, length}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr static std::size_t header_size() noexcept
|
||
|
|
{
|
||
|
|
return sizeof(string_arena);
|
||
|
|
}
|
||
|
|
|
||
|
|
char* data() noexcept
|
||
|
|
{
|
||
|
|
return reinterpret_cast<char*>(this) + header_size();
|
||
|
|
}
|
||
|
|
|
||
|
|
const char* data() const noexcept
|
||
|
|
{
|
||
|
|
return reinterpret_cast<const char*>(this) + header_size();
|
||
|
|
}
|
||
|
|
|
||
|
|
const char* begin() const noexcept { return data(); }
|
||
|
|
|
||
|
|
const char* end() const noexcept { return data() + length; }
|
||
|
|
};
|
||
|
|
|
||
|
|
string_ref allocate_string_ref(const char* s, std::size_t length)
|
||
|
|
{
|
||
|
|
const std::size_t arena_size = string_arena::header_size() + length;
|
||
|
|
char* buf = static_cast<char*>(::operator new(arena_size));
|
||
|
|
string_arena* a = new (buf) string_arena{length};
|
||
|
|
std::memcpy(a->data(), s, length);
|
||
|
|
return shared_string_ref{a};
|
||
|
|
}
|
||
|
|
|
||
|
|
explicit shared_string_ref(string_arena* a) noexcept
|
||
|
|
:
|
||
|
|
string_ref{
|
||
|
|
a->begin(),
|
||
|
|
a->end(),
|
||
|
|
string_ref::resource_management{©_construct, &move_construct, &destroy},
|
||
|
|
a
|
||
|
|
}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
const string_arena_base* get_arena() const noexcept
|
||
|
|
{
|
||
|
|
return static_cast<string_arena_base*>(this->context);
|
||
|
|
}
|
||
|
|
|
||
|
|
string_arena_base* get_arena() noexcept
|
||
|
|
{
|
||
|
|
return static_cast<string_arena_base*>(this->context);
|
||
|
|
}
|
||
|
|
|
||
|
|
static string_ref::state_type copy_construct(const string_ref& base) noexcept
|
||
|
|
{
|
||
|
|
const shared_string_ref& s = static_cast<const shared_string_ref&>(base);
|
||
|
|
const string_arena_base* a = s.get_arena();
|
||
|
|
if (a) a->ref_count.fetch_add(1, std::memory_order_relaxed);
|
||
|
|
return s.state();
|
||
|
|
}
|
||
|
|
|
||
|
|
static string_ref::state_type move_construct(string_ref&& base) noexcept
|
||
|
|
{
|
||
|
|
shared_string_ref& s = static_cast<shared_string_ref&>(base);
|
||
|
|
auto st = s.state();
|
||
|
|
s.context = nullptr;
|
||
|
|
s.clear();
|
||
|
|
return st;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void destroy(string_ref& base) noexcept
|
||
|
|
{
|
||
|
|
shared_string_ref& s = static_cast<shared_string_ref&>(base);
|
||
|
|
string_arena* a = static_cast<string_arena*>(s.get_arena());
|
||
|
|
if (a && (a->ref_count.fetch_sub(1, std::memory_order_release) == 1))
|
||
|
|
{
|
||
|
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||
|
|
::operator delete(a);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class Allocator>
|
||
|
|
struct allocated_string_arena : string_arena_base
|
||
|
|
{
|
||
|
|
constexpr allocated_string_arena(const Allocator& alloc, std::size_t length) noexcept
|
||
|
|
: string_arena_base{{1}, length}, allocator(alloc)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr static std::size_t header_size() noexcept
|
||
|
|
{
|
||
|
|
return sizeof(allocated_string_arena);
|
||
|
|
}
|
||
|
|
|
||
|
|
char* data() noexcept
|
||
|
|
{
|
||
|
|
return reinterpret_cast<char*>(this) + header_size();
|
||
|
|
}
|
||
|
|
|
||
|
|
const char* data() const noexcept
|
||
|
|
{
|
||
|
|
return reinterpret_cast<const char*>(this) + header_size();
|
||
|
|
}
|
||
|
|
|
||
|
|
const char* begin() const noexcept { return data(); }
|
||
|
|
|
||
|
|
const char* end() const noexcept { return data() + length; }
|
||
|
|
|
||
|
|
std::size_t allocated_size() const noexcept { return header_size() + length; }
|
||
|
|
|
||
|
|
Allocator allocator;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <class Allocator>
|
||
|
|
explicit shared_string_ref(allocated_string_arena<Allocator>* a) noexcept
|
||
|
|
:
|
||
|
|
string_ref{
|
||
|
|
a->begin(),
|
||
|
|
a->end(),
|
||
|
|
string_ref::resource_management{
|
||
|
|
©_construct,
|
||
|
|
&move_construct,
|
||
|
|
&allocator_destroy<Allocator>
|
||
|
|
},
|
||
|
|
a
|
||
|
|
}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <class Allocator>
|
||
|
|
string_ref allocate_string_ref(
|
||
|
|
const Allocator& allocator,
|
||
|
|
const char* s,
|
||
|
|
std::size_t length
|
||
|
|
)
|
||
|
|
{
|
||
|
|
using allocator_type = typename std::allocator_traits<
|
||
|
|
Allocator
|
||
|
|
>::template rebind_alloc<char>;
|
||
|
|
using arena_type = allocated_string_arena<allocator_type>;
|
||
|
|
|
||
|
|
allocator_type alloc{allocator};
|
||
|
|
const std::size_t arena_size = arena_type::header_size() + length;
|
||
|
|
char* buf = alloc.allocate(arena_size);
|
||
|
|
arena_type* a = new (buf) arena_type{alloc, length};
|
||
|
|
std::memcpy(a->data(), s, length);
|
||
|
|
return shared_string_ref{a};
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class Allocator>
|
||
|
|
static void allocator_destroy(string_ref& base) noexcept
|
||
|
|
{
|
||
|
|
using arena_type = allocated_string_arena<Allocator>;
|
||
|
|
|
||
|
|
shared_string_ref& s = static_cast<shared_string_ref&>(base);
|
||
|
|
arena_type* a = static_cast<arena_type*>(s.get_arena());
|
||
|
|
if (a && (a->ref_count.fetch_sub(1, std::memory_order_release) == 1))
|
||
|
|
{
|
||
|
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||
|
|
|
||
|
|
Allocator alloc = std::move(a->allocator);
|
||
|
|
const std::size_t allocated_size = a->allocated_size();
|
||
|
|
a->~arena_type();
|
||
|
|
alloc.deallocate(reinterpret_cast<char*>(a), allocated_size);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
shared_string_ref(const char* beg)
|
||
|
|
: string_ref{allocate_string_ref(beg, detail::cstring_null_scan(beg) - beg)}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
shared_string_ref(const char* beg, const char* end)
|
||
|
|
: string_ref{allocate_string_ref(beg, end - beg)}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <class Allocator>
|
||
|
|
shared_string_ref(const Allocator& alloc, const char* beg)
|
||
|
|
: string_ref{allocate_string_ref(alloc, beg, detail::cstring_null_scan(beg) - beg)}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <class Allocator>
|
||
|
|
shared_string_ref(const Allocator& alloc, const char* beg, const char* end)
|
||
|
|
: string_ref{allocate_string_ref(alloc, beg, end - beg)}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
std::size_t use_count() const noexcept
|
||
|
|
{
|
||
|
|
const string_arena_base* a = get_arena();
|
||
|
|
return a ? a->ref_count.load(std::memory_order_acquire) : 0;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
} // end namespace stdx
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#ifndef STDX_LAUNDER_HPP
|
||
|
|
#define STDX_LAUNDER_HPP
|
||
|
|
|
||
|
|
#include <new>
|
||
|
|
|
||
|
|
|
||
|
|
namespace stdx {
|
||
|
|
|
||
|
|
#if __cplusplus >= 201703L
|
||
|
|
#if defined(__cpp_lib_launder)
|
||
|
|
#define STDX_HAVE_NATIVE_LAUNDER 1
|
||
|
|
using std::launder;
|
||
|
|
#elif defined(STDX_CLANG_COMPILER)
|
||
|
|
#if __has_builtin(__builtin_launder)
|
||
|
|
#define STDX_HAVE_NATIVE_LAUNDER 1
|
||
|
|
template <class T>
|
||
|
|
constexpr T* launder(T* p) noexcept
|
||
|
|
{
|
||
|
|
return __builtin_launder(p);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
#endif
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if !defined(STDX_HAVE_NATIVE_LAUNDER)
|
||
|
|
template <class T>
|
||
|
|
constexpr T* launder(T* p) noexcept
|
||
|
|
{
|
||
|
|
return p;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
} // end namespace stdx
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#ifndef STDX_ERROR_HPP
|
||
|
|
#define STDX_ERROR_HPP
|
||
|
|
|
||
|
|
#include <exception>
|
||
|
|
#include <stdexcept>
|
||
|
|
#include <system_error>
|
||
|
|
#include <memory>
|
||
|
|
#include <cassert>
|
||
|
|
|
||
|
|
|
||
|
|
namespace stdx {
|
||
|
|
|
||
|
|
class error;
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
template <class... Args>
|
||
|
|
struct error_ctor_args;
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
} // end namespace stdx
|
||
|
|
|
||
|
|
namespace stdx_adl {
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
template <class Args, class Enable = void>
|
||
|
|
struct can_use_adl_to_call_make_error : std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
inline void make_error() noexcept { }
|
||
|
|
|
||
|
|
template <class... Args>
|
||
|
|
struct can_use_adl_to_call_make_error<
|
||
|
|
stdx::detail::error_ctor_args<Args...>,
|
||
|
|
std::enable_if_t<
|
||
|
|
std::is_same<
|
||
|
|
decltype(make_error(std::declval<Args>()...)),
|
||
|
|
stdx::error
|
||
|
|
>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
: std::true_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class... Args>
|
||
|
|
constexpr auto construct_error_from_adl(Args&&... args) noexcept(
|
||
|
|
noexcept(make_error(std::forward<Args>(args)...))
|
||
|
|
)
|
||
|
|
{
|
||
|
|
return make_error(std::forward<Args>(args)...);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
} // end namespace stdx_adl
|
||
|
|
|
||
|
|
namespace stdx {
|
||
|
|
|
||
|
|
enum class dynamic_exception_errc
|
||
|
|
{
|
||
|
|
runtime_error = 1,
|
||
|
|
domain_error,
|
||
|
|
invalid_argument,
|
||
|
|
length_error,
|
||
|
|
out_of_range,
|
||
|
|
logic_error,
|
||
|
|
range_error,
|
||
|
|
overflow_error,
|
||
|
|
underflow_error,
|
||
|
|
bad_alloc,
|
||
|
|
bad_array_new_length,
|
||
|
|
bad_optional_access,
|
||
|
|
bad_typeid,
|
||
|
|
bad_any_cast,
|
||
|
|
bad_cast,
|
||
|
|
bad_weak_ptr,
|
||
|
|
bad_function_call,
|
||
|
|
bad_exception,
|
||
|
|
bad_variant_access,
|
||
|
|
unspecified_exception
|
||
|
|
};
|
||
|
|
|
||
|
|
std::error_code error_code_from_exception(std::exception_ptr eptr) noexcept;
|
||
|
|
|
||
|
|
// -------------------- error_traits
|
||
|
|
//
|
||
|
|
template <class E>
|
||
|
|
struct error_traits
|
||
|
|
{ };
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
template <class E, class Enable = void>
|
||
|
|
struct is_convertible_from_exception_using_traits : std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class E>
|
||
|
|
struct is_convertible_from_exception_using_traits<
|
||
|
|
E,
|
||
|
|
std::enable_if_t<
|
||
|
|
std::is_convertible<
|
||
|
|
decltype(error_traits<E>::from_exception(std::declval<std::exception_ptr>())),
|
||
|
|
E
|
||
|
|
>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
: std::true_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class E, class Enable = void>
|
||
|
|
struct is_convertible_to_exception_using_traits : std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class E>
|
||
|
|
struct is_convertible_to_exception_using_traits<
|
||
|
|
E,
|
||
|
|
std::enable_if_t<
|
||
|
|
std::is_convertible<
|
||
|
|
decltype(error_traits<E>::to_exception(std::declval<E>())),
|
||
|
|
std::exception_ptr
|
||
|
|
>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
: std::true_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class E>
|
||
|
|
E from_exception_impl(std::exception_ptr e, std::is_convertible<std::exception_ptr, E>) noexcept
|
||
|
|
{
|
||
|
|
return e;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class E>
|
||
|
|
E from_exception_impl(
|
||
|
|
std::exception_ptr e,
|
||
|
|
is_convertible_from_exception_using_traits<E>
|
||
|
|
) noexcept
|
||
|
|
{
|
||
|
|
return error_traits<E>::from_exception(std::move(e));
|
||
|
|
}
|
||
|
|
|
||
|
|
void from_exception_impl(std::exception_ptr, sentinel_type) = delete;
|
||
|
|
|
||
|
|
template <class E, class G>
|
||
|
|
std::exception_ptr to_exception_impl(E&& e, std::is_convertible<G, std::exception_ptr>) noexcept
|
||
|
|
{
|
||
|
|
return std::forward<E>(e);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class E, class G>
|
||
|
|
std::exception_ptr to_exception_impl(
|
||
|
|
E&& e,
|
||
|
|
is_convertible_to_exception_using_traits<G>
|
||
|
|
) noexcept
|
||
|
|
{
|
||
|
|
return error_traits<E>::to_exception(std::forward<E>(e));
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class E>
|
||
|
|
void to_exception_impl(const E&, sentinel_type) = delete;
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
template <class E>
|
||
|
|
E from_exception(std::exception_ptr e) noexcept
|
||
|
|
{
|
||
|
|
return detail::from_exception_impl<E>(
|
||
|
|
std::move(e),
|
||
|
|
disjunction<
|
||
|
|
std::is_convertible<std::exception_ptr, E>,
|
||
|
|
detail::is_convertible_from_exception_using_traits<E>,
|
||
|
|
sentinel_type
|
||
|
|
>{}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class E>
|
||
|
|
std::exception_ptr to_exception(E&& e) noexcept
|
||
|
|
{
|
||
|
|
return detail::to_exception_impl(
|
||
|
|
std::forward<E>(e),
|
||
|
|
disjunction<
|
||
|
|
std::is_convertible<std::decay_t<E>, std::exception_ptr>,
|
||
|
|
detail::is_convertible_to_exception_using_traits<std::decay_t<E>>,
|
||
|
|
sentinel_type
|
||
|
|
>{}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
struct error_domain_id
|
||
|
|
{
|
||
|
|
constexpr error_domain_id(std::uint64_t l, std::uint64_t h) noexcept
|
||
|
|
: lo(l), hi(h)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
private:
|
||
|
|
|
||
|
|
friend constexpr bool operator == (const error_domain_id&, const error_domain_id&) noexcept;
|
||
|
|
|
||
|
|
std::uint64_t lo;
|
||
|
|
std::uint64_t hi;
|
||
|
|
};
|
||
|
|
|
||
|
|
constexpr bool operator == (const error_domain_id& lhs, const error_domain_id& rhs) noexcept
|
||
|
|
{
|
||
|
|
return (lhs.lo == rhs.lo) && (lhs.hi == rhs.hi);
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr bool operator != (const error_domain_id& lhs, const error_domain_id& rhs) noexcept
|
||
|
|
{
|
||
|
|
return !(lhs == rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T = void>
|
||
|
|
struct error_value;
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
struct is_error_value : std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
struct is_error_value<error_value<T>> : std::true_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
struct error_resource_management
|
||
|
|
{
|
||
|
|
using copy_constructor = error_value<void>(*)(const error&);
|
||
|
|
using move_constructor = error_value<void>(*)(error&&);
|
||
|
|
using destructor = void(*)(error&);
|
||
|
|
|
||
|
|
constexpr error_resource_management() noexcept
|
||
|
|
: copy{nullptr}, move{nullptr}, destroy{nullptr}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr error_resource_management(
|
||
|
|
copy_constructor cctor,
|
||
|
|
move_constructor mctor,
|
||
|
|
destructor dtor
|
||
|
|
) noexcept
|
||
|
|
: copy(cctor), move(mctor), destroy(dtor)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
copy_constructor copy;
|
||
|
|
move_constructor move;
|
||
|
|
destructor destroy;
|
||
|
|
};
|
||
|
|
|
||
|
|
class error_domain
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
|
||
|
|
virtual string_ref name() const noexcept = 0;
|
||
|
|
virtual bool equivalent(const error& lhs, const error& rhs) const noexcept = 0;
|
||
|
|
virtual string_ref message(const error&) const noexcept = 0;
|
||
|
|
|
||
|
|
virtual void throw_exception(const error& e) const;
|
||
|
|
|
||
|
|
friend class error;
|
||
|
|
friend constexpr bool operator == (const error_domain&, const error_domain&) noexcept;
|
||
|
|
friend constexpr bool operator != (const error_domain&, const error_domain&) noexcept;
|
||
|
|
|
||
|
|
constexpr error_domain_id id() const noexcept
|
||
|
|
{
|
||
|
|
return m_id;
|
||
|
|
}
|
||
|
|
|
||
|
|
protected:
|
||
|
|
|
||
|
|
constexpr explicit error_domain(error_domain_id id) noexcept
|
||
|
|
:
|
||
|
|
m_id{id},
|
||
|
|
m_resource_management{}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr error_domain(error_domain_id id, error_resource_management erm) noexcept
|
||
|
|
:
|
||
|
|
m_id{id},
|
||
|
|
m_resource_management{erm}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
error_domain(const error_domain &) = default;
|
||
|
|
error_domain(error_domain &&) = default;
|
||
|
|
error_domain &operator = (const error_domain &) = default;
|
||
|
|
error_domain &operator = (error_domain&&) = default;
|
||
|
|
~error_domain() = default;
|
||
|
|
|
||
|
|
template <
|
||
|
|
class E = error,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
std::is_convertible<const E&, error>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr dependent_type_t<E, error_value<>> copy(const E& e) const
|
||
|
|
{
|
||
|
|
return m_resource_management.copy ? m_resource_management.copy(e) : error_value<>{e.m_value};
|
||
|
|
}
|
||
|
|
|
||
|
|
template <
|
||
|
|
class E = error,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
std::is_rvalue_reference<E&&>::value
|
||
|
|
&& std::is_convertible<E&&, error>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr dependent_type_t<remove_cvref_t<E>, error_value<>> move(E&& e) const
|
||
|
|
{
|
||
|
|
return m_resource_management.move ?
|
||
|
|
m_resource_management.move(static_cast<E&&>(e)) : error_value<>{e.m_value};
|
||
|
|
}
|
||
|
|
|
||
|
|
void destroy(error& e) const noexcept
|
||
|
|
{
|
||
|
|
if (m_resource_management.destroy) m_resource_management.destroy(e);
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
|
||
|
|
error_domain_id m_id;
|
||
|
|
error_resource_management m_resource_management;
|
||
|
|
};
|
||
|
|
|
||
|
|
constexpr bool operator == (const error_domain& lhs, const error_domain& rhs) noexcept
|
||
|
|
{
|
||
|
|
return lhs.id() == rhs.id();
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr bool operator != (const error_domain& lhs, const error_domain& rhs) noexcept
|
||
|
|
{
|
||
|
|
return lhs.id() != rhs.id();
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
template <class ErasedType, class T>
|
||
|
|
struct error_type_is_erasable
|
||
|
|
:
|
||
|
|
bool_constant<
|
||
|
|
is_trivially_relocatable<T>::value
|
||
|
|
&& (sizeof(T) <= sizeof(ErasedType))
|
||
|
|
&& (alignof(T) <= alignof(ErasedType))
|
||
|
|
>
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class ErasedType>
|
||
|
|
struct error_type_is_erasable<ErasedType, void> : std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class ErasedType>
|
||
|
|
struct error_type_is_erasable<ErasedType, const void> : std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
using can_use_static_cast = bool_constant<
|
||
|
|
std::is_integral<T>::value
|
||
|
|
|| std::is_enum<T>::value
|
||
|
|
>;
|
||
|
|
|
||
|
|
struct erased_error
|
||
|
|
{
|
||
|
|
using integral_type = std::intptr_t;
|
||
|
|
using storage_type = std::aligned_storage_t<sizeof(integral_type), alignof(integral_type)>;
|
||
|
|
|
||
|
|
constexpr erased_error() noexcept : code{}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
error_type_is_erasable<integral_type, T>::value
|
||
|
|
&& can_use_static_cast<T>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr erased_error(T value) noexcept
|
||
|
|
: code{static_cast<integral_type>(value)}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
error_type_is_erasable<integral_type, T>::value
|
||
|
|
&& !can_use_static_cast<T>::value
|
||
|
|
&& is_bit_castable<T, integral_type>::value
|
||
|
|
>,
|
||
|
|
class = void
|
||
|
|
>
|
||
|
|
constexpr erased_error(T value) noexcept
|
||
|
|
: code{bit_cast<integral_type>(value)}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
error_type_is_erasable<integral_type, T>::value
|
||
|
|
&& !can_use_static_cast<T>::value
|
||
|
|
&& !is_bit_castable<T, integral_type>::value
|
||
|
|
>,
|
||
|
|
int = 0
|
||
|
|
>
|
||
|
|
erased_error(T value) noexcept(std::is_nothrow_move_constructible<T>::value)
|
||
|
|
{
|
||
|
|
new (&storage) T(std::move(value));
|
||
|
|
}
|
||
|
|
|
||
|
|
union
|
||
|
|
{
|
||
|
|
integral_type code;
|
||
|
|
storage_type storage;
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
error_type_is_erasable<erased_error::integral_type, T>::value
|
||
|
|
&& can_use_static_cast<T>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr T error_cast_impl(erased_error e) noexcept
|
||
|
|
{
|
||
|
|
return static_cast<T>(e.code);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
error_type_is_erasable<erased_error::integral_type, T>::value
|
||
|
|
&& !can_use_static_cast<T>::value
|
||
|
|
&& is_bit_castable<erased_error::integral_type, T>::value
|
||
|
|
>,
|
||
|
|
class = void
|
||
|
|
>
|
||
|
|
constexpr T error_cast_impl(erased_error e) noexcept
|
||
|
|
{
|
||
|
|
return bit_cast<T>(e.code);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
error_type_is_erasable<erased_error::integral_type, T>::value
|
||
|
|
&& !can_use_static_cast<T>::value
|
||
|
|
&& !is_bit_castable<erased_error::integral_type, T>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr T error_cast_impl(erased_error&& e) noexcept(
|
||
|
|
std::is_nothrow_move_constructible<T>::value
|
||
|
|
)
|
||
|
|
{
|
||
|
|
return std::move(*stdx::launder(reinterpret_cast<T*>(&e.storage)));
|
||
|
|
}
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
error_type_is_erasable<erased_error::integral_type, T>::value
|
||
|
|
&& !can_use_static_cast<T>::value
|
||
|
|
&& !is_bit_castable<erased_error::integral_type, T>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr T error_cast_impl(const erased_error& e) noexcept(
|
||
|
|
std::is_nothrow_copy_constructible<T>::value
|
||
|
|
)
|
||
|
|
{
|
||
|
|
return *stdx::launder(reinterpret_cast<const T*>(&e.storage));
|
||
|
|
}
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
struct error_value
|
||
|
|
{
|
||
|
|
using value_type = T;
|
||
|
|
|
||
|
|
constexpr error_value(const T& v) noexcept(
|
||
|
|
std::is_nothrow_copy_constructible<T>::value
|
||
|
|
)
|
||
|
|
: m_value(v)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr error_value(T&& v) noexcept(
|
||
|
|
std::is_nothrow_move_constructible<T>::value
|
||
|
|
)
|
||
|
|
: m_value(std::move(v))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr const T& value() const & noexcept
|
||
|
|
{
|
||
|
|
return m_value;
|
||
|
|
}
|
||
|
|
|
||
|
|
STDX_LEGACY_CONSTEXPR T& value() & noexcept
|
||
|
|
{
|
||
|
|
return m_value;
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr const T&& value() const && noexcept
|
||
|
|
{
|
||
|
|
return static_cast<const T&&>(m_value);
|
||
|
|
}
|
||
|
|
|
||
|
|
STDX_LEGACY_CONSTEXPR T&& value() && noexcept
|
||
|
|
{
|
||
|
|
return static_cast<T&&>(m_value);
|
||
|
|
}
|
||
|
|
|
||
|
|
T m_value;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <>
|
||
|
|
struct error_value<void>
|
||
|
|
{
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
!std::is_same<remove_cvref_t<T>, error_value>::value
|
||
|
|
&& !detail::is_error_value<remove_cvref_t<T>>::value
|
||
|
|
&& std::is_constructible<detail::erased_error, T&&>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr error_value(T&& v) noexcept(
|
||
|
|
std::is_nothrow_constructible<detail::erased_error, T&&>::value
|
||
|
|
)
|
||
|
|
: m_value(std::forward<T>(v))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
std::is_constructible<detail::erased_error, const T&>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr error_value(const error_value<T>& v) noexcept(
|
||
|
|
std::is_nothrow_constructible<detail::erased_error, const T&>::value
|
||
|
|
)
|
||
|
|
: error_value(v.value())
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
std::is_constructible<detail::erased_error, T&&>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr error_value(error_value<T>&& v) noexcept(
|
||
|
|
std::is_nothrow_constructible<detail::erased_error, T&&>::value
|
||
|
|
)
|
||
|
|
: error_value(std::move(v.value()))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
friend class error;
|
||
|
|
|
||
|
|
private:
|
||
|
|
|
||
|
|
detail::erased_error m_value;
|
||
|
|
};
|
||
|
|
|
||
|
|
class error;
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
struct error_copy_construct_t {};
|
||
|
|
struct error_move_construct_t {};
|
||
|
|
|
||
|
|
template <class... Args>
|
||
|
|
struct error_ctor_args {};
|
||
|
|
|
||
|
|
template <class Args, class DecayedArgs>
|
||
|
|
struct can_construct_error_from_adl
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class... Args, class... DecayedArgs>
|
||
|
|
struct can_construct_error_from_adl<error_ctor_args<Args...>, error_ctor_args<DecayedArgs...>>
|
||
|
|
: stdx_adl::detail::can_use_adl_to_call_make_error<error_ctor_args<Args...>>
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <>
|
||
|
|
struct can_construct_error_from_adl<error_ctor_args<>, error_ctor_args<>> : std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class Error>
|
||
|
|
struct can_construct_error_from_adl<error_ctor_args<Error>, error_ctor_args<error>>
|
||
|
|
: std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class E, class D, class T, class ErrorDomain>
|
||
|
|
struct can_construct_error_from_adl<
|
||
|
|
error_ctor_args<E, D>,
|
||
|
|
error_ctor_args<error_value<T>, ErrorDomain>
|
||
|
|
>
|
||
|
|
: std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class CC, class V, class ED, class ErrorDomain>
|
||
|
|
struct can_construct_error_from_adl<
|
||
|
|
error_ctor_args<CC, V, ED>,
|
||
|
|
error_ctor_args<error_copy_construct_t, error_value<>, ErrorDomain>
|
||
|
|
>
|
||
|
|
: std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class MC, class V, class ED, class ErrorDomain>
|
||
|
|
struct can_construct_error_from_adl<
|
||
|
|
error_ctor_args<MC, V, ED>,
|
||
|
|
error_ctor_args<error_move_construct_t, error_value<>, ErrorDomain>
|
||
|
|
>
|
||
|
|
: std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class E, class Enable = void>
|
||
|
|
struct is_convertible_to_error_using_traits : std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <>
|
||
|
|
struct is_convertible_to_error_using_traits<error_ctor_args<error>>
|
||
|
|
: std::false_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class E>
|
||
|
|
struct is_convertible_to_error_using_traits<
|
||
|
|
error_ctor_args<E>,
|
||
|
|
std::enable_if_t<
|
||
|
|
std::is_same<
|
||
|
|
decltype(error_traits<E>::to_error(std::declval<E>())),
|
||
|
|
error
|
||
|
|
>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
: std::true_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
template <class E, class... Args>
|
||
|
|
constexpr auto construct_error_impl(
|
||
|
|
is_convertible_to_error_using_traits<error_ctor_args<E>>,
|
||
|
|
Args&&... args
|
||
|
|
) noexcept(noexcept(error_traits<E>::to_error(std::declval<Args&&>()...)))
|
||
|
|
{
|
||
|
|
return error_traits<E>::to_error(std::forward<Args>(args)...);
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class A1, class A2, class... Args>
|
||
|
|
constexpr auto construct_error_impl(
|
||
|
|
can_construct_error_from_adl<A1, A2>,
|
||
|
|
Args&&... args
|
||
|
|
) noexcept(noexcept(stdx_adl::detail::construct_error_from_adl(std::declval<Args&&>()...)))
|
||
|
|
{
|
||
|
|
return stdx_adl::detail::construct_error_from_adl(std::forward<Args>(args)...);
|
||
|
|
}
|
||
|
|
|
||
|
|
struct cannot_construct_error
|
||
|
|
{
|
||
|
|
static constexpr bool value = false;
|
||
|
|
using type = void;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <class... Args>
|
||
|
|
void construct_error_impl(cannot_construct_error, Args&&...) = delete;
|
||
|
|
|
||
|
|
template <class Args, class DecayedArgs>
|
||
|
|
using construct_error_disjunction_impl_t = disjunction<
|
||
|
|
is_convertible_to_error_using_traits<DecayedArgs>,
|
||
|
|
can_construct_error_from_adl<Args, DecayedArgs>,
|
||
|
|
cannot_construct_error
|
||
|
|
>;
|
||
|
|
|
||
|
|
template <class... Args>
|
||
|
|
using construct_error_disjunction_t = construct_error_disjunction_impl_t<
|
||
|
|
error_ctor_args<Args...>,
|
||
|
|
error_ctor_args<remove_cvref_t<Args>...>
|
||
|
|
>;
|
||
|
|
|
||
|
|
struct error_move_access;
|
||
|
|
struct error_ref_access;
|
||
|
|
struct error_cref_access;
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
// Generic domain for std::errc codes
|
||
|
|
//
|
||
|
|
class generic_error_domain : public error_domain
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
|
||
|
|
constexpr generic_error_domain() noexcept
|
||
|
|
: error_domain{{0x574ce0d940b64a2bULL, 0xa7c4438dd858c9cfULL}}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
virtual string_ref name() const noexcept override
|
||
|
|
{
|
||
|
|
return "generic domain";
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override;
|
||
|
|
|
||
|
|
virtual string_ref message(const error&) const noexcept override;
|
||
|
|
};
|
||
|
|
|
||
|
|
STDX_LEGACY_INLINE_CONSTEXPR generic_error_domain generic_domain {};
|
||
|
|
|
||
|
|
class STDX_TRIVIALLY_RELOCATABLE error
|
||
|
|
{
|
||
|
|
using erased_type = detail::erased_error;
|
||
|
|
|
||
|
|
constexpr error(detail::error_copy_construct_t, error_value<> v, const error_domain* d) noexcept
|
||
|
|
: m_domain(d), m_value(v.m_value)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr error(detail::error_move_construct_t, error_value<> v, const error_domain* d) noexcept
|
||
|
|
: m_domain(d), m_value(std::move(v.m_value))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
constexpr error() noexcept : m_domain(&generic_domain), m_value{}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr error(const error& e)
|
||
|
|
: error(detail::error_copy_construct_t{}, e.m_domain->copy(e), e.m_domain)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr error(error&& e)
|
||
|
|
: error(detail::error_move_construct_t{}, e.m_domain->move(std::move(e)), e.m_domain)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
detail::error_type_is_erasable<erased_type, T>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr error(const error_value<T>& v, const error_domain& d) noexcept
|
||
|
|
: m_domain(&d), m_value(v.value())
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
detail::error_type_is_erasable<erased_type, T>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr error(error_value<T>&& v, const error_domain& d) noexcept
|
||
|
|
: m_domain(&d), m_value(static_cast<T&&>(v.value()))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr error(error_value<> v, const error_domain& d) noexcept
|
||
|
|
: m_domain(&d), m_value(v.m_value)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
template <
|
||
|
|
class A,
|
||
|
|
class... Args,
|
||
|
|
class = std::enable_if_t<
|
||
|
|
detail::construct_error_disjunction_t<A&&, Args&&...>::value
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr error(A&& a, Args&&... args) noexcept(
|
||
|
|
noexcept(
|
||
|
|
detail::construct_error_impl(
|
||
|
|
std::declval<detail::construct_error_disjunction_t<A&&, Args&&...>>(),
|
||
|
|
std::forward<A>(a),
|
||
|
|
std::forward<Args>(args)...
|
||
|
|
)
|
||
|
|
)
|
||
|
|
)
|
||
|
|
:
|
||
|
|
error(
|
||
|
|
detail::construct_error_impl(
|
||
|
|
detail::construct_error_disjunction_t<A&&, Args&&...>{},
|
||
|
|
std::forward<A>(a),
|
||
|
|
std::forward<Args>(args)...
|
||
|
|
)
|
||
|
|
)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
error& operator = (const error& e)
|
||
|
|
{
|
||
|
|
error_value<> v = e.domain().copy(e);
|
||
|
|
domain().destroy(*this);
|
||
|
|
m_domain = e.m_domain;
|
||
|
|
m_value = v.m_value;
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
error& operator = (error&& e) noexcept
|
||
|
|
{
|
||
|
|
if (this != &e)
|
||
|
|
{
|
||
|
|
error_value<> v = e.domain().move(std::move(e));
|
||
|
|
domain().destroy(*this);
|
||
|
|
m_domain = e.m_domain;
|
||
|
|
m_value = v.m_value;
|
||
|
|
}
|
||
|
|
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
~error() noexcept
|
||
|
|
{
|
||
|
|
m_domain->destroy(*this);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool is_set() const noexcept { return (*this != stdx::error{}); }
|
||
|
|
|
||
|
|
const error_domain& domain() const noexcept
|
||
|
|
{
|
||
|
|
return *m_domain;
|
||
|
|
}
|
||
|
|
|
||
|
|
string_ref message() const noexcept
|
||
|
|
{
|
||
|
|
return domain().message(*this);
|
||
|
|
}
|
||
|
|
|
||
|
|
[[noreturn]] void throw_exception() const
|
||
|
|
{
|
||
|
|
domain().throw_exception(*this);
|
||
|
|
abort();
|
||
|
|
}
|
||
|
|
|
||
|
|
friend class error_domain;
|
||
|
|
friend struct detail::error_move_access;
|
||
|
|
friend struct detail::error_ref_access;
|
||
|
|
friend struct detail::error_cref_access;
|
||
|
|
friend inline bool operator==(const error& lhs, const error& rhs) noexcept;
|
||
|
|
friend inline bool operator!=(const error& lhs, const error& rhs) noexcept;
|
||
|
|
|
||
|
|
private:
|
||
|
|
|
||
|
|
const error_domain* m_domain;
|
||
|
|
erased_type m_value;
|
||
|
|
};
|
||
|
|
|
||
|
|
inline bool operator == (const error& lhs, const error& rhs) noexcept
|
||
|
|
{
|
||
|
|
if (lhs.domain().equivalent(lhs, rhs)) return true;
|
||
|
|
if (rhs.domain().equivalent(rhs, lhs)) return true;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline bool operator != (const error& lhs, const error& rhs) noexcept
|
||
|
|
{
|
||
|
|
return !(lhs == rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
struct error_move_access
|
||
|
|
{
|
||
|
|
constexpr explicit error_move_access(error&& e) noexcept : m_value(&e.m_value)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
STDX_LEGACY_CONSTEXPR detail::erased_error&& rvalue_ref() noexcept
|
||
|
|
{
|
||
|
|
return std::move(*m_value);
|
||
|
|
}
|
||
|
|
|
||
|
|
detail::erased_error* m_value;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct error_ref_access
|
||
|
|
{
|
||
|
|
constexpr explicit error_ref_access(error& e) noexcept : m_ptr(&e.m_value)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
detail::erased_error& ref() noexcept { return *m_ptr; }
|
||
|
|
|
||
|
|
detail::erased_error* m_ptr;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct error_cref_access
|
||
|
|
{
|
||
|
|
constexpr explicit error_cref_access(const error& e) noexcept : m_ptr(&e.m_value)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
constexpr const detail::erased_error& ref() const noexcept { return *m_ptr; }
|
||
|
|
|
||
|
|
const detail::erased_error* m_ptr;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = void_t<
|
||
|
|
decltype(detail::error_cast_impl<T>(std::declval<const detail::erased_error&>()))
|
||
|
|
>
|
||
|
|
>
|
||
|
|
constexpr T error_cast(const error& e) noexcept(
|
||
|
|
noexcept(detail::error_cast_impl<T>(std::declval<const detail::erased_error&>()))
|
||
|
|
)
|
||
|
|
{
|
||
|
|
return detail::error_cast_impl<T>(detail::error_cref_access{e}.ref());
|
||
|
|
}
|
||
|
|
|
||
|
|
template <
|
||
|
|
class T,
|
||
|
|
class = void_t<decltype(detail::error_cast_impl<T>(std::declval<detail::erased_error>()))>
|
||
|
|
>
|
||
|
|
constexpr T error_cast(error&& e) noexcept(
|
||
|
|
noexcept(detail::error_cast_impl<T>(std::declval<detail::erased_error>()))
|
||
|
|
)
|
||
|
|
{
|
||
|
|
return detail::error_cast_impl<T>(detail::error_move_access{std::move(e)}.rvalue_ref());
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
struct default_error_constructors
|
||
|
|
{
|
||
|
|
template <class T>
|
||
|
|
static error_value<> copy_constructor(const error& e) noexcept(
|
||
|
|
std::is_nothrow_copy_constructible<T>::value
|
||
|
|
&& std::is_nothrow_move_constructible<T>::value
|
||
|
|
)
|
||
|
|
{
|
||
|
|
T value = error_cast<T>(e);
|
||
|
|
return error_value<>{std::move(value)};
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
static error_value<> move_constructor(error&& e) noexcept(
|
||
|
|
std::is_nothrow_move_constructible<T>::value
|
||
|
|
)
|
||
|
|
{
|
||
|
|
return error_value<>{error_cast<T>(std::move(e))};
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
static void destructor(error& e) noexcept
|
||
|
|
{
|
||
|
|
detail::erased_error& value = error_ref_access{e}.ref();
|
||
|
|
stdx::launder(reinterpret_cast<T*>(&value.storage))->~T();
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
constexpr static error_resource_management::copy_constructor copy() noexcept
|
||
|
|
{
|
||
|
|
return is_trivially_copy_constructible<T>::value ?
|
||
|
|
nullptr : ©_constructor<T>;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
constexpr static error_resource_management::move_constructor move() noexcept
|
||
|
|
{
|
||
|
|
return is_trivially_move_constructible<T>::value ?
|
||
|
|
nullptr : &move_constructor<T>;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
constexpr static error_resource_management::destructor destroy() noexcept
|
||
|
|
{
|
||
|
|
return is_trivially_destructible<T>::value ?
|
||
|
|
nullptr : &destructor<T>;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
template <class T>
|
||
|
|
struct default_error_resource_management_t : error_resource_management
|
||
|
|
{
|
||
|
|
constexpr default_error_resource_management_t() noexcept
|
||
|
|
:
|
||
|
|
error_resource_management{
|
||
|
|
detail::default_error_constructors::copy<T>(),
|
||
|
|
detail::default_error_constructors::move<T>(),
|
||
|
|
detail::default_error_constructors::destroy<T>()
|
||
|
|
}
|
||
|
|
{}
|
||
|
|
};
|
||
|
|
|
||
|
|
#if defined(STDX_VARIABLE_TEMPLATES)
|
||
|
|
template <class T>
|
||
|
|
inline constexpr default_error_resource_management_t<T> default_error_resource_management {};
|
||
|
|
#endif
|
||
|
|
|
||
|
|
template <>
|
||
|
|
struct error_traits<std::errc>
|
||
|
|
{
|
||
|
|
static std::exception_ptr to_exception(std::errc ec) noexcept
|
||
|
|
{
|
||
|
|
return std::make_exception_ptr(std::make_error_code(ec));
|
||
|
|
}
|
||
|
|
|
||
|
|
static error to_error(std::errc ec) noexcept
|
||
|
|
{
|
||
|
|
return error{error_value<std::errc>{ec}, generic_domain};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
struct error_code_wrapper : enable_reference_count
|
||
|
|
{
|
||
|
|
explicit error_code_wrapper(std::error_code ec) noexcept : code(ec)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
std::error_code code;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
// Error domain mapping to std::error_code
|
||
|
|
//
|
||
|
|
class error_code_error_domain : public error_domain
|
||
|
|
{
|
||
|
|
using internal_value_type = intrusive_ptr<detail::error_code_wrapper>;
|
||
|
|
|
||
|
|
friend struct error_traits<std::error_code>;
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
constexpr error_code_error_domain() noexcept
|
||
|
|
:
|
||
|
|
error_domain{
|
||
|
|
{0x84e99cdcecae4443ULL, 0x9050179b713fd2afULL},
|
||
|
|
default_error_resource_management_t<internal_value_type>{}
|
||
|
|
}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
virtual string_ref name() const noexcept override
|
||
|
|
{
|
||
|
|
return "std::error_code error domain";
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override;
|
||
|
|
|
||
|
|
virtual string_ref message(const error& e) const noexcept override;
|
||
|
|
|
||
|
|
[[noreturn]] virtual void throw_exception(const error& e) const override;
|
||
|
|
};
|
||
|
|
|
||
|
|
STDX_LEGACY_INLINE_CONSTEXPR error_code_error_domain error_code_domain {};
|
||
|
|
|
||
|
|
template <>
|
||
|
|
struct error_traits<std::error_code>
|
||
|
|
{
|
||
|
|
static std::error_code from_exception(std::exception_ptr e) noexcept
|
||
|
|
{
|
||
|
|
return error_code_from_exception(std::move(e));
|
||
|
|
}
|
||
|
|
|
||
|
|
static std::exception_ptr to_exception(std::error_code ec) noexcept
|
||
|
|
{
|
||
|
|
return std::make_exception_ptr(std::system_error{ec});
|
||
|
|
}
|
||
|
|
|
||
|
|
static error to_error(std::error_code ec) noexcept;
|
||
|
|
};
|
||
|
|
|
||
|
|
namespace detail {
|
||
|
|
|
||
|
|
template <class Ptr, bool = (sizeof(Ptr) <= sizeof(std::intptr_t))>
|
||
|
|
struct exception_ptr_wrapper_impl
|
||
|
|
{
|
||
|
|
struct control_block : enable_reference_count
|
||
|
|
{
|
||
|
|
explicit control_block(Ptr p) noexcept : ptr_(std::move(p))
|
||
|
|
{ }
|
||
|
|
|
||
|
|
Ptr ptr_;
|
||
|
|
};
|
||
|
|
|
||
|
|
explicit exception_ptr_wrapper_impl(Ptr p) : ptr{new control_block{std::move(p)}}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
Ptr get() noexcept { return ptr ? ptr->ptr_ : Ptr{}; }
|
||
|
|
|
||
|
|
intrusive_ptr<control_block> ptr;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <class Ptr>
|
||
|
|
struct exception_ptr_wrapper_impl<Ptr, true>
|
||
|
|
{
|
||
|
|
explicit exception_ptr_wrapper_impl(Ptr p) : ptr{std::move(p)}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
Ptr get() noexcept { return ptr; }
|
||
|
|
|
||
|
|
Ptr ptr;
|
||
|
|
};
|
||
|
|
|
||
|
|
using exception_ptr_wrapper = exception_ptr_wrapper_impl<std::exception_ptr>;
|
||
|
|
|
||
|
|
static_assert(sizeof(exception_ptr_wrapper) == sizeof(std::intptr_t), "Internal library error");
|
||
|
|
|
||
|
|
} // end namespace detail
|
||
|
|
|
||
|
|
#ifdef STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE
|
||
|
|
template <>
|
||
|
|
struct is_trivially_relocatable<detail::exception_ptr_wrapper> : std::true_type
|
||
|
|
{ };
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// Error domain mapping to std::exception_ptr
|
||
|
|
//
|
||
|
|
class dynamic_exception_error_domain : public error_domain
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
|
||
|
|
constexpr dynamic_exception_error_domain() noexcept
|
||
|
|
:
|
||
|
|
error_domain{
|
||
|
|
{0x3c223c0aa3cf45e5ULL, 0x80dac24345cfb9fcULL},
|
||
|
|
default_error_resource_management_t<detail::exception_ptr_wrapper>{}
|
||
|
|
}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
virtual string_ref name() const noexcept override
|
||
|
|
{
|
||
|
|
return "dynamic exception domain";
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override;
|
||
|
|
|
||
|
|
virtual string_ref message(const error&) const noexcept override;
|
||
|
|
|
||
|
|
[[noreturn]] virtual void throw_exception(const error& e) const override
|
||
|
|
{
|
||
|
|
assert(e.domain() == *this);
|
||
|
|
std::rethrow_exception(error_cast<detail::exception_ptr_wrapper>(e).get());
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
STDX_LEGACY_INLINE_CONSTEXPR dynamic_exception_error_domain dynamic_exception_domain {};
|
||
|
|
|
||
|
|
// Error domain mapping to dynamic_exception_errc
|
||
|
|
//
|
||
|
|
class dynamic_exception_code_error_domain : public error_domain
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
|
||
|
|
constexpr dynamic_exception_code_error_domain() noexcept
|
||
|
|
: error_domain{{0xa242506c26484677ULL, 0x82365303df25e338ULL}}
|
||
|
|
{ }
|
||
|
|
|
||
|
|
virtual string_ref name() const noexcept override
|
||
|
|
{
|
||
|
|
return "dynamic exception code domain";
|
||
|
|
}
|
||
|
|
|
||
|
|
virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override;
|
||
|
|
|
||
|
|
virtual string_ref message(const error&) const noexcept override;
|
||
|
|
};
|
||
|
|
|
||
|
|
STDX_LEGACY_INLINE_CONSTEXPR dynamic_exception_code_error_domain dynamic_exception_code_domain {};
|
||
|
|
|
||
|
|
inline error make_error(dynamic_exception_errc code) noexcept
|
||
|
|
{
|
||
|
|
return error{error_value<dynamic_exception_errc>{code}, dynamic_exception_code_domain};
|
||
|
|
}
|
||
|
|
|
||
|
|
struct thrown_dynamic_exception : std::exception
|
||
|
|
{
|
||
|
|
explicit thrown_dynamic_exception(stdx::error e) noexcept : m_error(e)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
stdx::error error() const noexcept
|
||
|
|
{
|
||
|
|
return m_error;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
|
||
|
|
stdx::error m_error;
|
||
|
|
};
|
||
|
|
|
||
|
|
template <>
|
||
|
|
struct error_traits<std::exception_ptr>
|
||
|
|
{
|
||
|
|
static std::exception_ptr from_exception(std::exception_ptr e) noexcept
|
||
|
|
{
|
||
|
|
return e;
|
||
|
|
}
|
||
|
|
|
||
|
|
static std::exception_ptr to_exception(std::exception_ptr e) noexcept
|
||
|
|
{
|
||
|
|
return e;
|
||
|
|
}
|
||
|
|
|
||
|
|
static error to_error(std::exception_ptr e) noexcept
|
||
|
|
{
|
||
|
|
return error{
|
||
|
|
error_value<detail::exception_ptr_wrapper>{detail::exception_ptr_wrapper{e}},
|
||
|
|
dynamic_exception_domain
|
||
|
|
};
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
} // end namespace stdx
|
||
|
|
|
||
|
|
namespace std {
|
||
|
|
|
||
|
|
template<>
|
||
|
|
struct is_error_code_enum<stdx::dynamic_exception_errc> : std::true_type
|
||
|
|
{ };
|
||
|
|
|
||
|
|
} // end namespace std
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#if __cplusplus >= 201703L
|
||
|
|
#include <any>
|
||
|
|
#include <variant>
|
||
|
|
#include <optional>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include <functional>
|
||
|
|
|
||
|
|
namespace stdx {
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
inline const char* dynamic_exception_errc_str(unsigned ev) noexcept
|
||
|
|
{
|
||
|
|
constexpr const char* msg[] =
|
||
|
|
{
|
||
|
|
"Success",
|
||
|
|
"std::runtime_error",
|
||
|
|
"std::domain_error",
|
||
|
|
"std::invalid_argument",
|
||
|
|
"std::length_error",
|
||
|
|
"std::out_of_range",
|
||
|
|
"std::logic_error",
|
||
|
|
"std::range_error",
|
||
|
|
"std::overflow_error",
|
||
|
|
"std::underflow_error",
|
||
|
|
"std::bad_alloc",
|
||
|
|
"std::bad_array_new_length",
|
||
|
|
"std::bad_optional_access",
|
||
|
|
"std::bad_typeid",
|
||
|
|
"std::bad_any_cast",
|
||
|
|
"std::bad_cast",
|
||
|
|
"std::bad_weak_ptr",
|
||
|
|
"std::bad_function_call",
|
||
|
|
"std::bad_exception",
|
||
|
|
"std::bad_variant_access",
|
||
|
|
"unspecified dynamic exception"
|
||
|
|
};
|
||
|
|
|
||
|
|
assert(ev < (sizeof(msg) / sizeof(const char*)));
|
||
|
|
return msg[ev];
|
||
|
|
}
|
||
|
|
|
||
|
|
class dynamic_exception_error_category : public std::error_category
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
|
||
|
|
const char* name() const noexcept override
|
||
|
|
{
|
||
|
|
return "dynamic_exception";
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string message(int code) const override
|
||
|
|
{
|
||
|
|
return dynamic_exception_errc_str(code);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool equivalent(int code, const std::error_condition& cond) const noexcept override
|
||
|
|
{
|
||
|
|
switch (static_cast<dynamic_exception_errc>(code))
|
||
|
|
{
|
||
|
|
case dynamic_exception_errc::domain_error:
|
||
|
|
return (cond == std::errc::argument_out_of_domain);
|
||
|
|
case dynamic_exception_errc::invalid_argument:
|
||
|
|
return (cond == std::errc::invalid_argument);
|
||
|
|
case dynamic_exception_errc::length_error:
|
||
|
|
return (cond == std::errc::value_too_large);
|
||
|
|
case dynamic_exception_errc::out_of_range:
|
||
|
|
case dynamic_exception_errc::range_error:
|
||
|
|
case dynamic_exception_errc::underflow_error:
|
||
|
|
return (cond == std::errc::result_out_of_range);
|
||
|
|
case dynamic_exception_errc::overflow_error:
|
||
|
|
return (cond == std::errc::value_too_large);
|
||
|
|
case dynamic_exception_errc::bad_alloc:
|
||
|
|
case dynamic_exception_errc::bad_array_new_length:
|
||
|
|
return (cond == std::errc::not_enough_memory);
|
||
|
|
default:;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
inline const std::error_category& dynamic_exception_category() noexcept
|
||
|
|
{
|
||
|
|
static const dynamic_exception_error_category dynamic_exception_error_category_instance;
|
||
|
|
return dynamic_exception_error_category_instance;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // end anonymous namespace
|
||
|
|
|
||
|
|
inline std::error_code make_error_code(dynamic_exception_errc code) noexcept
|
||
|
|
{
|
||
|
|
return std::error_code{static_cast<int>(code), dynamic_exception_category()};
|
||
|
|
}
|
||
|
|
|
||
|
|
inline std::error_code error_code_from_exception(std::exception_ptr eptr) noexcept
|
||
|
|
{
|
||
|
|
if (!eptr) return make_error_code(dynamic_exception_errc::bad_exception);
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
std::rethrow_exception(eptr);
|
||
|
|
}
|
||
|
|
catch (const std::domain_error&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::domain_error);
|
||
|
|
}
|
||
|
|
catch (const std::invalid_argument&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::invalid_argument);
|
||
|
|
}
|
||
|
|
catch (const std::length_error&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::length_error);
|
||
|
|
}
|
||
|
|
catch (const std::out_of_range&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::out_of_range);
|
||
|
|
}
|
||
|
|
catch (const std::logic_error&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::logic_error);
|
||
|
|
}
|
||
|
|
catch (const std::range_error&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::range_error);
|
||
|
|
}
|
||
|
|
catch (const std::overflow_error&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::overflow_error);
|
||
|
|
}
|
||
|
|
catch (const std::underflow_error&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::underflow_error);
|
||
|
|
}
|
||
|
|
catch (const std::system_error& e)
|
||
|
|
{
|
||
|
|
return e.code();
|
||
|
|
}
|
||
|
|
catch (const std::runtime_error&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::runtime_error);
|
||
|
|
}
|
||
|
|
catch (const std::bad_array_new_length&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::bad_array_new_length);
|
||
|
|
}
|
||
|
|
catch (const std::bad_alloc&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::bad_alloc);
|
||
|
|
}
|
||
|
|
catch (const std::bad_typeid&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::bad_typeid);
|
||
|
|
}
|
||
|
|
#if __cplusplus >= 201703L
|
||
|
|
catch (const std::bad_optional_access&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::bad_optional_access);
|
||
|
|
}
|
||
|
|
catch (const std::bad_any_cast&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::bad_any_cast);
|
||
|
|
}
|
||
|
|
catch (const std::bad_variant_access&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::bad_variant_access);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
catch (const std::bad_cast&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::bad_cast);
|
||
|
|
}
|
||
|
|
catch (const std::bad_weak_ptr&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::bad_weak_ptr);
|
||
|
|
}
|
||
|
|
catch (const std::bad_function_call&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::bad_function_call);
|
||
|
|
}
|
||
|
|
catch (const std::bad_exception&)
|
||
|
|
{
|
||
|
|
return make_error_code(dynamic_exception_errc::bad_exception);
|
||
|
|
}
|
||
|
|
catch (...)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
return make_error_code(dynamic_exception_errc::unspecified_exception);
|
||
|
|
}
|
||
|
|
|
||
|
|
inline error error_from_exception(std::exception_ptr eptr) noexcept
|
||
|
|
{
|
||
|
|
if (!eptr) return make_error(dynamic_exception_errc::bad_exception);
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
std::rethrow_exception(eptr);
|
||
|
|
}
|
||
|
|
catch (const std::domain_error&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::domain_error);
|
||
|
|
}
|
||
|
|
catch (const std::invalid_argument&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::invalid_argument);
|
||
|
|
}
|
||
|
|
catch (const std::length_error&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::length_error);
|
||
|
|
}
|
||
|
|
catch (const std::out_of_range&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::out_of_range);
|
||
|
|
}
|
||
|
|
catch (const std::logic_error&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::logic_error);
|
||
|
|
}
|
||
|
|
catch (const std::range_error&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::range_error);
|
||
|
|
}
|
||
|
|
catch (const std::overflow_error&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::overflow_error);
|
||
|
|
}
|
||
|
|
catch (const std::underflow_error&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::underflow_error);
|
||
|
|
}
|
||
|
|
catch (const std::system_error& e)
|
||
|
|
{
|
||
|
|
return error{e.code()};
|
||
|
|
}
|
||
|
|
catch (const std::runtime_error&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::runtime_error);
|
||
|
|
}
|
||
|
|
catch (const std::bad_array_new_length&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::bad_array_new_length);
|
||
|
|
}
|
||
|
|
catch (const std::bad_alloc&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::bad_alloc);
|
||
|
|
}
|
||
|
|
catch (const std::bad_typeid&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::bad_typeid);
|
||
|
|
}
|
||
|
|
#if __cplusplus >= 201703L
|
||
|
|
catch (const std::bad_optional_access&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::bad_optional_access);
|
||
|
|
}
|
||
|
|
catch (const std::bad_any_cast&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::bad_any_cast);
|
||
|
|
}
|
||
|
|
catch (const std::bad_variant_access&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::bad_variant_access);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
catch (const std::bad_cast&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::bad_cast);
|
||
|
|
}
|
||
|
|
catch (const std::bad_weak_ptr&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::bad_weak_ptr);
|
||
|
|
}
|
||
|
|
catch (const std::bad_function_call&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::bad_function_call);
|
||
|
|
}
|
||
|
|
catch (const std::bad_exception&)
|
||
|
|
{
|
||
|
|
return make_error(dynamic_exception_errc::bad_exception);
|
||
|
|
}
|
||
|
|
catch (...)
|
||
|
|
{ }
|
||
|
|
|
||
|
|
return make_error(dynamic_exception_errc::unspecified_exception);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------- ErrorDomain (abstract base class)
|
||
|
|
//
|
||
|
|
inline void error_domain::throw_exception(const error& e) const
|
||
|
|
{
|
||
|
|
throw thrown_dynamic_exception{e};
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------- GenericErrorDomain
|
||
|
|
//
|
||
|
|
inline bool generic_error_domain::equivalent(const error& lhs, const error& rhs) const noexcept
|
||
|
|
{
|
||
|
|
assert(lhs.domain() == *this);
|
||
|
|
if (lhs.domain() == rhs.domain())
|
||
|
|
{
|
||
|
|
return error_cast<std::errc>(lhs) == error_cast<std::errc>(rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
string_ref generic_error_code_message(std::errc code) noexcept
|
||
|
|
{
|
||
|
|
switch (code)
|
||
|
|
{
|
||
|
|
case std::errc::address_family_not_supported:
|
||
|
|
return "Address family not supported by protocol";
|
||
|
|
case std::errc::address_in_use:
|
||
|
|
return "Address already in use";
|
||
|
|
case std::errc::address_not_available:
|
||
|
|
return "Cannot assign requested address";
|
||
|
|
case std::errc::already_connected:
|
||
|
|
return "Transport endpoint is already connected";
|
||
|
|
case std::errc::argument_list_too_long:
|
||
|
|
return "Argument list too long";
|
||
|
|
case std::errc::argument_out_of_domain:
|
||
|
|
return "Numerical argument out of domain";
|
||
|
|
case std::errc::bad_address:
|
||
|
|
return "Bad address";
|
||
|
|
case std::errc::bad_file_descriptor:
|
||
|
|
return "Bad file descriptor";
|
||
|
|
case std::errc::bad_message:
|
||
|
|
return "Bad message";
|
||
|
|
case std::errc::broken_pipe:
|
||
|
|
return "Broken pipe";
|
||
|
|
case std::errc::connection_aborted:
|
||
|
|
return "Software caused connection abort";
|
||
|
|
case std::errc::connection_already_in_progress:
|
||
|
|
return "Operation already in progress";
|
||
|
|
case std::errc::connection_refused:
|
||
|
|
return "Connection refused";
|
||
|
|
case std::errc::connection_reset:
|
||
|
|
return "Connection reset by peer";
|
||
|
|
case std::errc::cross_device_link:
|
||
|
|
return "Invalid cross-device link";
|
||
|
|
case std::errc::destination_address_required:
|
||
|
|
return "Destination address required";
|
||
|
|
case std::errc::device_or_resource_busy:
|
||
|
|
return "Device or resource busy";
|
||
|
|
case std::errc::directory_not_empty:
|
||
|
|
return "Directory not empty";
|
||
|
|
case std::errc::executable_format_error:
|
||
|
|
return "Exec format error";
|
||
|
|
case std::errc::file_exists:
|
||
|
|
return "File exists";
|
||
|
|
case std::errc::file_too_large:
|
||
|
|
return "File too large";
|
||
|
|
case std::errc::filename_too_long:
|
||
|
|
return "File name too long";
|
||
|
|
case std::errc::function_not_supported:
|
||
|
|
return "Function not implemented";
|
||
|
|
case std::errc::host_unreachable:
|
||
|
|
return "No route to host";
|
||
|
|
case std::errc::identifier_removed:
|
||
|
|
return "Identifier removed";
|
||
|
|
case std::errc::illegal_byte_sequence:
|
||
|
|
return "Invalid or incomplete multibyte or wide character";
|
||
|
|
case std::errc::inappropriate_io_control_operation:
|
||
|
|
return "Inappropriate ioctl for device";
|
||
|
|
case std::errc::interrupted:
|
||
|
|
return "Interrupted system call";
|
||
|
|
case std::errc::invalid_argument:
|
||
|
|
return "Invalid argument";
|
||
|
|
case std::errc::invalid_seek:
|
||
|
|
return "Illegal seek";
|
||
|
|
case std::errc::io_error:
|
||
|
|
return "Input/output error";
|
||
|
|
case std::errc::is_a_directory:
|
||
|
|
return "Is a directory";
|
||
|
|
case std::errc::message_size:
|
||
|
|
return "Message too long";
|
||
|
|
case std::errc::network_down:
|
||
|
|
return "Network is down";
|
||
|
|
case std::errc::network_reset:
|
||
|
|
return "Network dropped connection on reset";
|
||
|
|
case std::errc::network_unreachable:
|
||
|
|
return "Network is unreachable";
|
||
|
|
case std::errc::no_buffer_space:
|
||
|
|
return "No buffer space available";
|
||
|
|
case std::errc::no_child_process:
|
||
|
|
return "No child processes";
|
||
|
|
case std::errc::no_link:
|
||
|
|
return "Link has been severed";
|
||
|
|
case std::errc::no_lock_available:
|
||
|
|
return "No locks available";
|
||
|
|
case std::errc::no_message:
|
||
|
|
return "No message of desired type";
|
||
|
|
case std::errc::no_protocol_option:
|
||
|
|
return "Protocol not available";
|
||
|
|
case std::errc::no_space_on_device:
|
||
|
|
return "No space left on device";
|
||
|
|
case std::errc::no_stream_resources:
|
||
|
|
return "Out of streams resources";
|
||
|
|
case std::errc::no_such_device_or_address:
|
||
|
|
return "No such device or address";
|
||
|
|
case std::errc::no_such_device:
|
||
|
|
return "No such device";
|
||
|
|
case std::errc::no_such_file_or_directory:
|
||
|
|
return "No such file or directory";
|
||
|
|
case std::errc::no_such_process:
|
||
|
|
return "No such process";
|
||
|
|
case std::errc::not_a_directory:
|
||
|
|
return "Not a directory";
|
||
|
|
case std::errc::not_a_socket:
|
||
|
|
return "Socket operation on non-socket";
|
||
|
|
case std::errc::not_a_stream:
|
||
|
|
return "Device not a stream";
|
||
|
|
case std::errc::not_connected:
|
||
|
|
return "Transport endpoint is not connected";
|
||
|
|
case std::errc::not_enough_memory:
|
||
|
|
return "Cannot allocate memory";
|
||
|
|
#if ENOTSUP != EOPNOTSUPP
|
||
|
|
case std::errc::not_supported:
|
||
|
|
return "Operation not supported";
|
||
|
|
#endif
|
||
|
|
case std::errc::operation_canceled:
|
||
|
|
return "Operation canceled";
|
||
|
|
case std::errc::operation_in_progress:
|
||
|
|
return "Operation now in progress";
|
||
|
|
case std::errc::operation_not_permitted:
|
||
|
|
return "Operation not permitted";
|
||
|
|
case std::errc::operation_not_supported:
|
||
|
|
return "Operation not supported";
|
||
|
|
#if EAGAIN != EWOULDBLOCK
|
||
|
|
case std::errc::operation_would_block:
|
||
|
|
return "Resource temporarily unavailable";
|
||
|
|
#endif
|
||
|
|
case std::errc::owner_dead:
|
||
|
|
return "Owner died";
|
||
|
|
case std::errc::permission_denied:
|
||
|
|
return "Permission denied";
|
||
|
|
case std::errc::protocol_error:
|
||
|
|
return "Protocol error";
|
||
|
|
case std::errc::protocol_not_supported:
|
||
|
|
return "Protocol not supported";
|
||
|
|
case std::errc::read_only_file_system:
|
||
|
|
return "Read-only file system";
|
||
|
|
case std::errc::resource_deadlock_would_occur:
|
||
|
|
return "Resource deadlock avoided";
|
||
|
|
case std::errc::resource_unavailable_try_again:
|
||
|
|
return "Resource temporarily unavailable";
|
||
|
|
case std::errc::result_out_of_range:
|
||
|
|
return "Numerical result out of range";
|
||
|
|
case std::errc::state_not_recoverable:
|
||
|
|
return "State not recoverable";
|
||
|
|
case std::errc::stream_timeout:
|
||
|
|
return "Timer expired";
|
||
|
|
case std::errc::text_file_busy:
|
||
|
|
return "Text file busy";
|
||
|
|
case std::errc::timed_out:
|
||
|
|
return "Connection timed out";
|
||
|
|
case std::errc::too_many_files_open_in_system:
|
||
|
|
return "Too many open files in system";
|
||
|
|
case std::errc::too_many_files_open:
|
||
|
|
return "Too many open files";
|
||
|
|
case std::errc::too_many_links:
|
||
|
|
return "Too many links";
|
||
|
|
case std::errc::too_many_symbolic_link_levels:
|
||
|
|
return "Too many levels of symbolic links";
|
||
|
|
case std::errc::value_too_large:
|
||
|
|
return "Value too large for defined data type";
|
||
|
|
case std::errc::wrong_protocol_type:
|
||
|
|
return "Protocol wrong type for socket";
|
||
|
|
default:
|
||
|
|
return "Unspecified error";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
} // end anonymous namespace
|
||
|
|
|
||
|
|
inline string_ref generic_error_domain::message(const error& e) const noexcept
|
||
|
|
{
|
||
|
|
assert(e.domain() == *this);
|
||
|
|
return generic_error_code_message(error_cast<std::errc>(e));
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------- ErrorCodeErrorDomain
|
||
|
|
//
|
||
|
|
inline string_ref error_code_error_domain::message(const error& e) const noexcept
|
||
|
|
{
|
||
|
|
assert(e.domain() == *this);
|
||
|
|
|
||
|
|
auto ptr = error_cast<internal_value_type>(e);
|
||
|
|
if (ptr)
|
||
|
|
{
|
||
|
|
std::string msg = ptr->code.message();
|
||
|
|
return shared_string_ref{msg.c_str(), msg.c_str() + msg.size()};
|
||
|
|
}
|
||
|
|
|
||
|
|
return string_ref{"Bad error code"};
|
||
|
|
}
|
||
|
|
|
||
|
|
inline void error_code_error_domain::throw_exception(const error& e) const
|
||
|
|
{
|
||
|
|
assert(e.domain() == *this);
|
||
|
|
|
||
|
|
std::error_code code;
|
||
|
|
auto ptr = error_cast<internal_value_type>(e);
|
||
|
|
if (ptr) code = ptr->code;
|
||
|
|
throw std::system_error{code};
|
||
|
|
}
|
||
|
|
|
||
|
|
inline bool error_code_error_domain::equivalent(const error& lhs, const error& rhs) const noexcept
|
||
|
|
{
|
||
|
|
assert(lhs.domain() == *this);
|
||
|
|
|
||
|
|
if (lhs.domain() == rhs.domain())
|
||
|
|
{
|
||
|
|
auto ptr1 = error_cast<internal_value_type>(lhs);
|
||
|
|
auto ptr2 = error_cast<internal_value_type>(rhs);
|
||
|
|
if (ptr1 && ptr2) return ptr1->code == ptr2->code.default_error_condition();
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (rhs.domain() == generic_domain)
|
||
|
|
{
|
||
|
|
auto ptr1 = error_cast<internal_value_type>(lhs);
|
||
|
|
if (ptr1) return ptr1->code == error_cast<std::errc>(rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline stdx::error error_traits<std::error_code>::to_error(std::error_code ec) noexcept
|
||
|
|
{
|
||
|
|
using internal_value_type = error_code_error_domain::internal_value_type;
|
||
|
|
|
||
|
|
if (ec.category() == std::generic_category())
|
||
|
|
{
|
||
|
|
return error{
|
||
|
|
error_value<std::errc>{static_cast<std::errc>(ec.default_error_condition().value())},
|
||
|
|
generic_domain
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
return error{
|
||
|
|
error_value<internal_value_type>{internal_value_type{new detail::error_code_wrapper{ec}}},
|
||
|
|
error_code_domain
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------- DynamicExceptionErrorDomain
|
||
|
|
//
|
||
|
|
inline string_ref dynamic_exception_error_domain::message(const error& e) const noexcept
|
||
|
|
{
|
||
|
|
assert(e.domain() == *this);
|
||
|
|
|
||
|
|
std::exception_ptr eptr = error_cast<detail::exception_ptr_wrapper>(e).get();
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
std::rethrow_exception(eptr);
|
||
|
|
}
|
||
|
|
catch (const std::exception& ex)
|
||
|
|
{
|
||
|
|
return shared_string_ref{ex.what()};
|
||
|
|
}
|
||
|
|
catch (...) {}
|
||
|
|
|
||
|
|
return string_ref{"Unknown dynamic exception"};
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
std::errc dynamic_exception_code_to_generic_code(dynamic_exception_errc code) noexcept
|
||
|
|
{
|
||
|
|
switch (code)
|
||
|
|
{
|
||
|
|
case dynamic_exception_errc::domain_error:
|
||
|
|
return std::errc::argument_out_of_domain;
|
||
|
|
case dynamic_exception_errc::invalid_argument:
|
||
|
|
return std::errc::invalid_argument;
|
||
|
|
case dynamic_exception_errc::length_error:
|
||
|
|
return std::errc::value_too_large;
|
||
|
|
case dynamic_exception_errc::out_of_range:
|
||
|
|
case dynamic_exception_errc::range_error:
|
||
|
|
case dynamic_exception_errc::underflow_error:
|
||
|
|
return std::errc::result_out_of_range;
|
||
|
|
case dynamic_exception_errc::overflow_error:
|
||
|
|
return std::errc::value_too_large;
|
||
|
|
case dynamic_exception_errc::bad_alloc:
|
||
|
|
case dynamic_exception_errc::bad_array_new_length:
|
||
|
|
return std::errc::not_enough_memory;
|
||
|
|
default:;
|
||
|
|
}
|
||
|
|
return std::errc{};
|
||
|
|
}
|
||
|
|
|
||
|
|
} // end anonymous namespace
|
||
|
|
|
||
|
|
inline bool dynamic_exception_error_domain::equivalent(const error& lhs, const error& rhs) const noexcept
|
||
|
|
{
|
||
|
|
assert(lhs.domain() == *this);
|
||
|
|
|
||
|
|
std::exception_ptr eptr = error_cast<detail::exception_ptr_wrapper>(lhs).get();
|
||
|
|
|
||
|
|
if (rhs.domain() == *this)
|
||
|
|
{
|
||
|
|
std::exception_ptr eptr2 = error_cast<detail::exception_ptr_wrapper>(rhs).get();
|
||
|
|
if (eptr == eptr2) return true;
|
||
|
|
|
||
|
|
error e1 = error_from_exception(eptr);
|
||
|
|
error e2 = error_from_exception(eptr2);
|
||
|
|
return e1.domain().equivalent(e1, e2);
|
||
|
|
}
|
||
|
|
else if (rhs.domain() == error_code_domain)
|
||
|
|
{
|
||
|
|
std::error_code ec = error_code_from_exception(eptr);
|
||
|
|
return error_code_domain.equivalent(rhs, error{ec});
|
||
|
|
}
|
||
|
|
|
||
|
|
error e = error_from_exception(eptr);
|
||
|
|
return e.domain().equivalent(e, rhs);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------- DynamicExceptionCodeErrorDomain
|
||
|
|
//
|
||
|
|
inline bool dynamic_exception_code_error_domain::equivalent(
|
||
|
|
const error& lhs,
|
||
|
|
const error& rhs
|
||
|
|
) const noexcept
|
||
|
|
{
|
||
|
|
assert(lhs.domain() == *this);
|
||
|
|
|
||
|
|
const dynamic_exception_errc code = error_cast<dynamic_exception_errc>(lhs);
|
||
|
|
|
||
|
|
if (rhs.domain() == *this)
|
||
|
|
{
|
||
|
|
return code == error_cast<dynamic_exception_errc>(rhs);
|
||
|
|
}
|
||
|
|
else if (rhs.domain() == error_code_domain)
|
||
|
|
{
|
||
|
|
return error_code_domain.equivalent(rhs, make_error_code(code));
|
||
|
|
}
|
||
|
|
else if (rhs.domain() == generic_domain)
|
||
|
|
{
|
||
|
|
std::errc generic_code = dynamic_exception_code_to_generic_code(code);
|
||
|
|
return generic_domain.equivalent(rhs, generic_code);
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
inline string_ref dynamic_exception_code_error_domain::message(const error& e) const noexcept
|
||
|
|
{
|
||
|
|
assert(e.domain() == *this);
|
||
|
|
return string_ref{
|
||
|
|
dynamic_exception_errc_str(static_cast<unsigned>(error_cast<dynamic_exception_errc>(e)))
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
} // end namespace stdx
|
||
|
|
|
||
|
|
|
||
|
|
|