#pragma once #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 #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 #if defined(__clang__) && defined(__has_warning) #pragma clang diagnostic push #if __has_warning("-Wdeprecated-declarations") #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif #endif #endif // STDX_COMPILER_HPP #ifndef STDX_TYPE_TRAITS_HPP #define STDX_TYPE_TRAITS_HPP #include NAMESPACE_STDX { // Implementation of std::void_t for use with pre-C++17 compilers. // namespace detail { template struct void_t_impl { using type = void; }; } // end namespace detail template using void_t = typename detail::void_t_impl::type; template struct dependent_type { using type = U; }; template using dependent_type_t = typename dependent_type::type; template struct disjunction : std::false_type { }; template struct disjunction : B1 { }; template struct disjunction : std::conditional_t< bool(B1::value), B1, disjunction > { }; struct sentinel_type { static constexpr bool value = true; using type = void; }; template struct bool_constant : std::integral_constant { }; // Implementation of std::remove_cvref for use with pre-C++20 compilers. // template struct remove_cvref { using type = std::remove_cv_t>; }; template using remove_cvref_t = typename remove_cvref::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 using is_trivially_copyable = std::is_trivial; template using is_trivially_copy_constructible = std::has_trivial_copy_constructor; template using is_trivially_destructible = std::has_trivial_destructor; #elif (__GNUC__ < 5) template using is_trivially_copyable = std::is_trivial; template using is_trivially_copy_constructible = std::has_trivial_copy_constructor; template using is_trivially_destructible = std::is_trivially_destructible; #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 using is_trivially_move_constructible = std::is_trivially_move_constructible; #else template using is_trivially_move_constructible = is_trivially_copyable; #endif #if defined(__cpp_lib_trivially_relocatable) using std::is_trivially_relocatable; #elif defined(__has_builtin) #if __has_builtin(__builtin_is_cpp_trivially_relocatable) template struct is_trivially_relocatable : std::bool_constant<__builtin_is_cpp_trivially_relocatable(T)> { }; #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE #elif __has_builtin(__is_trivially_relocatable) template struct is_trivially_relocatable : std::bool_constant<__is_trivially_relocatable(T)> { }; #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE #else template struct is_trivially_relocatable : is_trivially_copyable { }; #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE #endif #else template struct is_trivially_relocatable : is_trivially_copyable { }; #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 #include NAMESPACE_STDX { namespace detail { template using use_static_cast = bool_constant< ((std::is_integral::value || std::is_enum::value) && (std::is_integral::value || std::is_enum::value)) || (std::is_same::value && std::is_copy_constructible::value) >; template using is_integral_ptr_t = bool_constant< std::is_same::value || std::is_same::value >; template using use_reinterpret_cast = bool_constant< !std::is_same::value && (( std::is_pointer::value && std::is_pointer::value && std::is_convertible::value ) || (std::is_pointer::value && is_integral_ptr_t::value) || (std::is_pointer::value && is_integral_ptr_t::value) ) >; #if defined(STDX_GCC_COMPILER) template using use_union_type_punning = bool_constant< !use_static_cast::value && !use_reinterpret_cast::value && !std::is_array::value && !std::is_array::value >; template union bit_cast_union { From from; To to; }; #else template using use_union_type_punning = std::false_type; #endif template using can_bit_cast = bool_constant< (sizeof(To) == sizeof(From)) && is_trivially_copyable::value && is_trivially_copyable::value >; } // end namespace detail template < class To, class From, class = std::enable_if_t< detail::can_bit_cast::value && detail::use_static_cast::value > > constexpr To bit_cast(const From& from) noexcept { return static_cast(from); } template < class To, class From, class = std::enable_if_t< detail::can_bit_cast::value && detail::use_reinterpret_cast::value >, int = 0 > constexpr To bit_cast(const From& from) noexcept { return reinterpret_cast(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::value && detail::use_union_type_punning::value >, class = void > constexpr To bit_cast(const From& from) noexcept { return detail::bit_cast_union{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::value && !detail::use_static_cast::value && !detail::use_reinterpret_cast::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::value && !detail::use_static_cast::value && !detail::use_reinterpret_cast::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 struct is_bit_castable : bool_constant< (sizeof(To) == sizeof(From)) && is_trivially_copyable::value && is_trivially_copyable::value > { }; } // end namespace stdx #endif #ifndef STDX_INTRUSIVE_POINTER_HPP #define STDX_INTRUSIVE_POINTER_HPP #include #include #include 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, 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& shared_reference_count() noexcept { return m_reference_count; } private: std::atomic m_reference_count; }; struct default_intrusive_reference_count { template std::atomic& operator()(Pointer p) const noexcept { return p->shared_reference_count(); } }; namespace detail { template 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 friend class reference_count_base; constexpr intrusive_ptr_base() = default; constexpr intrusive_ptr_base(pointer p) noexcept : m_impl(p) { } template constexpr explicit intrusive_ptr_base( RefCountAccessorForwardingReference&& f, std::enable_if_t< std::is_constructible< RefCountAccessor, RefCountAccessorForwardingReference&& >::value >* = nullptr ) : m_impl(std::forward(f)) { } template explicit intrusive_ptr_base( pointer ptr, RefCountAccessorForwardingReference&& f, std::enable_if_t< std::is_constructible< RefCountAccessor, RefCountAccessorForwardingReference&& >::value >* = nullptr ) : m_impl(ptr, std::forward(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(f), std::forward(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(f), std::forward(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(f), std::forward(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::value > > constexpr explicit impl(RefCountAccess&& f) : RefCountAccessor(std::forward(f)) { } template < class RefCountAccess, class = std::enable_if_t< std::is_constructible::value > > impl(pointer ptr, RefCountAccess&& f) : PointerImplementation(ptr), RefCountAccessor(std::forward(f)) { } template < class RefCountAccess, class D, class = std::enable_if_t< std::is_constructible::value && std::is_constructible::value > > constexpr impl(RefCountAccess&& f, D&& d) : RefCountAccessor(std::forward(f)), Deleter(std::forward(d)) { } template < class RefCountAccess, class D, class = std::enable_if_t< std::is_constructible::value && std::is_constructible::value > > impl(pointer ptr, RefCountAccess&& f, D&& d) : PointerImplementation(ptr), RefCountAccessor(std::forward(f)), Deleter(std::forward(d)) { } impl(const impl&) = default; impl& operator = (const impl&) = default; impl(impl&&) = default; impl& operator = (impl&&) = default; Deleter& get_deleter() noexcept { return static_cast(*this); } const Deleter& get_deleter() const noexcept { return static_cast(*this); } void assign(pointer ptr) noexcept { static_cast(*this).assign(ptr); } void assign(std::nullptr_t) noexcept { static_cast(*this).assign(nullptr); } void swap(impl& other) { std::swap( static_cast(*this), static_cast(other) ); std::swap(static_cast(*this), static_cast(other)); std::swap( static_cast(this->get_deleter()), static_cast(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(m_impl).shared_object; } constexpr pointer ptr() const noexcept { return static_cast(m_impl).shared_object; } RefCountAccessor& ref_count_func() noexcept { return static_cast(m_impl); } const RefCountAccessor& ref_count_func() const noexcept { return static_cast(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{ 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 void invoke_deleter(pointer p, WeakReferenceCountDescriptor* d) { maybe_delete_shared_object(p, m_impl.get_deleter(), d); } template 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 > { using base_type = detail::intrusive_ptr_base< T, RefCountAccessor, Deleter, Pointer, detail::pointer_wrapper >; 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 constexpr intrusive_ptr(std::nullptr_t, RefCountAccess&& f) : base_type(std::forward(f)) { } template constexpr intrusive_ptr(std::nullptr_t, RefCountAccess&& f, D&& d) : base_type(std::forward(f), std::forward(d)) { } constexpr explicit intrusive_ptr(Pointer ptr) noexcept : base_type(ptr) { // reference count must initially be >= 1 } template intrusive_ptr(Pointer ptr, RefCountAccess&& f) noexcept : base_type(ptr, std::forward(f)) { // reference count must initially be >= 1 } template intrusive_ptr(Pointer ptr, RefCountAccess&& f, D&& d) noexcept : base_type( ptr, std::forward(f), std::forward(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::value> > intrusive_ptr(const intrusive_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(*this) = static_cast(rhs); return *this; } // Move assignment // intrusive_ptr& operator = (intrusive_ptr&& rhs) noexcept { if (this != std::addressof(rhs)) { this->decrement_shared_reference_count(); static_cast(*this) = std::move(static_cast(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(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 friend class intrusive_ptr; }; // -------------- Global equality operators // template bool operator == ( const intrusive_ptr& lhs, const intrusive_ptr& rhs ) noexcept { return lhs.get() == rhs.get(); } template bool operator != ( const intrusive_ptr& lhs, const intrusive_ptr& rhs ) noexcept { return !(lhs == rhs); } template bool operator < ( const intrusive_ptr& lhs, const intrusive_ptr& rhs ) noexcept { using pointer1 = typename intrusive_ptr::pointer; using pointer2 = typename intrusive_ptr::pointer; using common_type = typename std::common_type::type; return std::less{}(lhs.get(), rhs.get()); } template bool operator > ( const intrusive_ptr& lhs, const intrusive_ptr& rhs ) noexcept { return rhs < lhs; } template bool operator <= ( const intrusive_ptr& lhs, const intrusive_ptr& rhs ) noexcept { return !(rhs < lhs); } template bool operator >= ( const intrusive_ptr& lhs, const intrusive_ptr& rhs ) noexcept { return !(lhs < rhs); } template bool operator == (const intrusive_ptr& lhs, std::nullptr_t) noexcept { return !lhs; } template bool operator == (std::nullptr_t, const intrusive_ptr& rhs) noexcept { return !rhs; } template bool operator != (const intrusive_ptr& lhs, std::nullptr_t) noexcept { return bool(lhs); } template bool operator != (std::nullptr_t, const intrusive_ptr& rhs) noexcept { return bool(rhs); } template bool operator < (const intrusive_ptr& lhs, std::nullptr_t) noexcept { using pointer = typename intrusive_ptr::pointer; return std::less{}(lhs.get(), nullptr); } template bool operator < (std::nullptr_t, const intrusive_ptr& rhs) noexcept { using pointer = typename intrusive_ptr::pointer; return std::less{}(nullptr, rhs.get()); } template bool operator > (const intrusive_ptr& lhs, std::nullptr_t) noexcept { return (nullptr < lhs); } template bool operator > (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept { return (rhs < nullptr); } template bool operator <= (const intrusive_ptr& lhs, std::nullptr_t rhs) noexcept { return !(nullptr < lhs); } template bool operator <= (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept { return !(rhs < nullptr); } template bool operator >= (const intrusive_ptr& lhs, std::nullptr_t rhs) noexcept { return !(lhs < nullptr); } template bool operator >= (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept { return !(nullptr < rhs); } template void swap(intrusive_ptr& lhs, intrusive_ptr& rhs) noexcept { lhs.swap(rhs); } #ifdef STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE template struct is_trivially_relocatable> : std::true_type { }; #endif } // end namespace stdx #endif // include guard #ifndef STDX_STRING_REF_HPP #define STDX_STRING_REF_HPP #include #include #include 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 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; 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 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(this) + header_size(); } const char* data() const noexcept { return reinterpret_cast(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(::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(this->context); } string_arena_base* get_arena() noexcept { return static_cast(this->context); } static string_ref::state_type copy_construct(const string_ref& base) noexcept { const shared_string_ref& s = static_cast(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(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(base); string_arena* a = static_cast(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 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(this) + header_size(); } const char* data() const noexcept { return reinterpret_cast(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 explicit shared_string_ref(allocated_string_arena* a) noexcept : string_ref{ a->begin(), a->end(), string_ref::resource_management{ ©_construct, &move_construct, &allocator_destroy }, a } { } template 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; using arena_type = allocated_string_arena; 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 static void allocator_destroy(string_ref& base) noexcept { using arena_type = allocated_string_arena; shared_string_ref& s = static_cast(base); arena_type* a = static_cast(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(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 shared_string_ref(const Allocator& alloc, const char* beg) : string_ref{allocate_string_ref(alloc, beg, detail::cstring_null_scan(beg) - beg)} { } template 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 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 constexpr T* launder(T* p) noexcept { return __builtin_launder(p); } #endif #endif #endif #if !defined(STDX_HAVE_NATIVE_LAUNDER) template constexpr T* launder(T* p) noexcept { return p; } #endif } // end namespace stdx #endif #ifndef STDX_ERROR_HPP #define STDX_ERROR_HPP #include #include #include #include #include NAMESPACE_STDX { class error; namespace detail { template struct error_ctor_args; } // end namespace detail } // end namespace stdx namespace stdx_adl { namespace detail { template struct can_use_adl_to_call_make_error : std::false_type { }; inline void make_error() noexcept { } template struct can_use_adl_to_call_make_error< stdx::detail::error_ctor_args, std::enable_if_t< std::is_same< decltype(make_error(std::declval()...)), stdx::error >::value > > : std::true_type { }; template constexpr auto construct_error_from_adl(Args&&... args) noexcept( noexcept(make_error(std::forward(args)...)) ) { return make_error(std::forward(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 struct error_traits { }; namespace detail { template struct is_convertible_from_exception_using_traits : std::false_type { }; template struct is_convertible_from_exception_using_traits< E, std::enable_if_t< std::is_convertible< decltype(error_traits::from_exception(std::declval())), E >::value > > : std::true_type { }; template struct is_convertible_to_exception_using_traits : std::false_type { }; template struct is_convertible_to_exception_using_traits< E, std::enable_if_t< std::is_convertible< decltype(error_traits::to_exception(std::declval())), std::exception_ptr >::value > > : std::true_type { }; template E from_exception_impl(std::exception_ptr e, std::is_convertible) noexcept { return e; } template E from_exception_impl( std::exception_ptr e, is_convertible_from_exception_using_traits ) noexcept { return error_traits::from_exception(std::move(e)); } void from_exception_impl(std::exception_ptr, sentinel_type) = delete; template std::exception_ptr to_exception_impl(E&& e, std::is_convertible) noexcept { return std::forward(e); } template std::exception_ptr to_exception_impl( E&& e, is_convertible_to_exception_using_traits ) noexcept { return error_traits::to_exception(std::forward(e)); } template void to_exception_impl(const E&, sentinel_type) = delete; } // end namespace detail template E from_exception(std::exception_ptr e) noexcept { return detail::from_exception_impl( std::move(e), disjunction< std::is_convertible, detail::is_convertible_from_exception_using_traits, sentinel_type >{} ); } template std::exception_ptr to_exception(E&& e) noexcept { return detail::to_exception_impl( std::forward(e), disjunction< std::is_convertible, std::exception_ptr>, detail::is_convertible_to_exception_using_traits>, 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 struct error_value; namespace detail { template struct is_error_value : std::false_type { }; template struct is_error_value> : std::true_type { }; } // end namespace detail struct error_resource_management { using copy_constructor = error_value(*)(const error&); using move_constructor = error_value(*)(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::value > > constexpr dependent_type_t> 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::value && std::is_convertible::value > > constexpr dependent_type_t, error_value<>> move(E&& e) const { return m_resource_management.move ? m_resource_management.move(static_cast(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 struct error_type_is_erasable : bool_constant< is_trivially_relocatable::value && (sizeof(T) <= sizeof(ErasedType)) && (alignof(T) <= alignof(ErasedType)) > { }; template struct error_type_is_erasable : std::false_type { }; template struct error_type_is_erasable : std::false_type { }; template using can_use_static_cast = bool_constant< std::is_integral::value || std::is_enum::value >; struct erased_error { using integral_type = std::intptr_t; using storage_type = std::aligned_storage_t; constexpr erased_error() noexcept : code{} { } template < class T, class = std::enable_if_t< error_type_is_erasable::value && can_use_static_cast::value > > constexpr erased_error(T value) noexcept : code{static_cast(value)} { } template < class T, class = std::enable_if_t< error_type_is_erasable::value && !can_use_static_cast::value && is_bit_castable::value >, class = void > constexpr erased_error(T value) noexcept : code{bit_cast(value)} { } template < class T, class = std::enable_if_t< error_type_is_erasable::value && !can_use_static_cast::value && !is_bit_castable::value >, int = 0 > erased_error(T value) noexcept(std::is_nothrow_move_constructible::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::value && can_use_static_cast::value > > constexpr T error_cast_impl(erased_error e) noexcept { return static_cast(e.code); } template < class T, class = std::enable_if_t< error_type_is_erasable::value && !can_use_static_cast::value && is_bit_castable::value >, class = void > constexpr T error_cast_impl(erased_error e) noexcept { return bit_cast(e.code); } template < class T, class = std::enable_if_t< error_type_is_erasable::value && !can_use_static_cast::value && !is_bit_castable::value > > constexpr T error_cast_impl(erased_error&& e) noexcept( std::is_nothrow_move_constructible::value ) { return std::move(*stdx::launder(reinterpret_cast(&e.storage))); } template < class T, class = std::enable_if_t< error_type_is_erasable::value && !can_use_static_cast::value && !is_bit_castable::value > > constexpr T error_cast_impl(const erased_error& e) noexcept( std::is_nothrow_copy_constructible::value ) { return *stdx::launder(reinterpret_cast(&e.storage)); } } // end namespace detail template struct error_value { using value_type = T; constexpr error_value(const T& v) noexcept( std::is_nothrow_copy_constructible::value ) : m_value(v) { } constexpr error_value(T&& v) noexcept( std::is_nothrow_move_constructible::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(m_value); } STDX_LEGACY_CONSTEXPR T&& value() && noexcept { return static_cast(m_value); } T m_value; }; template <> struct error_value { template < class T, class = std::enable_if_t< !std::is_same, error_value>::value && !detail::is_error_value>::value && std::is_constructible::value > > constexpr error_value(T&& v) noexcept( std::is_nothrow_constructible::value ) : m_value(std::forward(v)) { } template < class T, class = std::enable_if_t< std::is_constructible::value > > constexpr error_value(const error_value& v) noexcept( std::is_nothrow_constructible::value ) : error_value(v.value()) { } template < class T, class = std::enable_if_t< std::is_constructible::value > > constexpr error_value(error_value&& v) noexcept( std::is_nothrow_constructible::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 struct error_ctor_args {}; template struct can_construct_error_from_adl { }; template struct can_construct_error_from_adl, error_ctor_args> : stdx_adl::detail::can_use_adl_to_call_make_error> { }; template <> struct can_construct_error_from_adl, error_ctor_args<>> : std::false_type { }; template struct can_construct_error_from_adl, error_ctor_args> : std::false_type { }; template struct can_construct_error_from_adl< error_ctor_args, error_ctor_args, ErrorDomain> > : std::false_type { }; template struct can_construct_error_from_adl< error_ctor_args, error_ctor_args, ErrorDomain> > : std::false_type { }; template struct can_construct_error_from_adl< error_ctor_args, error_ctor_args, ErrorDomain> > : std::false_type { }; template struct is_convertible_to_error_using_traits : std::false_type { }; template <> struct is_convertible_to_error_using_traits> : std::false_type { }; template struct is_convertible_to_error_using_traits< error_ctor_args, std::enable_if_t< std::is_same< decltype(error_traits::to_error(std::declval())), error >::value > > : std::true_type { }; template constexpr auto construct_error_impl( is_convertible_to_error_using_traits>, Args&&... args ) noexcept(noexcept(error_traits::to_error(std::declval()...))) { return error_traits::to_error(std::forward(args)...); } template constexpr auto construct_error_impl( can_construct_error_from_adl, Args&&... args ) noexcept(noexcept(stdx_adl::detail::construct_error_from_adl(std::declval()...))) { return stdx_adl::detail::construct_error_from_adl(std::forward(args)...); } struct cannot_construct_error { static constexpr bool value = false; using type = void; }; template void construct_error_impl(cannot_construct_error, Args&&...) = delete; template using construct_error_disjunction_impl_t = disjunction< is_convertible_to_error_using_traits, can_construct_error_from_adl, cannot_construct_error >; template using construct_error_disjunction_t = construct_error_disjunction_impl_t< error_ctor_args, error_ctor_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::value > > constexpr error(const error_value& 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::value > > constexpr error(error_value&& v, const error_domain& d) noexcept : m_domain(&d), m_value(static_cast(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::value > > constexpr error(A&& a, Args&&... args) noexcept( noexcept( detail::construct_error_impl( std::declval>(), std::forward(a), std::forward(args)... ) ) ) : error( detail::construct_error_impl( detail::construct_error_disjunction_t{}, std::forward(a), std::forward(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(std::declval())) > > constexpr T error_cast(const error& e) noexcept( noexcept(detail::error_cast_impl(std::declval())) ) { return detail::error_cast_impl(detail::error_cref_access{e}.ref()); } template < class T, class = void_t(std::declval()))> > constexpr T error_cast(error&& e) noexcept( noexcept(detail::error_cast_impl(std::declval())) ) { return detail::error_cast_impl(detail::error_move_access{std::move(e)}.rvalue_ref()); } namespace detail { struct default_error_constructors { template static error_value<> copy_constructor(const error& e) noexcept( std::is_nothrow_copy_constructible::value && std::is_nothrow_move_constructible::value ) { T value = error_cast(e); return error_value<>{std::move(value)}; } template static error_value<> move_constructor(error&& e) noexcept( std::is_nothrow_move_constructible::value ) { return error_value<>{error_cast(std::move(e))}; } template static void destructor(error& e) noexcept { detail::erased_error& value = error_ref_access{e}.ref(); stdx::launder(reinterpret_cast(&value.storage))->~T(); } template constexpr static error_resource_management::copy_constructor copy() noexcept { return is_trivially_copy_constructible::value ? nullptr : ©_constructor; } template constexpr static error_resource_management::move_constructor move() noexcept { return is_trivially_move_constructible::value ? nullptr : &move_constructor; } template constexpr static error_resource_management::destructor destroy() noexcept { return is_trivially_destructible::value ? nullptr : &destructor; } }; } // end namespace detail template struct default_error_resource_management_t : error_resource_management { constexpr default_error_resource_management_t() noexcept : error_resource_management{ detail::default_error_constructors::copy(), detail::default_error_constructors::move(), detail::default_error_constructors::destroy() } {} }; #if defined(STDX_VARIABLE_TEMPLATES) template inline constexpr default_error_resource_management_t default_error_resource_management {}; #endif template <> struct error_traits { 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{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; friend struct error_traits; public: constexpr error_code_error_domain() noexcept : error_domain{ {0x84e99cdcecae4443ULL, 0x9050179b713fd2afULL}, default_error_resource_management_t{} } { } 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 { 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 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 ptr; }; template struct exception_ptr_wrapper_impl { 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; 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 : 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{} } { } 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(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{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 { 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{e}}, dynamic_exception_domain }; } }; } // end namespace stdx namespace std { template<> struct is_error_code_enum : std::true_type { }; } // end namespace std #endif #if __cplusplus >= 201703L #include #include #include #endif #include 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(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(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(lhs) == error_cast(rhs); } return false; } // namespace { inline 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(e)); } // ---------- ErrorCodeErrorDomain // inline string_ref error_code_error_domain::message(const error& e) const noexcept { assert(e.domain() == *this); auto ptr = error_cast(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(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(lhs); auto ptr2 = error_cast(rhs); if (ptr1 && ptr2) return ptr1->code == ptr2->code.default_error_condition(); return false; } if (rhs.domain() == generic_domain) { auto ptr1 = error_cast(lhs); if (ptr1) return ptr1->code == error_cast(rhs); } return false; } inline stdx::error error_traits::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{static_cast(ec.default_error_condition().value())}, generic_domain }; } return error{ error_value{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(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 { inline 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(lhs).get(); if (rhs.domain() == *this) { std::exception_ptr eptr2 = error_cast(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(lhs); if (rhs.domain() == *this) { return code == error_cast(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(error_cast(e))) }; } } // end namespace stdx #if defined(__clang__) && defined(__has_warning) #pragma clang diagnostic pop #endif