Login
Log in using an SSO provider:
Fedora Account System
Red Hat Associate
Red Hat Customer
Login using a Red Hat Bugzilla account
Forgot Password
Create an Account
Red Hat Bugzilla – Attachment 1749573 Details for
Bug 1918957
GCC Seg Fault
Home
New
Search
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh90 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
[?]
This site requires JavaScript to be enabled to function correctly, please enable it.
Godbolt Example
Compiler Explorer C++ Editor #1 Code - 2021-01-21T170849.318.cpp (text/plain), 195.38 KB, created by
jacob.gannon
on 2021-01-21 23:11:54 UTC
(
hide
)
Description:
Godbolt Example
Filename:
MIME Type:
Creator:
jacob.gannon
Created:
2021-01-21 23:11:54 UTC
Size:
195.38 KB
patch
obsolete
>#include <type_traits> >#include <memory> >#include <mutex> >#include <condition_variable> >#include <stdexcept> >#include <cstddef> >#include <functional> >#include <future> >#include <deque> >#include <vector> >#include <thread> > >namespace hsh >{ > /* enable_if_t (c++14 std::enable_if_t) */ > template<bool B, class T = void > > using enable_if_t = typename std::enable_if<B, T>::type; > > template <class T> > struct type_identity { using type = T; }; > > template <class T> > using type_identity_t = typename type_identity<T>::type; > > template<bool B, class T, class F > > using conditional_t = typename std::conditional<B, T, F>::type; > > /* decay_t (c++14 std::decay_t) */ > template<class T> > using decay_t = typename std::decay<T>::type; > > template <class T> > using add_cv_t = typename std::add_cv<T>::type; > > template< class T > > using add_const_t = typename std::add_const<T>::type; > > template< class T > > using add_volatile_t = typename std::add_volatile<T>::type; > > template<class T> > using add_pointer_t = typename std::add_pointer<T>::type; > > template<class T> > using add_lvalue_reference_t = typename std::add_lvalue_reference<T>::type; > > template<class T> > using add_rvalue_reference_t = typename std::add_rvalue_reference<T>::type; > > /* remove_reference_t (c++14 std::remove_reference_t ) */ > template<class T> > using remove_reference_t = typename std::remove_reference<T>::type; > > /* remove_cv_t (c++14 std::remove_cv_t ) */ > template< class T > > using remove_cv_t = typename std::remove_cv<T>::type; > > /* remove_const_t (c++14 std::remove_const_t ) */ > template< class T > > using remove_const_t = typename std::remove_const<T>::type; > > /* remove_volatile_t (c++14 std::remove_volatile_t ) */ > template<class T> > using remove_volatile_t = typename std::remove_volatile<T>::type; > > /* remove_cvref (C++20 std::remove_cvref) */ > template<class T> > struct remove_cvref { using type = hsh::remove_cv_t<hsh::remove_reference_t<T>>; }; > > /* remove_cvref_t (C++20 std::remove_cvref_t) */ > template< class T > > using remove_cvref_t = typename remove_cvref<T>::type; > > /* remove_pointer_t (C++20 std::remove_pointer_t) */ > template< class T > > using remove_pointer_t = typename std::remove_pointer<T>::type; > > /* remove_extent_t (C++ 14, std::remove_extent_t) */ > template< class T > > using remove_extent_t = typename std::remove_extent<T>::type; > > template<class T> > using remove_all_extents_t = typename std::remove_all_extents<T>::type; > > /* void_t (c++17 std::void_t) */ > template< class... > > using void_t = void; > > template< class T > > struct is_null_pointer : std::is_same<std::nullptr_t, hsh::remove_cv_t<T>> {}; > > template <bool B> > using bool_constant = std::integral_constant<bool, B>; > > template <size_t N> > using size_constant = std::integral_constant<size_t, N>; > > /* CONJUCTION > Performs a compile-time logical AND operation on the passed types (which must have `::value` members convertible to `bool`) > Short-circuits if it encounters any `false` members (and does not compare the `::value` members of any remaining arguments). > - This metafunction is designed to be a drop-in replacement for the C++17 std::conjunction metafunction. */ > template <typename... Ts> > struct conjunction; > > template <typename T, typename... Ts> > struct conjunction<T, Ts...> : std::conditional<T::value, conjunction<Ts...>, T>::type {}; > > template <typename T> > struct conjunction<T> : T {}; > > template <> > struct conjunction<> : std::true_type {}; > > /* DISJUNCTION > Performs a compile-time logical OR operation on the passed types (which must have `::value` members convertible to `bool`) > - This metafunction is designed to be a drop-in replacement for the C++17 std::disjunction metafunction. */ > template <typename... Ts> > struct disjunction; > > template<class T, class... Ts> > struct disjunction<T, Ts...> : std::conditional<bool(T::value), T, disjunction<Ts...>>::type { }; > > template<class T> > struct disjunction<T> : T { }; > > template<> > struct disjunction<> : std::false_type { }; > > /* NEGATION > Performs a compile-time logical NOT operation on the passed type (which must have `::value` members convertible to `bool`) > - This metafunction is designed to be a drop-in replacement for the C++17 `std::negation` metafunction. */ > template <typename T> > struct negation : bool_constant<!T::value> {}; > > /* is_trivially_destructible > - Used to determine if a class can be trivally-destructed, usefull since GCC 4.8 is missing the std version, a trivally destructible type doesn't need to have it's destructor called */ > template <typename T> > struct is_trivially_destructible : bool_constant<conjunction<bool_constant<__has_trivial_destructor(T)>, std::is_destructible<T>>::value> {}; > > template <typename T> > struct is_trivially_default_constructible > : bool_constant<conjunction<bool_constant<__has_trivial_constructor(T)>, std::is_default_constructible<T>, is_trivially_destructible<T>>::value> {}; > > template <class T> > struct is_copy_or_move_constructible : disjunction<std::is_move_constructible<T>, std::is_copy_constructible<T>> {}; > > template <class T> > struct is_copy_or_move_assignable : disjunction<std::is_move_assignable<T>, std::is_copy_assignable<T>> {}; > > /* > is_trivially_copyable > Workaround for GCC 4.8 bug(std::is_trivially_copyable is part of the c++ 11 standard > A type is trivially copyable if: > - At least one copy constructor, move constructor, copy assignment operator, or move assignment operator is eligible > - Every eligible copy constructor (if any) is trivial > - Every eligible move constructor (if any) is trivial > - Every eligible copy assignment operator (if any) is trivial > - Every eligible move assignment operator (if any) is trivial > - Has a trivial non-deleted destructor > */ > template <typename T> > struct is_trivially_copyable > : bool_constant<conjunction< > disjunction<bool_constant<__has_trivial_copy(remove_all_extents_t<T>)>, negation<is_copy_or_move_constructible<remove_all_extents_t<T>>>>, > disjunction<bool_constant<__has_trivial_assign(remove_all_extents_t<T>)>, negation<is_copy_or_move_assignable<remove_all_extents_t<T>>>>, > disjunction<is_copy_or_move_constructible<remove_all_extents_t<T>>, is_copy_or_move_assignable<remove_all_extents_t<T>>>, > is_trivially_destructible<remove_all_extents_t<T>>, > negation<std::is_reference<remove_all_extents_t<T>>>>::value>{}; > > template <typename T> > struct is_trivially_copy_constructible > : bool_constant<conjunction<std::is_copy_constructible<T>, bool_constant<__has_trivial_copy(T)>>::value> {}; > > template <typename T> > struct is_trivially_move_constructible : bool_constant<conjunction<std::is_move_constructible<T>, bool_constant<__has_trivial_copy(T)>>::value> {}; > > template <typename T> > struct is_trivially_copy_assignable > : bool_constant<conjunction<std::is_copy_assignable<T>, bool_constant<__has_trivial_assign(T)>>::value> {}; > > template <typename T> > struct is_trivially_move_assignable > : bool_constant<conjunction<std::is_move_assignable<T>, bool_constant<__has_trivial_assign(T)>>::value> {}; > > > /* Type Trait to verify if T is a std::array */ > template <typename> > struct is_std_array : std::false_type {}; > > template <typename T, size_t N> > struct is_std_array<std::array<T, N>> : std::true_type {}; > > /* C++ 20 std::is_unbounded_array */ > template<class> > struct is_unbounded_array : std::false_type {}; > template<class T> > struct is_unbounded_array<T[]> : std::true_type {}; > > /* C++ 20 std::is_bounded_array */ > template<class> > struct is_bounded_array : std::false_type {}; > template<class T, std::size_t N> > struct is_bounded_array<T[N]> : std::true_type {}; > > template<typename T> > struct is_reference_wrapper : std::false_type {}; > > template<typename T> > struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {}; > > /* The following copy_traits, can be used to disable the copy/move Constructor/Assignment Operator of a template class. > This requires that: > - class inherits from ctor_base<ctor_copy_traits<T>::traits> > - class inherits from assign_base<assign_copy_traits<T>::traits> > - class implements copy/move constructor and assignment operators to be = default; > */ > enum class copy_traits { copy = 0, move = 1, none = 2 }; > > /* Base class for enabling/disabling copy/move construction. */ > template <copy_traits> > class ctor_base; > > template<> > struct ctor_base<copy_traits::copy> > { > constexpr ctor_base() noexcept = default; > constexpr ctor_base(const ctor_base&) = default; > constexpr ctor_base(ctor_base&&) = default; > ctor_base& operator=(const ctor_base&) noexcept = default; > ctor_base& operator=(ctor_base&&) noexcept = default; > }; > > template<> > struct ctor_base<copy_traits::move> > { > constexpr ctor_base() noexcept = default; > constexpr ctor_base(const ctor_base&) = delete; > constexpr ctor_base(ctor_base&&) = default; > ctor_base& operator=(const ctor_base&) noexcept = default; > ctor_base& operator=(ctor_base&&) noexcept = default; > }; > > template<> > struct ctor_base<copy_traits::none> > { > constexpr ctor_base() noexcept = default; > constexpr ctor_base(const ctor_base&) = delete; > constexpr ctor_base(ctor_base&&) = delete; > ctor_base& operator=(const ctor_base&) noexcept = default; > ctor_base& operator=(ctor_base&&) noexcept = default; > }; > > /* Base class for enabling/disabling copy/move assignment. */ > template <copy_traits> > class assign_base; > > template<> > struct assign_base<copy_traits::copy> > { > constexpr assign_base() noexcept = default; > constexpr assign_base(const assign_base&) = default; > constexpr assign_base(assign_base&&) = default; > assign_base& operator=(const assign_base&) noexcept = default; > assign_base& operator=(assign_base&&) noexcept = default; > }; > > template<> > struct assign_base<copy_traits::move> > { > constexpr assign_base() noexcept = default; > constexpr assign_base(const assign_base&) = default; > constexpr assign_base(assign_base&&) = default; > assign_base& operator=(const assign_base&) noexcept = delete; > assign_base& operator=(assign_base&&) noexcept = default; > }; > > template<> > struct assign_base<copy_traits::none> > { > constexpr assign_base() noexcept = default; > constexpr assign_base(const assign_base&) = default; > constexpr assign_base(assign_base&&) = default; > assign_base& operator=(const assign_base&) noexcept = delete; > assign_base& operator=(assign_base&&) noexcept = delete; > }; > > template <typename T> > struct ctor_copy_traits { > static constexpr copy_traits traits = > std::is_copy_constructible<T>::value ? copy_traits::copy : std::is_move_constructible<T>::value ? copy_traits::move : copy_traits::none; > }; > > template <typename T> > struct assign_copy_traits { > static constexpr copy_traits traits = > hsh::conjunction<std::is_copy_assignable<T>, std::is_copy_constructible<T>>::value > ? copy_traits::copy : hsh::conjunction<std::is_move_assignable<T>, std::is_move_constructible<T>>::value ? copy_traits::move : copy_traits::none; > }; > > /* The following operation_support_trait, can be used to determine if a Parameter pack of Types has trivial, is availible, or isn't availible for a given Type Attribute. */ > enum class operation_support_trait { trivial = 0, available = 1, unavailable = 2 }; > > template <typename... Types> > struct copy_constructor_traits > { > static constexpr operation_support_trait traits = conjunction<is_trivially_copy_constructible<Types>...>::value ? > operation_support_trait::trivial : conjunction<std::is_copy_constructible<Types>...>::value ? operation_support_trait::available : operation_support_trait::unavailable; > }; > > template <typename... Types> > struct move_constructor_traits > { > static constexpr operation_support_trait traits = conjunction<is_trivially_move_constructible<Types>...>::value ? > operation_support_trait::trivial : conjunction<std::is_move_constructible<Types>...>::value ? operation_support_trait::available : operation_support_trait::unavailable; > }; > > template <typename... Types> > struct copy_assignment_traits > { > static constexpr operation_support_trait traits = conjunction<is_trivially_copy_assignable<Types>...>::value ? > operation_support_trait::trivial : conjunction<std::is_copy_assignable<Types>...>::value ? operation_support_trait::available : operation_support_trait::unavailable; > }; > > template <typename... Types> > struct move_assignment_traits > { > static constexpr operation_support_trait traits = conjunction<is_trivially_move_assignable<Types>...>::value ? > operation_support_trait::trivial : conjunction<std::is_move_assignable<Types>...>::value ? operation_support_trait::available : operation_support_trait::unavailable; > }; > > template<typename... Types> > struct destructor_traits > { > static constexpr operation_support_trait traits = conjunction<is_trivially_destructible<Types>...>::value ? > operation_support_trait::trivial : conjunction<std::is_destructible<Types>...>::value ? operation_support_trait::available : operation_support_trait::unavailable; > }; > > /* Type that can be used with SNIFAE to denote a failure case, no non type trait should actually return this type or it's undefined behavior */ > struct unused {}; > > namespace detail > { > template <typename T> > constexpr T&& fwd(hsh::remove_reference_t<T>& t) noexcept { return static_cast<T&&>(t); } > > enum class invoke_arg_type { object = 0, ref_wrapper = 1, pointer_type = 2 }; > > template<typename C, typename T> > struct invoke_arg_type_trait > { > static constexpr invoke_arg_type value = std::is_base_of<C, decay_t<T>>::value ? invoke_arg_type::object > : is_reference_wrapper<decay_t<T>>::value ? invoke_arg_type::ref_wrapper : invoke_arg_type::pointer_type; > }; > > template<bool IsFunctionPtr, invoke_arg_type> > struct Invoker {}; > > template<> > struct Invoker<true, invoke_arg_type::object> > { > template <typename R, typename C, typename T, typename... Args> > inline static constexpr auto invoke(R C::*func, T &&obj, Args &&... args) noexcept(noexcept((fwd<T>(obj).*func)(fwd<Args>(args)...))) > -> decltype((fwd<T>(obj).*func)(fwd<Args>(args)...)) > { > return (fwd<T>(obj).*func)(fwd<Args>(args)...); > } > }; > > template<> > struct Invoker<true, invoke_arg_type::ref_wrapper> > { > template <typename R, typename C, typename T, typename... Args> > inline static constexpr auto invoke(R C::*func, T &&obj, Args&&... args) noexcept(noexcept((fwd<T>(obj).get().*func)(fwd<Args>(args)...))) > -> decltype((fwd<T>(obj).get().*func)(fwd<Args>(args)...)) > { > return (obj.get().*func)(fwd<Args>(args)...); > } > }; > > template<> > struct Invoker<true, invoke_arg_type::pointer_type> > { > template <typename R, typename C, typename T, typename... Args> > inline static constexpr auto invoke(R C::*func, T &&obj, Args &&... args) noexcept(noexcept(((*fwd<T>(obj)).*func)(fwd<Args>(args)...))) > -> decltype(((*fwd<T>(obj)).*func)(fwd<Args>(args)...)) > { > return ((*fwd<T>(obj)).*func)(fwd<Args>(args)...); > } > }; > > template<> > struct Invoker<false, invoke_arg_type::object> > { > template <typename R, typename C, typename T> > inline static constexpr auto invoke(R C::*member, T &&obj) noexcept -> decltype(fwd<T>(obj).*member){ > return fwd<T>(obj).*member; > } > }; > > template<> > struct Invoker<false, invoke_arg_type::ref_wrapper> > { > template <typename R, typename C, typename T> > inline static constexpr auto invoke(R C::*member, T &&obj) noexcept -> decltype(fwd<T>(obj).get().*member){ > return obj.get().*member; > } > }; > > template<> > struct Invoker<false, invoke_arg_type::pointer_type> > { > template <typename R, typename C, typename T> > inline static constexpr auto invoke(R C::*member, T &&obj) noexcept -> decltype((*fwd<T>(obj)).*member){ > return (*fwd<T>(obj)).*member; > } > }; > > template <typename R, typename C, typename T, typename... Args, typename Invker = Invoker<std::is_function<R>::value, invoke_arg_type_trait<C, T>::value>> > inline constexpr auto invoke_impl(R C::*f, T &&obj, Args &&... args) noexcept(noexcept(Invker::invoke(f, fwd<T>(obj), fwd<Args>(args)...))) > -> decltype(Invker::invoke(f, fwd<T>(obj), fwd<Args>(args)...)) > { > return Invker::invoke(f, fwd<T>(obj), fwd<Args>(args)...); > } > > template <typename F, typename... Args> > inline constexpr auto invoke_impl(F&& f, Args &&... args) noexcept(noexcept(fwd<F>(f)(fwd<Args>(args)...))) > -> decltype(fwd<F>(f)(fwd<Args>(args)...)) > { > return fwd<F>(f)(fwd<Args>(args)...); > } > > template <class Ret, class F, class ...Args> > struct invokable_r > { > template <class Fn, class ...Args2> > static auto try_call(int) -> decltype(invoke_impl(std::declval<Fn>(), std::declval<Args2>()...)); > > template <class Fn, class ...Args2> > static unused try_call(...); > > using Result = decltype(try_call<F, Args...>(0)); > > static constexpr bool value = conditional_t<negation<std::is_same<Result, unused>>::value, > conditional_t<std::is_void<Ret>::value, std::true_type, std::is_convertible<Result, Ret>>, std::false_type>::value; > }; > > template <class F, class ...Args> > struct invoke_of : public std::enable_if<invokable_r<void, F, Args...>::value, typename invokable_r<void, F, Args...>::Result> {}; > > template <typename F, typename... Args> > struct is_invoke_nothrow : bool_constant<noexcept(invoke_impl(std::declval<decay_t<F>>(), std::declval<Args>()...))> {}; > } > > /* C++ 17 invoke_result and invoke_result_t */ > template <typename F, typename... Args> > using invoke_result = detail::invoke_of<F, Args...>; > > template <typename F, typename... Args> > using invoke_result_t = typename invoke_result<F, Args...>::type; > > /* C++ 17 is_invocable, is_invocable_r */ > template <class F, class ...Args> > using is_invocable = detail::invokable_r<void, F, Args...>; > > template <class Ret, class F, class ...Args> > using is_invocable_r = detail::invokable_r<Ret, F, Args...>; > > /* C++ 17 is_nothrow_invocable, is_nothrow_invocable_r > Note: If running with a pre C++ 17 compiler these will return false for pointers to member function that are noexcept > This was a language problem that was fixed in C++ 17, and there aren't any workarounds. is_nothrow_invocable will still work for functors, lambdas, pointers to members, ete */ > template <typename F, typename... Args> > struct is_nothrow_invocable : conjunction<is_invocable<F, Args...>, detail::is_invoke_nothrow<F, Args...>> {}; > > template <typename R, typename F, typename... Args> > struct is_nothrow_invocable_r : conjunction<is_invocable_r<R, F, Args...>, detail::is_invoke_nothrow<F, Args...>> {}; > > /* Array that can be used in TypeTraits, because it's constexpr */ > template <typename T, std::size_t N> > struct static_array > { > constexpr const T &operator[](std::size_t index) const { return data[index]; } > T data[N == 0 ? 1 : N]; > }; > > /* Static Max/Min: These are usefull for getting max or min from a parameter pack, i.e. static_max<sizeof(Types)...>::value */ > template <size_t I0, size_t ...in> > struct static_min; > > template <size_t I0> > struct static_min<I0> { static constexpr size_t value = I0; }; > > template <size_t I0, size_t I1, size_t ...in> > struct static_min<I0, I1, in...> { > static constexpr size_t value = I0 <= I1 ? static_min<I0, in...>::value : static_min<I1, in...>::value; > }; > > template <size_t I0, size_t ... in> > struct static_max; > > template <size_t I0> > struct static_max<I0> { static constexpr size_t value = I0; }; > > template <size_t I0, size_t I1, size_t ... in> > struct static_max<I0, I1, in...> { > static constexpr size_t value = I0 >= I1 ? static_max<I0, in...>::value : static_max<I1, in...>::value; > }; > > constexpr bool static_conjunction(bool b) noexcept { return b; } > > template <typename... Bs> > constexpr bool static_conjunction(bool b, Bs... bs) noexcept { return b ? static_conjunction(bs...) : false; } > > > /* C++ 14 std::aligned_storage_t */ > template <size_t N, size_t Align = alignof(void*)> > using aligned_storage_t = typename std::aligned_storage<N, Align>::type; > > /* Usefull type trait when you need aligned_storage for a given type T */ > template <class T> > using aligned_storage_for_t = aligned_storage_t<sizeof(T), alignof(T)>; > > /* C++ 11 std::aligned_union (Missing on GCC 4.8.x) */ > template <std::size_t Len, class... Types> > struct aligned_union > { > static constexpr std::size_t alignment_value = static_max<alignof(Types)...>::value; > using type = aligned_storage_t<static_max<Len, sizeof(Types)...>::value, alignment_value>; > }; > > /* C++ 14 std::aligned_union_t */ > template< std::size_t Len, class... Types > > using aligned_union_t = typename aligned_union<Len, Types...>::type; > > /* Usefull type trait for when you need an aligned_union for a given parameter pack Types... */ > template<class... Types> > using aligned_union_for_t = aligned_union_t<static_max<sizeof(Types)...>::value, Types...>; > > namespace detail > { > namespace swappable //extra namespace bc we need to have a using std::swap statement for ADL lookup > { > using std::swap; > > template <class T, class U = T, bool NotVoid = conjunction<negation<std::is_void<T>>, negation<std::is_void<U>>>::value> > struct swappable_with > { > template <class LHS, class RHS> > static decltype(swap(std::declval<LHS>(), std::declval<RHS>())) test_swap(int); > > template <class, class> > static unused test_swap(long); > > static constexpr bool value = conjunction<negation<std::is_same<decltype(test_swap<T, U>(0)), unused>>, negation<std::is_same<decltype(test_swap<U, T>(0)), unused>>>::value; > }; > > template <class T, class U> > struct swappable_with<T, U, false> : std::false_type {}; > > template <class T, class U = T, bool Swappable = swappable_with<T, U>::value> > struct nothrow_swappable_with > : conjunction<bool_constant<noexcept(swap(std::declval<T>(), std::declval<U>()))>, bool_constant<noexcept(swap(std::declval<U>(), std::declval<T>()))>> {}; > > template <class T, class U> > struct nothrow_swappable_with<T, U, false> : std::false_type {}; > } > } > > /* C++ 17 is_swappable_with */ > template <class T, class U> > using is_swappable_with = detail::swappable::swappable_with<T, U>; > > /* C++ 17 is_nothrow_swappable_with */ > template< class T, class U > > using is_nothrow_swappable_with = detail::swappable::nothrow_swappable_with<T, U>; > > /* C++ 17 is_swappable */ > template <class T> > using is_swappable = detail::swappable::swappable_with<T&>; > > /* C++ 17 is_nothrow_swappable */ > template <class T> > using is_nothrow_swappable = detail::swappable::nothrow_swappable_with<T&>; > > namespace detail > { > template <class T> > struct hashable > { > template <class X> > static decltype(std::declval<std::hash<X>>()(std::declval<X>())) test_hash(int); > > template <class> > static unused test_hash(long); > > static constexpr bool value = negation<std::is_same<decltype(test_hash<T>(0)), unused>>::value; > }; > > template <class T, bool = hashable<T>::value> > struct no_throw_hashable : bool_constant<noexcept(std::declval<std::hash<T>>()(std::declval<T>()))> {}; > > template<class T> > struct no_throw_hashable<T, false> : std::false_type {}; > } > > template <class T> > using is_hashable = detail::hashable<T>; > > template <class T> > using is_nothrow_hashable = detail::no_throw_hashable<T>; > >/* C++ 14's std::make_unique, should really be in a memory header, but there's not enough other things to make one */ > template<class T, class... Args> > enable_if_t<!std::is_array<T>::value, std::unique_ptr<T>> make_unique(Args&&... args) { > return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); > } > > template<class T> > enable_if_t<is_unbounded_array<T>::value, std::unique_ptr<T>> make_unique(std::size_t n) { > return std::unique_ptr<T>(new remove_extent_t<T>[n]()); > } > > template<class T, class... Args> > enable_if_t<is_bounded_array<T>::value> make_unique(Args&&...) = delete; > > /* C++ 14's version of move (adds constexpr) */ > template <typename T> > constexpr hsh::remove_reference_t<T>&& move(T&& t) noexcept { > return static_cast<hsh::remove_reference_t<T>&&>(t); > } > > /* C++ 14's version of std::forward (adds constexpr) */ > template <typename T> > constexpr T&& forward(hsh::remove_reference_t<T>& t) noexcept { return static_cast<T&&>(t); } > > /* C++ 14's std::exchange */ > template<class T, class U = T> > T exchange( T& obj, U&& new_value ) > { > T old_value = hsh::move(obj); > obj = hsh::forward<U>(new_value); > return old_value; > } > > /* constexpr version of std::addressof */ > template<typename T> > inline constexpr T* addressof(T& value) noexcept > { > return reinterpret_cast<T*>(&const_cast<char&>(reinterpret_cast<const volatile char&>(value))); > } > > template<typename T> > inline constexpr const T* addressof(const T&& value) noexcept = delete; > > /* C++ 17's std::as_const */ > template <class T> > constexpr typename std::add_const<T>::type& as_const(T& t) noexcept { return t; } > > template <class T> > void as_const(const T&&) = delete; > > /* C++ 17's std::nullopt_t */ > struct nullopt_t { explicit constexpr nullopt_t(int) {} }; > constexpr nullopt_t nullopt{ 1 }; > > /* C++ 17's std::in_place_t */ > struct in_place_t { explicit in_place_t() = default; }; > constexpr in_place_t in_place{}; > > namespace detail > { > template <typename T> > struct in_place_type_tag > { > explicit in_place_type_tag() = delete; > in_place_type_tag(const in_place_type_tag&) = delete; > in_place_type_tag& operator=(const in_place_type_tag&) = delete; > }; > > template <size_t I> > struct in_place_index_tag > { > explicit in_place_index_tag() = delete; > in_place_index_tag(const in_place_index_tag&) = delete; > in_place_index_tag& operator=(const in_place_index_tag&) = delete; > }; > > } > > /* C++ 17's std::monostate, it's in utility instead of variant bc it's also needed by optional */ > struct monostate {}; > constexpr bool operator> (monostate, monostate) noexcept { return false; } > constexpr bool operator< (monostate, monostate) noexcept { return false; } > constexpr bool operator!=(monostate, monostate) noexcept { return false; } > constexpr bool operator<=(monostate, monostate) noexcept { return true; } > constexpr bool operator>=(monostate, monostate) noexcept { return true; } > constexpr bool operator==(monostate, monostate) noexcept { return true; } > > /* C++ 17's std::in_place_type_t */ > template <typename T> > using in_place_type_t = void(*)(detail::in_place_type_tag<T>); > > template <typename T> > inline void in_place_type(detail::in_place_type_tag<T>) {} > > /* C++ 17's std::in_place_index_t */ > template <size_t I> > using in_place_index_t = void(*)(detail::in_place_index_tag<I>); > > template <size_t I> > inline void in_place_index(detail::in_place_index_tag<I>) {} > > /* A tag that can be used to specify a given iterator range is ordered, this can be an optimization for algorithms to avoid unneeded sorting */ > struct ordered_range_t { explicit ordered_range_t() = default; }; > constexpr ordered_range_t ordered_range{}; > > /* A tag that can be used to specify a given iterator range is ordered and unique, this can be an optimization for algorithms to avoid unneeded sorting and removals */ > struct ordered_unique_range_t : public ordered_range_t { explicit ordered_unique_range_t() = default; }; > constexpr ordered_unique_range_t ordered_unique_range{}; > > template <typename T, T... Ints> > class integer_sequence > { > public: > using value_type = T; > static_assert(std::is_integral<T>::value, "integer_sequence can only be instantiated with an integral type"); > static constexpr size_t size() noexcept { return sizeof...(Ints); } > }; > > namespace detail > { > template <size_t N, typename IndexSeq> > struct make_index_sequence_impl; > > template <size_t N, size_t... Is> > struct make_index_sequence_impl<N, integer_sequence<size_t, Is...>> > { > using type = typename make_index_sequence_impl<N - 1, integer_sequence<size_t, N - 1, Is...>>::type; > }; > > template <size_t... Is> > struct make_index_sequence_impl<0, integer_sequence<size_t, Is...>> > { > using type = integer_sequence<size_t, Is...>; > }; > > template <typename Target, typename Seq> > struct integer_sequence_convert_impl; > > template <typename Target, size_t... Is> > struct integer_sequence_convert_impl<Target, integer_sequence<size_t, Is...>> > { > using type = integer_sequence<Target, Is...>; > }; > > template <typename T, size_t N> > struct make_integer_sequence_impl > { > using type = typename integer_sequence_convert_impl<T, typename make_index_sequence_impl<N, integer_sequence<size_t>>::type>::type; > }; > } // end namespace detail > > template <size_t... Is> > using index_sequence = integer_sequence<size_t, Is...>; > > template <size_t N> > using make_index_sequence = typename detail::make_index_sequence_impl<N, integer_sequence<size_t>>::type; > > template <typename T, size_t N> > using make_integer_sequence = typename detail::make_integer_sequence_impl<T, N>::type; > > template<typename... T> > using index_sequence_for = make_index_sequence<sizeof...(T)>; > > namespace detail > { > template<typename T> > void hash_combine(size_t& seed, const T& val) > { > seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2); > } > } > > /* based on std proposal: P0814R0( hash_combine() Again ) */ > template<typename... Types> > size_t hash_combine(const Types&... args) noexcept(hsh::conjunction<hsh::is_nothrow_hashable<Types>...>::value) > { > size_t seed = 0; > auto l = { (detail::hash_combine(seed, args),0)... }; > (void)l; // cast to avoid unused variable warning > return seed; > } > > > /* Dual type functions */ > template <typename T, typename U> > struct equal_to_2 > { > constexpr bool operator()(const T& a, const U& b) const { return a == b; } > constexpr bool operator()(const U& b, const T& a) const { return b == a; } > }; > > template <typename T> > struct equal_to_2<T, T> : public std::equal_to<T>{}; > > template <typename T, typename U> > struct not_equal_to_2 > { > constexpr bool operator()(const T& a, const U& b) const { return a != b; } > constexpr bool operator()(const U& b, const T& a) const { return b != a; } > }; > > template <typename T> > struct not_equal_to_2<T, T> : public std::not_equal_to<T>{}; > > template <typename T, typename U> > struct greater_2 > { > constexpr bool operator()(const T& a, const U& b) const { return a > b; } > constexpr bool operator()(const U& b, const T& a) const { return b > a; } > }; > > template <typename T> > struct greater_2<T, T> : public std::greater<T>{}; > > template <typename T, typename U> > struct less_2 > { > constexpr bool operator()(const T& a, const U& b) const { return a < b; } > constexpr bool operator()(const U& b, const T& a) const { return b < a; } > }; > > template <typename T> > struct less_2<T, T> : public std::less<T>{}; > > template <typename T, typename U> > struct greater_equal_2 > { > constexpr bool operator()(const T& a, const U& b) const { return a >= b; } > constexpr bool operator()(const U& b, const T& a) const { return b >= a; } > }; > > template <typename T> > struct greater_equal_2<T, T> : public std::greater_equal<T>{}; > > template <typename T, typename U> > struct less_equal_2 > { > constexpr bool operator()(const T& a, const U& b) const { return a <= b; } > constexpr bool operator()(const U& b, const T& a) const { return b <= a; } > }; > > template <typename T> > struct less_equal_2<T, T> : public std::less_equal<T>{}; > > template <typename T, typename U> > struct logical_or_2 > { > constexpr bool operator()(const T& a, const U& b) const { return a || b; } > constexpr bool operator()(const U& b, const T& a) const { return b || a; } > }; > > template <typename T> > struct logical_or_2<T, T> : public std::logical_or<T> {}; > > template <typename T, typename U> > struct logical_and_2 > { > constexpr bool operator()(const T& a, const U& b) const { return a && b; } > constexpr bool operator()(const U& b, const T& a) const { return b && a; } > }; > > template <typename T> > struct logical_and_2<T, T> : public std::logical_and<T> {}; > > /* Adapts a less functor, i.e std::less, to be a equal functor, i.e. std::equal_to */ > template <typename Compare> > struct less_to_equal_to > { > explicit less_to_equal_to(const Compare& cmp) noexcept(std::is_nothrow_copy_constructible<Compare>::value) : cmp(cmp) {} > explicit less_to_equal_to(Compare&& cmp) noexcept(std::is_nothrow_move_constructible<Compare>::value) : cmp(std::move(cmp)) {} > > template <typename T> > bool operator()(const T& a, const T& b) const noexcept(hsh::is_nothrow_invocable<Compare, const T&,const T&>::value) > { > return !cmp(a, b) && !cmp(b, a); > } > > private: > Compare cmp; > }; > > struct first_element > { > template<typename T, typename U> > constexpr const T& operator()(const std::pair<T, U>& pair) const noexcept(std::is_nothrow_copy_constructible<T>::value) { return pair.first; } > > template<typename T, typename U> > constexpr T&& operator()(std::pair<T, U>&& pair) const noexcept(std::is_nothrow_move_constructible<T>::value) { return hsh::move(pair.first); } > }; > > struct second_element > { > template<typename T, typename U> > constexpr const U& operator()(const std::pair<T, U>& pair) const noexcept(std::is_nothrow_copy_constructible<U>::value) { return pair.second; } > > template<typename T, typename U> > constexpr U&& operator()(std::pair<T, U>&& pair) const noexcept(std::is_nothrow_move_constructible<U>::value) { return hsh::move(pair.second); } > }; > > /* Compare Adaptor that will use the first element of a std::pair as an arguement to the comparator */ > template <typename Compare> > struct compare_first_element > { > explicit compare_first_element(const Compare &comp) noexcept(std::is_nothrow_copy_constructible<Compare>::value) : comp(comp) {} > explicit compare_first_element(Compare &&comp) noexcept(std::is_nothrow_move_constructible<Compare>::value) : comp(std::move(comp)) {} > > template <typename Key, typename V, typename K> > inline bool operator()(const std::pair<Key, V>& lhs, const K& rhs) const noexcept(hsh::is_nothrow_invocable<Compare, const Key&, const K&>::value) > { > return comp(lhs.first, rhs); > } > > template <typename Key, typename V, typename K> > inline bool operator()(const K& lhs, const std::pair<Key, V>& rhs) const noexcept(hsh::is_nothrow_invocable<Compare, const K&, const Key&>::value) > { > return comp(lhs, rhs.first); > } > > private: > Compare comp; > }; > > /* Compare Adaptor that will use the second element of a std::pair as an arguement to the comparator */ > template <typename Compare> > struct compare_second_element > { > explicit compare_second_element(const Compare &comp) noexcept(std::is_nothrow_copy_constructible<Compare>::value) : comp(comp) {} > explicit compare_second_element(Compare &&comp) noexcept(std::is_nothrow_move_constructible<Compare>::value) : comp(std::move(comp)) {} > > template <typename K, typename V> > inline bool operator()(const std::pair<K, V>& lhs, const V& rhs) const noexcept(hsh::is_nothrow_invocable<Compare, const V&, const V&>::value) > { > return comp(lhs.second, rhs); > } > > template <typename K, typename V> > inline bool operator()(const V& lhs, const std::pair<K, V>& rhs) const noexcept(hsh::is_nothrow_invocable<Compare, const V&, const V&>::value) > { > return comp(lhs, rhs.second); > } > > private: > Compare comp; > }; > > /* --- C++ 17 std::invoke --- */ > template <typename F, typename... Args> > inline auto invoke(F&& func, Args&&... args) noexcept(noexcept(detail::invoke_impl(std::forward<F>(func), std::forward<Args>(args)...))) > -> decltype(hsh::detail::invoke_impl(std::forward<F>(func), std::forward<Args>(args)...)) > { > return hsh::detail::invoke_impl(std::forward<F>(func), std::forward<Args>(args)...); > } > > namespace detail > { > template <class... Ts> > struct overloader; > > template <class F, class... FRest> > struct overloader<F, FRest...> : F, overloader<FRest...> > { > overloader(F&& f, FRest&&... rest) : F(std::forward<F>(f)), overloader<FRest...>(std::forward<FRest>(rest)...) {} > > using F::operator(); > using overloader<FRest...>::operator(); > }; > > template <class F> > struct overloader<F> : F > { > explicit overloader(F&& f) : F(std::forward<F>(f)) {} > > using F::operator(); > }; > } > > /* std::overload (C++ 23) */ > template <class... Ts> > auto overload(Ts&&... ts) -> decltype(detail::overloader<hsh::decay_t<Ts>...>(std::forward<Ts>(ts)...)) > { > return detail::overloader<hsh::decay_t<Ts>...>{ std::forward<Ts>(ts)...}; > } > > > namespace detail > { > template <class F, class Tuple, size_t... I> > auto apply_impl(F&& f, Tuple&& t, index_sequence<I...>) noexcept(noexcept(hsh::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...))) > -> decltype(hsh::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...)) > { > return hsh::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...); > } > } > > /* C++ 17 std::apply */ > template <class F, class Tuple> > auto apply(F&& f, Tuple&& t) noexcept(noexcept(detail::apply_impl(std::forward<F>(f), std::forward<Tuple>(t), hsh::make_index_sequence<std::tuple_size<hsh::decay_t<Tuple>>::value>{}))) > -> decltype(detail::apply_impl(std::forward<F>(f), std::forward<Tuple>(t), hsh::make_index_sequence<std::tuple_size<hsh::decay_t<Tuple>>::value>{})) > { > return detail::apply_impl(std::forward<F>(f), std::forward<Tuple>(t), > hsh::make_index_sequence<std::tuple_size<hsh::decay_t<Tuple>>::value>{}); > } > > namespace detail > { > /* Helper function that calls invoke, unpacks tuple, and forwards parameter pack (basically hsh::apply that also forwards a parameter pack) */ > template <size_t... I, class F, class Tuple, class... Args> > auto call_front_binder(hsh::index_sequence<I...>, F&& f, Tuple&& tpl, Args&&... args) > noexcept(noexcept(hsh::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(tpl))..., std::forward<Args>(args)...))) > -> decltype(hsh::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(tpl))..., std::forward<Args>(args)...)) > { > return hsh::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(tpl))..., std::forward<Args>(args)...); > } > > /* Empty Base Optimization class so empty functors/lambdas don't take up any space in front_binder */ > template<class F, bool = std::is_empty<F>::value> > class front_binder_base > { > public: > explicit front_binder_base(const F& f) : _func(f) {} > explicit front_binder_base(F&& f) : _func(std::forward<F>(f)) {} > > F& function() { return _func; } > const F& function() const { return _func; } > > F _func; > }; > > template <class F> > class front_binder_base<F, true> : public F > { > public: > explicit front_binder_base(const F& f) : F(f) {} > explicit front_binder_base(F&& f) : F(std::forward<F>(f)) {} > > F& function() { return *this; } > const F& function() const { return *this; } > }; > > template <class F, class... BoundArgs> > class front_binder > { > static_assert(std::is_constructible<hsh::decay_t<F>, F>::value, > "bind_front() requires the decayed callable to be constructible from an undecayed callable"); > static_assert(std::is_move_constructible<hsh::decay_t<F>>::value, > "bind_front() requires the decayed callable to be move constructible"); > static_assert(hsh::conjunction<std::is_constructible<hsh::decay_t<BoundArgs>, BoundArgs>...>::value, > "bind_front() requires the decayed bound arguments to be constructible from undecayed bound arguments"); > static_assert(hsh::conjunction<std::is_move_constructible<hsh::decay_t<BoundArgs>>...>::value, > "bind_front() requires the decayed bound arguments to be move constructible"); > > using func_t = hsh::decay_t<F>; > using bound_args_t = std::tuple<hsh::decay_t<BoundArgs>...>; > using seq = hsh::index_sequence_for<BoundArgs...>; > > F f; > bound_args_t bound_args; > > public: > template <class F1, class... BoundArgs1, enable_if_t<sizeof...(BoundArgs1) != 0 || !std::is_same<remove_cvref_t<F1>, front_binder>::value>* = nullptr> > explicit front_binder(F1&& f, BoundArgs1&&... args) : f(std::forward<F1>(f)), bound_args(std::forward<BoundArgs1>(args)...) {} > > template <typename... Args> > auto operator()(Args&&... args) & -> decltype(call_front_binder(seq{}, f, bound_args, std::forward<Args>(args)...)) > { > return call_front_binder(seq{}, f, bound_args, std::forward<Args>(args)...); > } > > template <typename... Args> > auto operator()(Args&&... args) const& -> decltype(call_front_binder(seq{}, f, bound_args, std::forward<Args>(args)...)) > { > return call_front_binder(seq{}, f, bound_args, std::forward<Args>(args)...); > } > > template <typename... Args> > auto operator()(Args&&... args) && -> decltype(call_front_binder(seq{}, f, std::move(bound_args), std::forward<Args>(args)...)) > { > return call_front_binder(seq{}, f, std::move(bound_args), std::forward<Args>(args)...); > } > > template <typename... Args> > auto operator()(Args&&... args) const&& -> decltype(call_front_binder(seq{}, f, std::move(bound_args), std::forward<Args>(args)...)) > { > return call_front_binder(seq{}, f, std::move(bound_args), std::forward<Args>(args)...); > } > }; > } //end of namespace detail > > /* C++ 20's bind_front */ > template <class F, class... BoundArgs> > auto bind_front(F&& f, BoundArgs&&... args) -> detail::front_binder<decay_t<F>, decay_t<BoundArgs>...> > { > return detail::front_binder<decay_t<F>, decay_t<BoundArgs>...>(std::forward<F>(f), std::forward<BoundArgs>(args)...); > } > > /* specialization for empty parameter packs */ > template<typename F> > void for_each_parameter_pack(F&& f){} > > template<typename F, typename... Args> > void for_each_parameter_pack(F&& f, Args&&... args) > { > auto l = { (hsh::invoke(std::forward<F>(f), args),0)... }; > (void)l; // cast to avoid unused variable warning > } > > > namespace detail > { > template<typename T, typename F, size_t... I> > void for_each_tuple_element(F&& f, T&& t, index_sequence<I...>) > { > for_each_parameter_pack(std::forward<F>(f), std::get<I>(std::forward<T>(t))...); > } > } > > template<typename F, typename... Ts> > void for_each_in_tuple(F&& f, std::tuple<Ts...>& t) > { > detail::for_each_tuple_element(std::forward<F>(f), t, hsh::index_sequence_for<Ts...>{}); > } > > template<typename F, typename... Ts> > void for_each_in_tuple(F&& f, const std::tuple<Ts...>& t) > { > detail::for_each_tuple_element(std::forward<F>(f), t, hsh::index_sequence_for<Ts...>{}); > } > > template<typename F, typename... Ts> > void for_each_in_tuple(F&& f, std::tuple<Ts...>&& t) > { > detail::for_each_tuple_element(std::forward<F>(f), std::move(t), hsh::index_sequence_for<Ts...>{}); > } > > template <class T> > class optional; > > namespace optional_details > { > template <class T> struct is_optional_impl : std::false_type {}; > template <class T> struct is_optional_impl<optional<T>> : std::true_type {}; > template <class T> using is_optional = is_optional_impl<hsh::decay_t<T>>; > > > /* Base Storage for Non-Trivally Destructible T's */ > template <typename T, bool = is_trivially_destructible<T>::value> > class optional_storage_base > { > public: > constexpr optional_storage_base() noexcept : valueless{} {} > > template <typename... Args> > constexpr explicit optional_storage_base(in_place_t, Args&&... args) > : _value(std::forward<Args>(args)...), initialized(true) {} > > > ~optional_storage_base() { destroy(); } > > inline void destroy() > { > if (initialized) { > _value.~T(); > initialized = false; > } > } > > union { > T _value; > char valueless; > }; > bool initialized = false; > }; > > /* template specialization for Trivally destructible T */ > template <typename T> > class optional_storage_base<T, true> > { > public: > constexpr optional_storage_base() noexcept : valueless{} {} > > template <typename... Args> > constexpr explicit optional_storage_base(in_place_t, Args&&... args) > : _value(std::forward<Args>(args)...), initialized(true) {} > > inline void destroy() { initialized = false; } > > union { > T _value; > char valueless; > }; > bool initialized = false; > }; > > /* Base Class that adds helpers for late initialization, assignment, and a few getters */ > template <class T> > class optional_storage_impl : public optional_storage_base<T> > { > using super = optional_storage_base<T>; > public: > using super::super; > > template <typename... Args> > void construct(Args&&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value) > { > ::new(std::addressof(this->_value)) T(std::forward<Args>(args)...); > this->initialized = true; > } > > /* Perfect Forwarding Assignment Helper(covers copy and move) bc less code less problems */ > template <class Optional> > void assign(Optional&& rhs) > { > if (this->initialized) > { > if (rhs.initialized) { > this->_value = std::forward<Optional>(rhs).get(); > } > else { > this->destroy(); > } > } > else if (rhs.initialized) > { > construct(std::forward<Optional>(rhs).get()); > } > //else both are empty do nothing > } > > T &get() & noexcept { return this->_value; } > constexpr const T& get() const & noexcept { return this->_value; } > T &&get() && noexcept { return hsh::move(this->_value); } > constexpr const T &&get() const && noexcept { return hsh::move(this->_value); } > }; > > /* Class that handles copy construction for T, specialized for Trival,Copyable, and Non Copyable types */ > template <class T, operation_support_trait = copy_constructor_traits<T>::traits> > class optional_storage_copy_constructor : public optional_storage_impl<T> > { > using super = optional_storage_impl<T>; > public: > using super::super; > }; > > template <class T> > class optional_storage_copy_constructor<T, operation_support_trait::available> : public optional_storage_impl<T> > { > using super = optional_storage_impl<T>; > public: > using super::super; > optional_storage_copy_constructor() = default; > optional_storage_copy_constructor(const optional_storage_copy_constructor& other) noexcept(std::is_nothrow_copy_constructible<T>::value) > { > if (other.initialized) { > this->construct(other.get()); > } > } > optional_storage_copy_constructor(optional_storage_copy_constructor&&) = default; > optional_storage_copy_constructor &operator=(const optional_storage_copy_constructor&) = default; > optional_storage_copy_constructor &operator=(optional_storage_copy_constructor&&) = default; > }; > > template <class T> > class optional_storage_copy_constructor<T, operation_support_trait::unavailable> : public optional_storage_impl<T> > { > using super = optional_storage_impl<T>; > public: > using super::super; > optional_storage_copy_constructor() = default; > optional_storage_copy_constructor(const optional_storage_copy_constructor&) = delete; > optional_storage_copy_constructor(optional_storage_copy_constructor&&) = default; > optional_storage_copy_constructor &operator=(const optional_storage_copy_constructor&) = default; > optional_storage_copy_constructor &operator=(optional_storage_copy_constructor&&) = default; > }; > > /* Class that handles Move construction for T, specialized for Trival, Movable, and Non Movable types */ > template <class T, operation_support_trait = move_constructor_traits<T>::traits> > class optional_storage_move_constructor : public optional_storage_copy_constructor<T> > { > using super = optional_storage_copy_constructor<T>; > public: > using super::super; > }; > > template <class T> > class optional_storage_move_constructor<T, operation_support_trait::available> : public optional_storage_copy_constructor<T> > { > using super = optional_storage_copy_constructor<T>; > public: > using super::super; > optional_storage_move_constructor() = default; > optional_storage_move_constructor(const optional_storage_move_constructor&) = default; > optional_storage_move_constructor(optional_storage_move_constructor&& other) noexcept(std::is_nothrow_move_constructible<T>::value) > { > if (other.initialized) { > this->construct(std::move(other).get()); > } > } > optional_storage_move_constructor &operator=(const optional_storage_move_constructor&) = default; > optional_storage_move_constructor &operator=(optional_storage_move_constructor&&) = default; > }; > > template <class T> > class optional_storage_move_constructor<T, operation_support_trait::unavailable> : public optional_storage_copy_constructor<T> > { > using super = optional_storage_copy_constructor<T>; > public: > using super::super; > optional_storage_move_constructor() = default; > optional_storage_move_constructor(const optional_storage_move_constructor&) = default; > optional_storage_move_constructor(optional_storage_move_constructor&&) = delete; > optional_storage_move_constructor &operator=(const optional_storage_move_constructor&) = default; > optional_storage_move_constructor &operator=(optional_storage_move_constructor&&) = default; > }; > > /* Class that handles Copy Assignment for T, specialized for Trival, Copyable, and Non Copyable types */ > template <class T, operation_support_trait = copy_assignment_traits<T>::traits> > class optional_storage_copy_assignment : public optional_storage_move_constructor<T> > { > using super = optional_storage_move_constructor<T>; > public: > using super::super; > }; > > template <class T> > class optional_storage_copy_assignment<T, operation_support_trait::available> : public optional_storage_move_constructor<T> > { > using super = optional_storage_move_constructor<T>; > public: > using super::super; > optional_storage_copy_assignment() = default; > optional_storage_copy_assignment(const optional_storage_copy_assignment&) = default; > optional_storage_copy_assignment(optional_storage_copy_assignment&&) = default; > optional_storage_copy_assignment &operator=(const optional_storage_copy_assignment& other) > noexcept(conjunction<std::is_nothrow_copy_assignable<T>, std::is_nothrow_copy_constructible<T>>::value) > { > this->assign(other); > return *this; > } > optional_storage_copy_assignment &operator=(optional_storage_copy_assignment&&) = default; > }; > > template <class T> > class optional_storage_copy_assignment<T, operation_support_trait::unavailable> : public optional_storage_move_constructor<T> > { > using super = optional_storage_move_constructor<T>; > public: > using super::super; > optional_storage_copy_assignment() = default; > optional_storage_copy_assignment(const optional_storage_copy_assignment&) = default; > optional_storage_copy_assignment(optional_storage_copy_assignment&&) = default; > optional_storage_copy_assignment &operator=(const optional_storage_copy_assignment&) = delete;; > optional_storage_copy_assignment &operator=(optional_storage_copy_assignment&&) = default; > }; > > /* Class that handles Move Assignment for T, specialized for Trival, Movable, and Non Movable types */ > template <class T, operation_support_trait = move_assignment_traits<T>::traits> > class optional_storage_move_assignment : public optional_storage_copy_assignment<T> > { > using super = optional_storage_copy_assignment<T>; > public: > using super::super; > }; > > template <class T> > class optional_storage_move_assignment<T, operation_support_trait::available> : public optional_storage_copy_assignment<T> > { > using super = optional_storage_copy_assignment<T>; > public: > using super::super; > optional_storage_move_assignment() = default; > optional_storage_move_assignment(const optional_storage_move_assignment&) = default; > optional_storage_move_assignment(optional_storage_move_assignment&&) = default; > optional_storage_move_assignment &operator=(const optional_storage_move_assignment&) = default; > optional_storage_move_assignment &operator=(optional_storage_move_assignment&& other) > noexcept(conjunction<std::is_nothrow_move_assignable<T>, std::is_nothrow_move_constructible<T>>::value) > { > this->assign(std::move(other)); > return *this; > } > }; > > template <class T> > class optional_storage_move_assignment<T, operation_support_trait::unavailable> : public optional_storage_copy_assignment<T> > { > using super = optional_storage_copy_assignment<T>; > public: > using super::super; > optional_storage_move_assignment() = default; > optional_storage_move_assignment(const optional_storage_move_assignment&) = default; > optional_storage_move_assignment(optional_storage_move_assignment&&) = default; > optional_storage_move_assignment &operator=(const optional_storage_move_assignment&) = default; > optional_storage_move_assignment &operator=(optional_storage_move_assignment&&) = delete; > }; > > template<typename Opt, > typename F, > typename R = hsh::invoke_result_t<F, decltype(*std::declval<Opt>())>, > enable_if_t<!std::is_same<R, void>::value>* = nullptr> > constexpr auto map_impl(Opt&& opt, F&& f) -> optional<R> > { > return opt ? hsh::invoke(hsh::forward<F>(f), *hsh::forward<Opt>(opt)) : optional<R>{}; > } > > template<typename Opt, > typename F, > typename R = hsh::invoke_result_t<F, decltype(*std::declval<Opt>())>, > enable_if_t<std::is_same<R, void>::value>* = nullptr, > typename Ret = hsh::monostate> > auto map_impl(Opt&& opt, F&& f) -> optional<Ret> > { > if (opt) > { > hsh::invoke(hsh::forward<F>(f), *hsh::forward<Opt>(opt)); > return hsh::monostate{}; > } > return hsh::nullopt; > } > } > > > class bad_optional_access : public std::logic_error > { > public: > bad_optional_access() : std::logic_error("hsh::bad_optional_access exception: optional has no value") {} > }; > > template <typename T, typename U> > struct is_constructible_convertible_from_optional > : disjunction<std::is_constructible<T, optional<U>&>, > std::is_constructible<T, optional<U>&&>, > std::is_constructible<T, const optional<U>&>, > std::is_constructible<T, const optional<U>&&>, > std::is_convertible<optional<U>&, T>, > std::is_convertible<optional<U>&&, T>, > std::is_convertible<const optional<U>&, T>, > std::is_convertible<const optional<U>&&, T>> {}; > > // Whether T is constructible or convertible or assignable from optional<U>. > template <typename T, typename U> > struct is_constructible_convertible_assignable_from_optional > : disjunction<is_constructible_convertible_from_optional<T, U>, > std::is_assignable<T&, optional<U>&>, > std::is_assignable<T&, optional<U>&&>, > std::is_assignable<T&, const optional<U>&>, > std::is_assignable<T&, const optional<U>&&>> {}; > > template <class T> > class optional : private optional_details::optional_storage_move_assignment<T> > { > using base = optional_details::optional_storage_move_assignment<T>; > public: > /* Sanity Check T */ > static_assert(!std::is_reference<T>::value, "hsh::optional of a reference type is ill-formed, use std::reference_wrapper if you need a ref"); > static_assert(!std::is_same<T, in_place_t>::value, "hsh::optional of a in_place_t type is ill-formed"); > static_assert(!std::is_same<T, nullopt_t>::value, "hsh::optional of a nullopt_t type is ill-formed"); > > /* --- Constructors --- */ > constexpr optional() noexcept = default; > constexpr optional(nullopt_t) noexcept {} > optional(const optional &rhs) = default; > optional(optional &&rhs) = default; > > template < > typename U, > enable_if_t<conjunction<negation<std::is_same<T, U> >, > std::is_constructible<T, const U&>, > negation<is_constructible_convertible_from_optional<T, U> >, > std::is_convertible<const U&, T> >::value, bool> = false> > optional(const optional<U>& rhs) > { > if (rhs) { > this->construct(*rhs); > } > } > > template < > typename U, > hsh::enable_if_t< > hsh::conjunction< > hsh::negation<std::is_same<T, U>>, > std::is_constructible<T, const U&>, > hsh::negation< > hsh::is_constructible_convertible_from_optional<T, U>>, > hsh::negation<std::is_convertible<const U&, T>>>::value, bool> = false> > explicit optional(const optional<U>& rhs) > { > if (rhs) { > this->construct(*rhs); > } > } > > template < > typename U, > hsh::enable_if_t< > hsh::conjunction< > hsh::negation<std::is_same<T, U> >, > std::is_constructible<T, U&&>, > hsh::negation< > hsh::is_constructible_convertible_from_optional<T, U> >, > std::is_convertible<U&&, T> >::value, bool> = false> > optional(optional<U>&& rhs) > { > if (rhs) { > this->construct(std::move(*rhs)); > } > } > > template < > typename U, > enable_if_t< > conjunction< > negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, > negation<is_constructible_convertible_from_optional<T, U>>, > negation<std::is_convertible<U&&, T>>>::value,bool> = false> > explicit optional(optional<U>&& rhs) > { > if (rhs) { > this->construct(std::move(*rhs)); > } > } > > template < > typename U = T, > enable_if_t< > conjunction<negation<std::is_same< > in_place_t, typename std::decay<U>::type> >, > negation<std::is_same< > optional<T>, typename std::decay<U>::type> >, > std::is_convertible<U&&, T>, > std::is_constructible<T, U&&> >::value,bool> = false> > constexpr optional(U&& other) : base(in_place, std::forward<U>(other)) {} > > template < > typename U = T, > enable_if_t< > conjunction<negation<std::is_same< > in_place_t, typename std::decay<U>::type>>, > negation<std::is_same< > optional<T>, typename std::decay<U>::type>>, > negation<std::is_convertible<U&&, T>>, > std::is_constructible<T, U&&>>::value, bool> = false> > constexpr explicit optional(U&& other) > : base(in_place, std::forward<U>(other)) {} > > template <typename InPlaceT, typename... Args, enable_if_t<conjunction<std::is_same<InPlaceT, in_place_t>, std::is_constructible<T, Args&&...> >::value>* = nullptr> > constexpr explicit optional(InPlaceT, Args&&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value) > : base(in_place, std::forward<Args>(args)...) {} > > template <typename U, typename... Args, typename = enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value>> > constexpr explicit optional(in_place_t, std::initializer_list<U> ilist, Args&&... args) > noexcept(std::is_nothrow_constructible<T, std::initializer_list<U>&, Args...>::value) > : base(in_place, ilist, std::forward<Args>(args)...) {} > > /* --- Destructor --- */ > ~optional() = default; > > /* --- Assignment Operators --- */ > optional& operator=(nullopt_t) noexcept { this->destroy(); return *this; } > optional& operator=(const optional& other) = default; > optional& operator=(optional&& other) = default; > > template < > typename U = T, > typename = hsh::enable_if_t<hsh::conjunction< > hsh::negation< > std::is_same<optional<T>, typename std::decay<U>::type>>, > hsh::negation< > hsh::conjunction<std::is_scalar<T>, > std::is_same<T, typename std::decay<U>::type>>>, > std::is_constructible<T, U>, std::is_assignable<T&, U>>::value>> > optional & operator=(U&& value) > { > if ( has_value() ) { > this->_value = std::forward<U>(value); > } else { > this->construct(std::forward<U>(value)); > } > return *this; > } > > template < > typename U, > typename = hsh::enable_if_t<hsh::conjunction< > hsh::negation<std::is_same<T, U>>, > std::is_constructible<T, const U&>, std::is_assignable<T&, const U&>, > hsh::negation<hsh::is_constructible_convertible_assignable_from_optional<T, U>>>::value>> > optional & operator=(const optional<U>& other) > { > if (has_value()) > { > if (other){ > this->_value = *other; > } else { > this->destroy(); > } > } > else if(other) > { > this->construct(*other); > } > return *this; > } > > template < > typename U, > typename = hsh::enable_if_t<hsh::conjunction< > hsh::negation<std::is_same<T, U>>, std::is_constructible<T, U>, > std::is_assignable<T&, U>, > hsh::negation< > hsh::is_constructible_convertible_assignable_from_optional<T, U>>>::value>> > optional& operator=(optional<U>&& other) > { > if (has_value()) > { > if (other) { > this->_value = std::move(*other); > } > else { > this->destroy(); > } > } > else if (other) > { > this->construct(std::move(*other)); > } > return *this; > } > > void swap(optional& rhs) noexcept(hsh::conjunction<std::is_nothrow_move_constructible<T>, hsh::is_nothrow_swappable<T>>::value) > { > using std::swap; > if (has_value()) > { > if (rhs.has_value()) > { > swap(**this, *rhs); > } > else > { > rhs.construct(std::move(this->_value)); > this->destroy(); > } > } > else if (rhs.has_value()) > { > this->construct(std::move(rhs._value)); > rhs.destroy(); > } > } > > explicit operator bool() const noexcept { return this->initialized; } > bool has_value() const noexcept { return this->initialized; } > > void reset() noexcept { this->destroy(); } > > constexpr const T* operator->() const noexcept { return std::addressof(this->_value); } > T* operator->() noexcept { return std::addressof(this->_value); } > > constexpr const T& operator*() const& noexcept { return this->_value; } > T& operator*() & noexcept { return this->_value; } > constexpr const T&& operator*() const && noexcept { return hsh::move(this->_value); } > T&& operator*() && noexcept { return hsh::move(this->_value); } > > const T& value() const & > { > if (!has_value()) { > throw bad_optional_access(); > } > return this->_value; > } > > T& value() & > { > if (!has_value()) { > throw bad_optional_access(); > } > return this->_value; > } > > T&& value() && > { > if (!has_value()) { > throw bad_optional_access(); > } > return std::move(this->_value); > } > > const T&& value() const && > { > if (!has_value()) { > throw bad_optional_access(); > } > return std::move(this->_value); > } > > template <typename U> > T value_or(U&& v) const& noexcept { return has_value() ? **this : static_cast<T>(std::forward<U>(v)); } > > template <typename U> > T value_or(U&& v) && noexcept { return has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(v)); } > > template <class... Args, typename = enable_if_t<std::is_constructible<T,Args&&...>::value>> > T& emplace(Args&&... args) > { > this->destroy(); > this->construct(std::forward<Args>(args)...); > return this->_value; > } > > template< class U, class... Args, typename = enable_if_t<std::is_constructible<T,std::initializer_list<U>&, Args&&...>::value>> > T& emplace(std::initializer_list<U> il, Args&&... args) > { > this->destroy(); > this->construct(il, std::forward<Args>(args)...); > return this->_value; > } > > /* Note: following monadic operators based on: http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0798r0.html */ > /* Map */ > template<typename F> > auto map(F&& f) & -> decltype(optional_details::map_impl(*this, hsh::forward<F>(f))) > { > return optional_details::map_impl(*this, hsh::forward<F>(f)); > } > > template<typename F> > constexpr auto map(F&& f) const& -> decltype(optional_details::map_impl(*this, hsh::forward<F>(f))) > { > return optional_details::map_impl(*this, hsh::forward<F>(f)); > } > > template<typename F> > auto map(F&& f) && -> decltype(optional_details::map_impl(hsh::move(*this), hsh::forward<F>(f))) > { > return optional_details::map_impl(hsh::move(*this), hsh::forward<F>(f)); > } > > template<typename F> > constexpr auto map(F&& f) const&& -> decltype(optional_details::map_impl(hsh::move(*this), hsh::forward<F>(f))) > { > return optional_details::map_impl(hsh::move(*this), hsh::forward<F>(f)); > } > > /* and_then */ > template<typename F> > auto and_then(F&& f) & -> hsh::invoke_result_t<F, T&> > { > using result = hsh::invoke_result_t<F, T&>; > static_assert(optional_details::is_optional<result>::value, "The functor passed to and_then must return an optional"); > return has_value() ? hsh::invoke(hsh::forward<F>(f), **this) : result{}; > } > > template<typename F> > constexpr auto and_then(F&& f) const & -> hsh::invoke_result_t<F, const T&> > { > using result = hsh::invoke_result_t<F, const T&>; > static_assert(optional_details::is_optional<result>::value, "The functor passed to and_then must return an optional"); > return has_value() ? hsh::invoke(hsh::forward<F>(f), **this) : result{}; > } > > template<typename F> > auto and_then(F&& f) && -> hsh::invoke_result_t<F, T&&> > { > using result = hsh::invoke_result_t<F, T&&>; > static_assert(optional_details::is_optional<result>::value, "The functor passed to and_then must return an optional"); > return has_value() ? hsh::invoke(hsh::forward<F>(f), hsh::move(**this)) : result{}; > } > > template<typename F> > constexpr auto and_then(F&& f) const && -> hsh::invoke_result_t<F, const T&&> > { > using result = hsh::invoke_result_t<F, const T&&>; > static_assert(optional_details::is_optional<result>::value, "The functor passed to and_then must return an optional"); > return has_value() ? hsh::invoke(hsh::forward<F>(f), hsh::move(**this)) : result{}; > } > > /* or_else */ > template<typename F, enable_if_t<std::is_same<void, hsh::invoke_result_t<F>>::value>* = nullptr> > optional<T> or_else(F&& f) & > { > if (has_value()) { > return *this; > } > hsh::invoke(hsh::forward<F>(f)); > return hsh::nullopt; > } > > template<typename F, enable_if_t<!std::is_same<void, hsh::invoke_result_t<F>>::value>* = nullptr> > optional<T> or_else(F&& f) & > { > return has_value() ? *this : hsh::forward<F>(f)(); > } > > template<typename F, enable_if_t<std::is_same<void, hsh::invoke_result_t<F>>::value>* = nullptr> > optional<T> or_else(F&& f) const & > { > if (has_value()) { > return *this; > } > hsh::invoke(hsh::forward<F>(f)); > return hsh::nullopt; > } > > template<typename F, enable_if_t<!std::is_same<void, hsh::invoke_result_t<F>>::value>* = nullptr> > optional<T> or_else(F&& f) const & > { > return has_value() ? *this : hsh::forward<F>(f)(); > } > > template<typename F, enable_if_t<std::is_same<void, hsh::invoke_result_t<F>>::value>* = nullptr> > optional<T> or_else(F&& f) && > { > if (has_value()) { > return hsh::move(*this); > } > hsh::invoke(hsh::forward<F>(f)); > return hsh::nullopt; > } > > template<typename F, enable_if_t<!std::is_same<void, hsh::invoke_result_t<F>>::value>* = nullptr> > optional<T> or_else(F&& f) && > { > return has_value() ? hsh::move(*this) : hsh::forward<F>(f)(); > } > > template<typename F, enable_if_t<std::is_same<void, hsh::invoke_result_t<F>>::value>* = nullptr> > optional<T> or_else(F&& f) const && > { > if (has_value()) { > return hsh::move(*this); > } > hsh::invoke(hsh::forward<F>(f)); > return hsh::nullopt; > } > > template<typename F, enable_if_t<!std::is_same<void, hsh::invoke_result_t<F>>::value>* = nullptr> > optional<T> or_else(F&& f) const && > { > return has_value() ? hsh::move(*this) : hsh::forward<F>(f)(); > } > }; > > template<class T> > void swap(optional<T>& lhs, optional<T>& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } > > /* --- Equality Operators ------------------------------------------------------------------ */ > template<class T1, class T2> > constexpr bool operator==(const optional<T1>& x, const optional<T2>& y) noexcept(noexcept(*x == *y)) { > return x ? (y && (*x == *y)) : !y; > } > > template<class T1, class T2> > constexpr bool operator!=(const optional<T1>& x, const optional<T2>& y) noexcept(noexcept(x == y)) { return !(x == y); } > > template<class T1, class T2> > constexpr bool operator<(const optional<T1>& x, const optional<T2>& y) noexcept(noexcept(*x < *y)) { > return y ? !x || (*x < *y) : false; > } > > template<class T1, class T2> > constexpr bool operator<=(const optional<T1>& x, const optional<T2>& y) noexcept(noexcept(y < x)) { return !(y < x); } > > template<class T1, class T2> > constexpr bool operator>(const optional<T1>& x, const optional<T2>& y) noexcept(noexcept(y < x)) { return (y < x); } > > template<class T1, class T2> > constexpr bool operator>=(const optional<T1>& x, const optional<T2>& y) noexcept(noexcept(x < y)) { return !(x < y); } > > template<class T> > constexpr bool operator==(const optional<T>& x, nullopt_t) noexcept { return !x; } > > template<class T> > constexpr bool operator==(nullopt_t, const optional<T>& x) noexcept { return !x; } > > template<class T> > constexpr bool operator!=(const optional<T>& x, nullopt_t) noexcept { return static_cast<bool>(x); } > > template<class T> > constexpr bool operator!=(nullopt_t, const optional<T>& x) noexcept { return static_cast<bool>(x); } > > template<class T> > constexpr bool operator<(const optional<T>& x, nullopt_t) noexcept { return false; } > > template<class T> > constexpr bool operator<(nullopt_t, const optional<T>& x) noexcept { return static_cast<bool>(x); } > > template<class T> > constexpr bool operator<=(const optional<T>& x, nullopt_t) noexcept { return !x; } > > template<class T> > constexpr bool operator<=(nullopt_t, const optional<T>& x) noexcept { return true; } > > template<class T> > constexpr bool operator>(const optional<T>& x, nullopt_t) noexcept { return static_cast<bool>(x); } > > template<class T> > constexpr bool operator>(nullopt_t, const optional<T>& x) noexcept { return false; } > > template<class T> > constexpr bool operator>=(const optional<T>& x, nullopt_t) noexcept { return true; } > > template<class T> > constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept { return !x; } > > /* --- Helpers for creating optional values ---------------------------------------------- */ > template <typename T> > optional<typename std::decay<T>::type> make_optional(T&& v) { > return optional<typename std::decay<T>::type>(std::forward<T>(v)); > } > > template <typename T, typename... Args> > optional<T> make_optional(Args&&... args) { > return optional<T>(in_place, std::forward<Args>(args)...); > } > > template <typename T, typename U, typename... Args> > optional<T> make_optional(std::initializer_list<U> il, Args&&... args) { > return optional<T>(in_place, il, std::forward<Args>(args)...); > } > > template<class T, class Mutex = std::mutex, class Container = std::deque<T>> > class blocking_queue > { > public: > using mutex_type = Mutex; > using container_type = Container; > using value_type = typename container_type::value_type; > using reference = typename container_type::reference; > using const_reference = typename container_type::const_reference; > using size_type = typename container_type::size_type; > > /* ---- Constructors ---------------------- */ > blocking_queue() = default; > > template<class Allocator> > explicit blocking_queue(const Allocator& allocator, enable_if_t<std::uses_allocator<container_type, Allocator>::value>* = nullptr) > : queue(allocator) {} > > explicit blocking_queue(const container_type& cont) : queue(cont) {} > > explicit blocking_queue(container_type&& cont) : queue(std::move(cont)) {} > > blocking_queue(std::initializer_list<value_type> il) : queue(il.begin(), il.end()) {} > > template<class InputIt> > blocking_queue(InputIt first, InputIt last) : queue(first, last) {} > > template<class Allocator> > blocking_queue(const container_type& cont, const Allocator& allocator, enable_if_t<std::uses_allocator<container_type, Allocator>::value>* = nullptr) > : queue(cont, allocator) {} > > template<class Allocator> > blocking_queue(container_type&& cont, const Allocator& allocator, enable_if_t<std::uses_allocator<container_type, Allocator>::value>* = nullptr) > : queue(std::move(cont), allocator) {} > > template<class Allocator> > blocking_queue(std::initializer_list<value_type> il, const Allocator& allocator, enable_if_t<std::uses_allocator<container_type, Allocator>::value>* = nullptr) > : queue(il.begin(), il.end(), allocator) {} > > template<class InputIt, class Allocator> > blocking_queue(InputIt first, InputIt last, const Allocator& allocator, enable_if_t<std::uses_allocator<container_type, Allocator>::value>* = nullptr) > : queue(first, last, allocator) {} > > ~blocking_queue() { > shutdown(); > clear(); > } > > inline void push(const value_type& value) > { > { > std::lock_guard<mutex_type> scoped_lock{ lock }; > queue.push_back(value); > } > not_empty_var.notify_one(); > } > > inline void push(value_type&& value) > { > { > std::lock_guard<mutex_type> scoped_lock{ lock }; > queue.push_back(std::move(value)); > } > not_empty_var.notify_one(); > } > > template<class... Args> > inline void emplace(Args&&... args) > { > { > std::lock_guard<mutex_type> scoped_lock{ lock }; > queue.emplace_back(std::forward<Args>(args)...); > } > not_empty_var.notify_one(); > } > > template<class InputIt> > inline void insert(InputIt first, InputIt last) > { > { > std::lock_guard<mutex_type> scoped_lock{ lock }; > queue.insert(queue.end(), first, last); > } > not_empty_var.notify_all(); > } > > inline void insert(std::initializer_list<value_type> il) { insert(il.begin(), il.end()); } > > > inline hsh::optional<value_type> try_pop() > { > std::unique_lock<mutex_type> ulock{ lock }; > return (destroy_queue || queue.empty()) ? nullopt : pop_internal(ulock); > } > > inline bool try_pop(value_type& out) > { > std::unique_lock<mutex_type> ulock{ lock }; > return (destroy_queue || queue.empty()) ? false : pop_internal(ulock, out); > } > > template <class Rep, class Period> > inline hsh::optional<value_type> try_pop_for(const std::chrono::duration<Rep, Period>& timeout) > { > return try_pop_until( std::chrono::steady_clock::now() + timeout ); > } > > template <class Rep, class Period> > inline bool try_pop_for(value_type& out, const std::chrono::duration<Rep, Period>& timeout) > { > return try_pop_until( out, std::chrono::steady_clock::now() + timeout ); > } > > template <class Clock, class Duration> > inline hsh::optional<value_type> try_pop_until(const std::chrono::time_point<Clock, Duration>& timeout_time) > { > std::unique_lock<mutex_type> ulock{ lock }; > if( !not_empty_var.wait_until(ulock, timeout_time, [this](){ return !queue.empty() || destroy_queue;}) || destroy_queue ){ > return {}; > } > return pop_internal(ulock); > } > > template <class Clock, class Duration> > inline bool try_pop_until(value_type& out, const std::chrono::time_point<Clock, Duration>& timeout_time) > { > std::unique_lock<mutex_type> ulock{ lock }; > if( !not_empty_var.wait_until(ulock, timeout_time, [this](){ return !queue.empty() || destroy_queue;}) || destroy_queue ){ > return false; > } > return pop_internal(ulock, out); > } > > inline hsh::optional<value_type> wait_pop() > { > std::unique_lock<mutex_type> ulock{ lock }; > not_empty_var.wait(ulock, [this]() { return !queue.empty() || destroy_queue; }); > return destroy_queue ? nullopt : pop_internal(ulock); > } > > inline bool wait_pop(value_type& out) > { > std::unique_lock<mutex_type> ulock{ lock }; > not_empty_var.wait(ulock, [this]() { return !queue.empty() || destroy_queue; }); > return destroy_queue ? false : pop_internal(ulock, out); > } > > template<class OutputIt> > void consume_all(OutputIt out) > { > { > std::lock_guard<mutex_type> ulock{ lock }; > std::copy(std::make_move_iterator(queue.begin()), std::make_move_iterator(queue.end()), out); > queue.clear(); > } > empty_var.notify_all(); > } > > void consume_all(container_type& out) > { > { > std::lock_guard<mutex_type> ulock{ lock }; > out = std::move(queue); > queue.clear(); > } > empty_var.notify_all(); > } > > /* > Note: If there are multiple Producers the try/wait_empty methods behavior won't be consistent due to the asynchronus nature of the queue. > */ > > template <class Rep, class Period> > inline bool try_empty_for(const std::chrono::duration<Rep, Period>& timeout) > { > return try_empty_until(std::chrono::steady_clock::now() + timeout); > } > > template <class Clock, class Duration> > inline bool try_empty_until(const std::chrono::time_point<Clock, Duration>& timeout_time) > { > std::unique_lock<mutex_type> ulock{ lock }; > return empty_var.wait_until(ulock, timeout_time, [this](){ return queue.empty() || destroy_queue; } ); > } > > inline void wait_empty() > { > std::unique_lock<mutex_type> ulock{ lock }; > empty_var.wait(ulock, [this] { return queue.empty() || destroy_queue; }); > } > > inline void clear() > { > { > std::lock_guard<mutex_type> scoped_lock{ lock }; > queue.clear(); > } > empty_var.notify_all(); > } > > inline void shutdown() > { > { > std::lock_guard<mutex_type> scoped_lock{ lock }; > destroy_queue = true; > } > not_empty_var.notify_all(); > empty_var.notify_all(); > } > > inline bool is_shutdown() const > { > std::lock_guard<mutex_type> scoped_lock{ lock }; > return destroy_queue; > } > > inline bool empty() const > { > std::lock_guard<mutex_type> scoped_lock{ lock }; > return queue.empty(); > } > > inline size_type size() const > { > std::lock_guard<mutex_type> scoped_lock{ lock }; > return queue.size(); > } > > private: > /* Note: pop_internal has the following prerequistes that must be true or undefined behavior happens: > - ulock must be held before calling them > - queue must not be empty */ > inline hsh::optional<value_type> pop_internal(std::unique_lock<mutex_type>& ulock) > { > hsh::optional<value_type> ret_val{ std::move(queue.front()) }; > queue.pop_front(); > bool is_empty = queue.empty(); > ulock.unlock(); > if( is_empty ){ empty_var.notify_all(); } > return ret_val; > } > > inline bool pop_internal(std::unique_lock<mutex_type>& ulock, value_type& out) > { > out = std::move(queue.front()); > queue.pop_front(); > bool is_empty = queue.empty(); > ulock.unlock(); > if( is_empty ){ empty_var.notify_all(); } > return true; > } > > std::condition_variable_any not_empty_var; /* Note: This conditional variable is for waiting on the queue to not be empty/being destroyed */ > std::condition_variable_any empty_var; /* Note: This conditional variable is for waiting for the queue to be empty */ > mutable mutex_type lock; > container_type queue; > bool destroy_queue = false; > }; > >template <typename> > class unique_function; > > template <typename R, typename ... Args> > class unique_function<R(Args...)> > { > private: > //Note: for std::function: LLVM uses 3, gcc, and msvc 2 maybe this should be tunable..? > static constexpr size_t inline_storage_size = 6u * sizeof(void*); > > /* Type Trait for determining which types T can be constructed internally */ > template <typename F> > using is_inline_constructible = conjunction<bool_constant<(sizeof(F) <= inline_storage_size)>, std::is_nothrow_move_constructible<F>>; > > public: > unique_function() = default; > unique_function(std::nullptr_t) noexcept {} > > /* constructor overload for functions that will be stored internally (storage.internal) */ > template < > typename F, > typename F_TYPE = decay_t<F>, > enable_if_t< > conjunction<negation<std::is_same<F_TYPE, unique_function>>, > is_invocable_r<R, F&&, Args...>, > is_inline_constructible<F_TYPE>>::value>* = nullptr > > unique_function(F&& f) noexcept(noexcept(F_TYPE(std::forward<F>(f)))) : handler(internal_handler<F_TYPE>::get_handler()) > { > ::new (static_cast<void*>(&storage.internal)) F_TYPE(std::forward<F>(f)); > } > > /* constructor overload for functions that will be stored on the heap(storage.dynamic) (can't be noexcept bc we're allocating on the heap) */ > template< > typename F, > typename F_TYPE = decay_t<F>, > enable_if_t< > conjunction<negation<std::is_same<F_TYPE, unique_function>>, > is_invocable_r<R, F&&, Args...>, > negation<is_inline_constructible<F_TYPE>>>::value>* = nullptr> > unique_function(F&& f) : handler(dynamic_handler<F_TYPE>::get_handler()) > { > storage.dynamic = new F_TYPE(std::forward<F>(f)); > } > > /* unique function is not copy constructible */ > unique_function(const unique_function&) = delete; > > unique_function(unique_function&& other) noexcept : handler(other.handler) > { > if (handler) > { > handler->move_func_ptr(storage, other.storage); > other.handler = nullptr; > } > } > > ~unique_function() { reset(); } > > /* unique function is not copy assignable*/ > unique_function& operator=(const unique_function&) = delete; > > unique_function& operator=(unique_function&& other) noexcept > { > reset(); > ::new (this) unique_function(std::move(other)); > return *this; > } > > unique_function& operator=(std::nullptr_t) noexcept > { > reset(); > return *this; > } > > template <typename F, > enable_if_t< > conjunction<negation<std::is_same<decay_t<F>, unique_function>>, > is_invocable_r<R, F&&, Args...>, > std::is_nothrow_constructible<unique_function, F&&>>::value>* = nullptr> > unique_function& operator=(F&& f) noexcept > { > reset(); > ::new (this) unique_function(std::forward<F>(f)); > return *this; > } > > template <typename F, > enable_if_t< > conjunction<negation<std::is_same<decay_t<F>, unique_function>>, > is_invocable_r<R, F&&, Args...>, > negation<std::is_nothrow_constructible<unique_function, F&&>>>::value>* = nullptr> > unique_function& operator=(F&& f) > { > unique_function(std::forward<F>(f)).swap(*this); > return *this; > } > > explicit operator bool() const noexcept { return handler; } > > R operator()(Args... args) const > { > if (!handler) { > throw std::bad_function_call(); > } > return handler->call_func_ptr(storage, std::forward<Args>(args)...); > } > > inline void reset() > { > /* Note: The only time destroy_func_ptr will be null is when the type-erased callable is trivially_destructible, this saves us a function_ptr call */ > if (handler && handler->destroy_func_ptr) > { > handler->destroy_func_ptr(storage); > } > handler = nullptr; > } > > void swap(unique_function& that) noexcept { std::swap(*this, that); } > > private: > union storage_union > { > using stack_storage_t = typename std::aligned_storage<inline_storage_size, std::alignment_of<void*>::value>::type; > > void* dynamic; > stack_storage_t internal; > }; > > using call_func_ptr_t = R(*)(const storage_union& storage, Args... args); > using move_func_ptr_t = void(*)(storage_union& lhs, storage_union& rhs); > using destroy_func_ptr_t = void(*)(storage_union& storage); > > struct storage_handler > { > call_func_ptr_t call_func_ptr; > move_func_ptr_t move_func_ptr; > destroy_func_ptr_t destroy_func_ptr; > }; > storage_union storage; > const storage_handler* handler = nullptr; > > /* Template for functions to use when the function fits inside our internal small buffer */ > template<typename F> > struct internal_handler > { > /* Cast Helpers since casting from the internal storage to the correct type is such a pain in the a$$ */ > static inline F* internal_cast(storage_union& storage) noexcept { return reinterpret_cast<F*>(static_cast<void*>(&storage.internal)); } > static inline const F* internal_cast(const storage_union& storage) noexcept { return reinterpret_cast<const F*>(static_cast<const void*>(&storage.internal)); } > > /* Call Operator for Functions that don't return void */ > template <bool IsVoid = std::is_same<R, void>::value> > static enable_if_t<!IsVoid, R> call_impl(const storage_union& storage, Args... args) > { > return hsh::invoke(*const_cast<F*>(internal_cast(storage)), std::forward<Args>(args)...); > } > > /* Call Operator for Functions that return void */ > template <bool IsVoid = std::is_same<R, void>::value> > static enable_if_t<IsVoid> call_impl(const storage_union& storage, Args... args) > { > hsh::invoke(*const_cast<F*>(internal_cast(storage)), std::forward<Args>(args)...); > } > > static void move_impl(storage_union& lhs, storage_union& rhs) > { > ::new (static_cast<void*>(&lhs.internal)) F(std::move(*internal_cast(rhs))); > } > > static void destroy_impl(storage_union& storage) > { > internal_cast(storage)->~F(); > } > > inline static const storage_handler* get_handler() noexcept > { > static constexpr storage_handler handler{ &call_impl, &move_impl, hsh::is_trivially_destructible<F>::value ? nullptr : &destroy_impl }; > return &handler; > } > }; > > /* Template for functions to use when the function has to be allocated externally on the heap */ > template<typename F> > struct dynamic_handler > { > /* Call Operator for Functions that don't return void */ > template <bool IsVoid = std::is_same<R, void>::value> > static enable_if_t<!IsVoid, R> call_impl(const storage_union& storage, Args... args) > { > return hsh::invoke(*reinterpret_cast<F*>(const_cast<void*>(storage.dynamic)), std::forward<Args>(args)...); > } > > /* Call Operator for Functions that return void */ > template <bool IsVoid = std::is_same<R, void>::value> > static enable_if_t<IsVoid> call_impl(const storage_union& storage, Args... args) > { > hsh::invoke(*reinterpret_cast<F*>(const_cast<void*>(storage.dynamic)), std::forward<Args>(args)...); > } > > static void move_impl(storage_union& lhs, storage_union& rhs) > { > lhs.dynamic = hsh::exchange(rhs.dynamic, nullptr); > } > > static void destroy_impl(storage_union& storage) > { > delete static_cast<F*>(storage.dynamic); > } > > inline static const storage_handler* get_handler() noexcept > { > static constexpr storage_handler handler{ &call_impl, &move_impl, &destroy_impl }; > return &handler; > } > }; > }; > > template <typename T> > void swap(unique_function<T> &lhs, unique_function<T> &rhs) noexcept { lhs.swap(rhs); } > > template <typename T> > bool operator==(const unique_function<T>& f, nullptr_t) { return !f; } > > template <typename T> > bool operator==( nullptr_t, const unique_function<T>& f) { return !f; } > > template <typename T> > bool operator!=(const unique_function<T>& f, nullptr_t) { return !(f == nullptr); } > > template <typename T> > bool operator!=( nullptr_t, const unique_function<T>& f) { return !(nullptr == f); } > >/* Executor Concept: Allocator -> allocation and Executor -> execution > > A class is an executor if: > - it has a member function execute(F, Args) where hsh::invoke(F,Args) is a valid expression and F and Args are movable. > - it is cheap to copy and nothrow copyable (ideally trivially copyable) > - is is equality comparable (i.e. == and != ) > > The exeuctor should be lightweight, ideally just contain a handle to a execution_context that will actually be the thing that runs the task. > see inline_executor, or detached_thread_executor for a simple example or see basic_thread_pool(post_executor, dispatch_executor) for an example of wrapping a threadpool. > */ > > /* polymorphic interface for passing through a virtual interface: Executors should be used as templates when ever possible */ > class executor_interface > { > public: > virtual ~executor_interface() = default; > virtual void execute(hsh::unique_function<void()>&& f) = 0; > }; > > /* template wrapper: converts any executor to a executor interface*/ > template<typename Ex> > class generic_executor_wrapper : public executor_interface > { > public: > explicit generic_executor_wrapper(Ex ex) noexcept : ex(ex) {} > > virtual void execute(hsh::unique_function<void()>&& f) override { ex.execute( hsh::move( f ) ); } > > private: > Ex ex; > }; > > /* inline executor: always runs the callable on the current thread and blocks until the call is complete, usefull for callbacks and testing */ > class inline_executor > { > public: > template <typename F> > void execute(F&& f) noexcept(hsh::is_nothrow_invocable<F&&>::value) { hsh::invoke( std::forward<F>(f) ); } > > friend bool operator==(const inline_executor& lhs, const inline_executor& rhs) { return true; } > friend bool operator!=(const inline_executor& lhs, const inline_executor& rhs) { return false; } > }; > > /* detached thread executor: launches a new thread, and detaches it. So it's always non-blocking. Can be dangerous if the task isn't complete before exiting a program. See std::thread::detach for why it's risky */ > class detached_thread_executor > { > public: > template <typename F> > void execute(F&& f) > { > std::thread new_thread( std::forward<F>(f) ); > new_thread.detach(); > } > > friend bool operator==(const detached_thread_executor& lhs, const detached_thread_executor& rhs) { return true; } > friend bool operator!=(const detached_thread_executor& lhs, const detached_thread_executor& rhs) { return false; } > }; > > /* basic_thread_pool: has 2 executors: dispatch_executor and post_executor. > dispatch_executor - if the current thread calling execute is a thread in the pool, it will run the task inplace otherwise it will push it to the queue of pending tasks. > this can be an optimization for callbacks so 'ready' work can be completed right away without clobbering the stack or context switching. However it might block the > current thread in some cases, so it can be confusing to use. > post_executor - always queues up the work into the pending work queue. Simply but for tasks that spawn other tasks this can result in a lot of context switching. And work being done by 'cold' threads. > */ > class basic_thread_pool > { > using task_t = hsh::unique_function<void()>; > public: > class dispatch_executor > { > public: > template<typename F> > void execute(F&& f) { pool->dispatch( hsh::forward<F>(f) ); } > > basic_thread_pool& context() noexcept { return *pool; } > > private: > friend class basic_thread_pool; > friend bool operator==(const dispatch_executor&,const dispatch_executor&) noexcept; > explicit dispatch_executor(basic_thread_pool *pool) noexcept : pool(pool) {} > basic_thread_pool *pool; > }; > > class post_executor > { > public: > > template<typename F> > void execute(F&& f) { pool->post( hsh::forward<F>(f) ); } > > basic_thread_pool& context() noexcept { return *pool; } > > private: > friend class basic_thread_pool; > friend bool operator==(const post_executor&, const post_executor&) noexcept; > explicit post_executor(basic_thread_pool *pool) noexcept : pool(pool) {} > basic_thread_pool *pool; > }; > > basic_thread_pool() : basic_thread_pool( std::thread::hardware_concurrency() ) {} > > explicit basic_thread_pool(unsigned int num_threads) > { > auto num_threads_to_create = std::max(1u, num_threads); > threads.reserve(num_threads_to_create); > for(unsigned int thread_index = 0; thread_index < num_threads_to_create; ++thread_index) > { > threads.emplace_back(&basic_thread_pool::worker_task, this); > } > } > > /* disallow copy and move construction */ > basic_thread_pool(const basic_thread_pool&) = delete; > basic_thread_pool(basic_thread_pool&&) = delete; > > ~basic_thread_pool() > { > tasks.shutdown(); > for(auto &t : threads) { > t.join(); > } > } > > /* disallow copy and move assignment */ > basic_thread_pool& operator=(const basic_thread_pool&) = delete; > basic_thread_pool& operator=(basic_thread_pool&&) = delete; > > /* get exeuctor from pool */ > post_executor get_post_executor() noexcept { return post_executor(this); } > dispatch_executor get_dispatch_executor() noexcept { return dispatch_executor(this); } > > size_t queue_depth() const noexcept { return tasks.size(); } > size_t thread_count() const noexcept { return threads.size(); } > > inline bool is_current_thread_in_pool() const noexcept { return thread_pool_id() == this; } > > /* Adds entry to the Threadpool's worker queue and returns, > this function never blocks, and should be the default for how to submit work to a threadpool */ > template <typename F> > void post(F&& f) { tasks.emplace( hsh::forward<F>(f) ); } > > /* if the current thread is in the pool this function will execute the function before returning, if not it adds the work to a queue and returns > This function can be usefull when a given callback might span other work that needs to be run. This function should only ever be called in callbacks > */ > template <typename F> > void dispatch(F&& f) > { > if( is_current_thread_in_pool() ) { > hsh::invoke( hsh::forward<F>(f) ); > } else { > tasks.emplace( hsh::forward<F>(f) ); > } > } > > private: > void worker_task() > { > /* setup: thread_local id, that will help determine if a given thread is in the pool */ > thread_pool_id() = this; > while( true ) > { > task_t task; > if(tasks.wait_pop(task)) { > task(); > } else { > return; //queue has been shutdown > } > } > } > > inline basic_thread_pool*& thread_pool_id() const > { > static thread_local basic_thread_pool* tp_id = nullptr; > return tp_id; > } > > std::vector<std::thread> threads; > hsh::blocking_queue<task_t> tasks; > }; > > inline bool operator==(const basic_thread_pool::dispatch_executor& lhs, const basic_thread_pool::dispatch_executor& rhs) noexcept { return lhs.pool == rhs.pool; } > > inline bool operator!=(const basic_thread_pool::dispatch_executor& lhs, const basic_thread_pool::dispatch_executor& rhs) noexcept { return !(lhs == rhs); } > > inline bool operator==(const basic_thread_pool::post_executor& lhs, const basic_thread_pool::post_executor& rhs) noexcept { return lhs.pool == rhs.pool; } > > inline bool operator!=(const basic_thread_pool::post_executor& lhs, const basic_thread_pool::post_executor& rhs) noexcept { return !(lhs == rhs); } > > namespace detail > { > template <typename Ex, typename F> > class executor_binder > { > public: > executor_binder(Ex ex, F&& f) : ex(ex), f(hsh::forward<F>(f)) {} > > void operator()() { ex.execute( hsh::move(f) ); } > > private: > /* TODO_J: these should be in a compressed pair */ > Ex ex; > F f; > }; > } > > /* can be used to bind an executor (Ex) to a function(F), to create a new function with signature void(). The resulting function is a 1 shot invocable, as it will move it's internal F into the executor */ > template<typename Ex, typename F> > auto bind_executor(Ex ex, F&& f) -> detail::executor_binder<Ex, decay_t<F>> > { > return detail::executor_binder<Ex, decay_t<F>>{ ex, hsh::forward<F>(f) }; > } > > /* Stop Token: synchronization method that can be passed to asynchronous tasks to coordinate cancellation */ > class stop_token > { > public: > stop_token() : _state(std::make_shared<state>()) {} > > template<class Alloc> > stop_token(std::allocator_arg_t, const Alloc& alloc) : _state(std::allocate_shared<state>(alloc)) {} > > stop_token(const stop_token&) = default; > stop_token(stop_token&&) = default; > ~stop_token() = default; > > stop_token& operator=(const stop_token&) = default; > stop_token& operator=(stop_token&&) = default; > > void request_stop() noexcept { _state->request_stop(); } > > bool stop_requested() const noexcept { return _state->stop_requested(); } > > private: > struct state > { > void request_stop() noexcept { stop_flag.exchange(true, std::memory_order_release); } > bool stop_requested() const noexcept { return stop_flag.load(std::memory_order_acquire); } > > std::atomic_bool stop_flag{ false }; > }; > std::shared_ptr<state> _state; > }; > >template <class T> > class shared_future; > > template <typename T> > class promise; > > template <class> > class packaged_task; > > template <class T> > class future; > > /* flag that can be passed to then, to make sure the future isn't unwrapped before the call to then */ > struct no_unwrap_t { explicit no_unwrap_t() = default; }; > constexpr no_unwrap_t no_unwrap{}; > > /* Type Trait Helpers */ > namespace future_details > { > struct future_tag_t { explicit future_tag_t() = default; }; > > struct shared_future_tag_t { explicit shared_future_tag_t() = default; }; > > > template<typename T> > struct is_future_impl : std::false_type > { > using type = T; > }; > > template<typename T> > struct is_future_impl<hsh::future<T>> : std::true_type > { > using type = T; > > using tag = future_tag_t; > }; > > template<typename T> > struct is_future_impl<hsh::shared_future<T>> : std::true_type > { > using type = T; > > using tag = shared_future_tag_t; > }; > } > > /* Type Trait for telling if a template paramter is a future or shared_future */ > template <typename T> > using is_future = future_details::is_future_impl<decay_t<T>>; > > template <typename T> > using future_value_type_t = typename future_details::is_future_impl<decay_t<T>>::type; > > > namespace future_details > { > enum class future_state > { > uninitialized = 0, > value = 1, > exception = 2, > }; > > inline void throw_promise_already_satisfied() { throw std::future_error(std::future_errc::promise_already_satisfied); } > inline void throw_no_state() { throw std::future_error(std::future_errc::no_state); } > inline void throw_future_already_retrieved() { throw std::future_error(std::future_errc::future_already_retrieved); } > > template <class T> > class shared_state_base > { > public: > shared_state_base() : dummy{} {} > > shared_state_base(const shared_state_base&) = delete; > shared_state_base(shared_state_base&&) = delete; > > template <class... Args> > inline void construct_value(Args&&... args) > { > new (std::addressof(this->value)) T(std::forward<Args>(args)...); > } > > inline void construct_error(std::exception_ptr&& e) > { > new (std::addressof(this->exception)) std::exception_ptr(std::move(e)); > } > > ~shared_state_base() > { > switch (state) > { > case future_state::value: > value.~T(); > break; > case future_state::exception: > exception.~exception_ptr(); > break; > default: > break; > } > } > > shared_state_base& operator=(const shared_state_base&) = delete; > shared_state_base& operator=(shared_state_base&&) = delete; > > future_state unwrap_helper(future_tag_t, shared_state_base& other) > { > if (future_state::value == other.state) { > construct_value( std::move(other.value) ); > return future_state::value; > } else { > construct_error( std::move(other.exception) ); > return future_state::exception; > } > } > > future_state unwrap_helper(shared_future_tag_t, shared_state_base& other) > { > if (future_state::value == other.state) { > construct_value( other.value ); > return future_state::value; > } else { > auto copy = other.exception; > construct_error( std::move(copy) ); > return future_state::exception; > } > } > > union > { > char dummy; > T value; > std::exception_ptr exception; > }; > mutable std::mutex lock; > mutable std::condition_variable cv; > future_state state = future_state::uninitialized; > }; > > template <> > class shared_state_base<void> > { > public: > shared_state_base() : dummy{} {} > > shared_state_base(const shared_state_base&) = delete; > shared_state_base(shared_state_base&&) = delete; > > ~shared_state_base() > { > if (future_state::exception == state) { > exception.~exception_ptr(); > } > } > > shared_state_base& operator=(const shared_state_base&) = delete; > shared_state_base& operator=(shared_state_base&&) = delete; > > inline void construct_error(std::exception_ptr&& e) > { > new (std::addressof(this->exception)) std::exception_ptr(std::move(e)); > } > > > future_state unwrap_helper(future_tag_t, shared_state_base& other) > { > if (future_state::value == other.state) { > return future_state::value; > } else { > construct_error(std::move(other.exception)); > return future_state::exception; > } > } > > future_state unwrap_helper(shared_future_tag_t, shared_state_base& other) > { > if (future_state::value == other.state) { > return future_state::value; > } else { > auto copy = other.exception; > construct_error(std::move(copy)); > return future_state::exception; > } > } > > union > { > char dummy; > std::exception_ptr exception; > }; > mutable std::mutex lock; > mutable std::condition_variable cv; > future_state state = future_state::uninitialized; > }; > > template <typename T> > class shared_state : public shared_state_base<T> > { > static_assert(!std::is_reference<T>::value, "hsh::future/promise doesn't support references. If needed use std::reference_wrapper"); > public: > > /* ----- Status Helpers ----- */ > bool is_ready() const > { > std::lock_guard<std::mutex> scoped_lock{ this->lock }; > return future_state::uninitialized != this->state; > } > > bool has_value() const > { > std::lock_guard<std::mutex> scoped_lock{ this->lock }; > return future_state::value == this->state; > } > > bool has_exception() const > { > std::lock_guard<std::mutex> scoped_lock{ this->lock }; > return future_state::exception == this->state; > } > > /* --- Value Observers --- */ > > /* Void Overload */ > template<typename T1 = T, enable_if_t<std::is_void<T1>::value>* = nullptr> > T1 get() const & > { > wait(); > if (future_state::exception == this->state) { > std::rethrow_exception(this->exception); > } > } > > /* Non-Void L-Value Overload */ > template<typename T1 = T, enable_if_t<!std::is_void<T1>::value>* = nullptr> > const T1& get() const & > { > wait(); > if (future_state::exception == this->state) { > std::rethrow_exception(this->exception); > } > return this->value; > } > > /* Non-Void R-Value Overload */ > template<typename T1 = T, enable_if_t<!std::is_void<T1>::value>* = nullptr> > T1 get() && > { > wait(); > if (future_state::exception == this->state) { > std::rethrow_exception(this->exception); > } > return std::move(this->value); > } > > std::exception_ptr get_exception_ptr() > { > wait(); > return future_state::exception == this->state ? this->exception : std::exception_ptr{}; > } > > /* ---- Wait Related Functions ---- */ > void wait() const > { > std::unique_lock<std::mutex> ulock{ this->lock }; > this->cv.wait(ulock, [this]() { return future_state::uninitialized != this->state; }); > } > > template <class Rep, class Period> > std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout) const > { > std::unique_lock<std::mutex> ulock{ this->lock }; > if (this->cv.wait_for(ulock, timeout, [this]() { return future_state::uninitialized != this->state; })) { > return std::future_status::ready; > } > return std::future_status::timeout; > } > > template <class Clock, class Duration> > std::future_status wait_until(const std::chrono::time_point<Clock, Duration>& timeout) const > { > std::unique_lock<std::mutex> ulock{ this->lock }; > if (this->cv.wait_until(ulock, timeout, [this]() { return future_state::uninitialized != this->state; })) { > return std::future_status::ready; > } > return std::future_status::timeout; > } > > /* ----- Value Set Function ----- */ > /* Void Overload */ > template<typename T1 = T, enable_if_t<std::is_void<T1>::value>* = nullptr> > void set_value() > { > if (future_state::uninitialized != this->state) { > throw_promise_already_satisfied(); > } > set_ready(future_state::value); > } > > /* Non-Void (Perfect Forwarding) */ > template<typename T1 = T, enable_if_t<!std::is_void<T1>::value>* = nullptr> > void set_value(T1&& val) > { > if ( future_state::uninitialized != this->state ) { > throw_promise_already_satisfied(); > } > > this->construct_value(std::forward<T1>(val)); > set_ready(future_state::value); > } > > /* Void Set With */ > template<class F, class... Args, typename T1 = T, enable_if_t<std::is_void<T1>::value>* = nullptr> > void set_with(F&& f, Args&&... args) > { > if (future_state::uninitialized != this->state) { > throw_promise_already_satisfied(); > } > > try { > hsh::invoke(std::forward<F>(f), std::forward<Args>(args)...); > set_ready(future_state::value); > } > catch (...) { > this->construct_error(std::current_exception()); > set_ready(future_state::exception); > } > } > > /* Non-Void Set With */ > template<class F, class... Args, typename T1 = T, enable_if_t<!std::is_void<T1>::value>* = nullptr> > void set_with(F&& f, Args&&... args) > { > if (future_state::uninitialized != this->state) { > throw_promise_already_satisfied(); > } > > try { > this->construct_value(hsh::invoke(std::forward<F>(f), std::forward<Args>(args)...)); > set_ready(future_state::value); > } catch (...) { > this->construct_error(std::current_exception()); > set_ready(future_state::exception); > } > } > > template <typename... Args> > void emplace(Args&&... args) > { > if ( future_state::uninitialized != this->state ) { > throw_promise_already_satisfied(); > } > this->construct_value(std::forward<Args>(args)...); > set_ready(future_state::value); > } > > template <typename E> > void set_exception(const E& e) > { > if (future_state::uninitialized != this->state) { > throw_promise_already_satisfied(); > } > this->construct_error(std::make_exception_ptr(e)); > set_ready(future_state::exception); > } > > void set_exception(std::exception_ptr e) > { > if (future_state::uninitialized != this->state) { > throw_promise_already_satisfied(); > } > this->construct_error(std::move(e)); > set_ready(future_state::exception); > } > > /* ----- Continuation Based Functions ----- */ > template <typename F> > void add_continuation(F&& f) > { > { > std::lock_guard<std::mutex> scoped_lock{ this->lock }; > if ( future_state::uninitialized == this->state ) { > continuations.emplace_back( std::forward<F>(f) ); > return; > } > } > > /* don't need to lock bc value(or exception) is already set, and will throw and exception if someboday tries to reset it (locking would actually cause a deadlock here)*/ > hsh::invoke( std::forward<F>(f) ); > } > > void abandon_state() > { > if (future_state::uninitialized == this->state){ > set_exception(std::future_error(std::future_errc::broken_promise)); > } > } > > /* Helpers that should only be called from inside a continuation when we know the state is complete */ > bool continuation_has_value() const noexcept { return future_state::value == this->state; } > bool continuation_has_exception() const noexcept { return future_state::exception == this->state; } > > /* Non-Void L-Value Overload */ > template<typename T1 = T, enable_if_t<!std::is_void<T1>::value>* = nullptr> > const T1& continuation_get() const & { return this->value; } > > /* Non-Void R-Value Overload */ > template<typename T1 = T, enable_if_t<!std::is_void<T1>::value>* = nullptr> > T1 continuation_get() && { return std::move(this->value); } > > std::exception_ptr continuation_get_exception_ptr() const noexcept { return this->exception; } > > void unwrap_callback(future_tag_t tag, shared_state& other) { this->set_ready(this->unwrap_helper(tag, other)); } > > void unwrap_callback(shared_future_tag_t tag, shared_state& other) { this->set_ready(this->unwrap_helper(tag, other)); } > > private: > inline void set_ready(future_state new_state) > { > { > std::unique_lock<std::mutex> ulock{ this->lock }; > this->state = new_state; > if (!continuations.empty()) { > fire_continuations(ulock); > } > } > this->cv.notify_all(); > } > > /* lock should be held when calling this function */ > void fire_continuations(std::unique_lock<std::mutex>& ulock) > { > /* move vector */ > std::vector<hsh::unique_function<void()>> tmp_continuations; > tmp_continuations.swap(continuations); > ulock.unlock(); > > for (auto &continuation : tmp_continuations) { > continuation(); > } > } > > std::vector<hsh::unique_function<void()>> continuations; > }; > > template<typename T, typename R, bool IsShared> > struct on_value > { > explicit on_value(std::shared_ptr<shared_state<T>> state) noexcept : state(std::move(state)) {} > > future<R> get_future() { return promise.get_future(); } > > /* Void T overload */ > template<typename F, typename T1 = T, enable_if_t<std::is_void<T1>::value>* = nullptr> > void operator()(F&& f) > { > if (state->continuation_has_value()) { > promise.set_with(std::forward<F>(f)); > } else { > promise.set_exception(state->continuation_get_exception_ptr()); > } > } > > /* Non-Void T overload (Future) */ > template<typename F, typename T1 = T, enable_if_t<!std::is_void<T1>::value && !IsShared>* = nullptr> > void operator()(F&& f) > { > if (state->continuation_has_value()) { > promise.set_with(std::forward<F>(f), std::move(*state).continuation_get()); > } else { > promise.set_exception(state->continuation_get_exception_ptr()); > } > } > > /* Non-Void T overload (SharedFuture) */ > template<typename F, typename T1 = T, enable_if_t<!std::is_void<T1>::value && IsShared>* = nullptr> > void operator()(F&& f) > { > if (state->continuation_has_value()) { > promise.set_with(std::forward<F>(f), state->continuation_get()); > } else { > promise.set_exception(state->continuation_get_exception_ptr()); > } > } > > private: > hsh::promise<R> promise; > std::shared_ptr<shared_state<T>> state; > }; > > template<typename T, bool IsShared> > struct on_error > { > public: > explicit on_error(std::shared_ptr<shared_state<T>> state) noexcept : state(std::move(state)) {} > > future<T> get_future() { return promise.get_future(); } > > /* Void T overload */ > template<typename F, typename T1 = T, enable_if_t<std::is_void<T1>::value>* = nullptr> > void operator()(F&& f) > { > if (state->continuation_has_value()) { > promise.set_value(); > } else { > promise.set_with(std::forward<F>(f), state->continuation_get_exception_ptr()); > } > } > > /* Non-Void T overload (Future) */ > template<typename F, typename T1 = T, enable_if_t<!std::is_void<T1>::value && !IsShared>* = nullptr> > void operator()(F&& f) > { > if (state->continuation_has_value()) { > promise.set_value( std::move(*state).continuation_get() ); > } else { > promise.set_with(std::forward<F>(f), state->continuation_get_exception_ptr()); > } > } > > /* Non-Void T overload (SharedFuture) */ > template<typename F, typename T1 = T, enable_if_t<!std::is_void<T1>::value && IsShared>* = nullptr> > void operator()(F&& f) > { > if (state->continuation_has_value()) { > promise.set_value( state->continuation_get() ); > } else { > promise.set_with(std::forward<F>(f), state->continuation_get_exception_ptr()); > } > } > > private: > hsh::promise<T> promise; > std::shared_ptr<shared_state<T>> state; > }; > > template<typename T> > struct fallback_to > { > explicit fallback_to(T&& val) : val(std::forward<T>(val)) {} > > T&& operator()(std::exception_ptr e) { return std::move(val); } > > T val; > }; > > struct future_waiter > { > template<typename F> > void operator()(F&& future) { future.wait(); } > }; > > } // end future_details > > template <class T> > class future > { > using shared_state_ptr = std::shared_ptr<future_details::shared_state<T>>; > public: > future() = default; > > future(future const& rhs) = delete; > future(future && other) = default; > > /* unwrap move constructor */ > explicit future(future<future<T>>&& rhs) : future(rhs.unwrap()) {} > explicit future(future<shared_future<T>>&& rhs) : future(rhs.unwrap()) {} > > ~future() = default; > > future& operator=(future const& rhs) = delete; > future& operator=(future && other) = default; > > /* leaving this function as is, bc that's what the std version does, but it seems like it should have a r-value specifier */ > shared_future<T> share() noexcept { return std::move(state); } > > /* unwrap: future<future<T>> -> future<T>, future<shared_future<T>> -> future<T> */ > template<typename T1 = T, > typename R = enable_if_t<is_future<T1>::value, future_value_type_t<T1>>> > auto unwrap() -> future<R> > { > if (!state) { > future_details::throw_no_state(); > } > /* move state so it will be empty after this call */ > auto temp_state = std::move(state); > /* Create a new state what will be the value of the inner future */ > auto new_state = std::make_shared<future_details::shared_state<R>>(); > /* Add a continuation to the current state so we can process it's result when it's ready */ > temp_state->add_continuation([temp_state, new_state]() > { > /* if the outter future had an exception we can short circuit, and set the exception */ > if (temp_state->continuation_has_exception()) { > new_state->set_exception(temp_state->continuation_get_exception_ptr()); > return; > } > > /* Otherwise we need to add a continuation to the inner future, so we can process it's return when it's done */ > auto inner_state = temp_state->continuation_get().state; > inner_state->add_continuation([inner_state, new_state]() > { > new_state->unwrap_callback(is_future<T1>::tag(), *inner_state); > }); > }); > return future<R>{ new_state }; > } > > /* ----- Status Helpers ----- */ > bool valid() const noexcept { return nullptr != state; } > explicit operator bool() const noexcept { return valid(); } > > bool is_ready() const { return valid() && state->is_ready(); } > bool has_exception() const { return valid() && state->has_exception(); } > bool has_value() const { return valid() && state->has_value(); } > > /* --- Value Observers --- */ > T get() > { > if (!state) { > future_details::throw_no_state(); > } > /* steal the current state out so it's NULL after this call, then use the r-value get of the shared_state */ > auto local_state = std::move(state); > return std::move(*local_state).get(); > } > > std::exception_ptr get_exception_ptr() > { > if (!state) { > return std::exception_ptr{}; > } > return state->get_exception_ptr(); > } > > /* ---- Wait Related Functions ---- */ > void wait() const > { > if ( !state ) { > future_details::throw_no_state(); > } > state->wait(); > } > > template <class Rep, class Period> > std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout) const > { > if (!state) { > future_details::throw_no_state(); > } > return state->wait_for(timeout); > } > > template <class Clock, class Duration> > std::future_status wait_until(const std::chrono::time_point<Clock, Duration>& timeout) const > { > if (!state) { > future_details::throw_no_state(); > } > return state->wait_until(timeout); > } > > /* ----- Continuation Based Functions ----- */ > template<typename Ex, > typename F, > typename Result = hsh::invoke_result_t<F, future>, > typename R = conditional_t<is_future<Result>::value, Result, future<Result>>> > auto then(Ex ex, F&& f) -> R > { > if (!state) { > future_details::throw_no_state(); > } > > auto local_state = std::move(state); > packaged_task<Result()> task{ hsh::bind_front([local_state](F& f) mutable { return hsh::invoke(f, future<T>{std::move(local_state)}); }, std::forward<F>(f)) }; > R ret{ task.get_future() }; > local_state->add_continuation( hsh::bind_executor(ex, std::move(task)) ); > return ret; > } > > /* overload that can be used to prevent the implicit unwrapping that happens on future.then */ > template<typename Ex, > typename F, > typename R = hsh::invoke_result_t<F, future>> > auto then(no_unwrap_t, Ex ex, F&& f) -> future<R> > { > if (!state) { > future_details::throw_no_state(); > } > > auto local_state = std::move(state); > packaged_task<R()> task{ hsh::bind_front([local_state](F& f) mutable { return hsh::invoke(f, future<T>{std::move(local_state)}); }, std::forward<F>(f)) }; > auto ret = task.get_future(); > local_state->add_continuation(hsh::bind_executor(ex, std::move(task))); > return ret; > } > > /* Void on-value overload */ > template<typename Ex, > typename F, > typename T1 = T, > enable_if_t<std::is_void<T1>::value>* = nullptr, > typename Result = hsh::invoke_result_t<F>, > typename R = conditional_t<is_future<Result>::value, Result, future<Result>>> > auto on_value(Ex ex, F&& f) -> R > { > if (!state) { > future_details::throw_no_state(); > } > > auto local_state = std::move(state); //move state so it will be empty after this call > future_details::on_value<T, Result, false> task{ local_state }; > R ret{ task.get_future() }; > local_state->add_continuation(hsh::bind_executor(ex, hsh::bind_front(std::move(task), std::forward<F>(f)))); > return ret; > } > > /* Void on-value overload(no unwrap overload) */ > template<typename Ex, > typename F, > typename T1 = T, > enable_if_t<std::is_void<T1>::value>* = nullptr, > typename R = hsh::invoke_result_t<F>> > auto on_value(no_unwrap_t, Ex ex, F&& f) -> future<R> > { > if (!state) { > future_details::throw_no_state(); > } > > auto local_state = std::move(state); //move state so it will be empty after this call > future_details::on_value<T, R, false> task{ local_state }; > auto ret = task.get_future(); > local_state->add_continuation( hsh::bind_executor( ex, hsh::bind_front(std::move(task), std::forward<F>(f)) ) ); > return ret; > } > > /* Non-Void on-value overload */ > template<typename Ex, > typename F, > typename T1 = T, > enable_if_t<!std::is_void<T1>::value>* = nullptr, > typename Result = hsh::invoke_result_t<F, T1&&>, > typename R = conditional_t<is_future<Result>::value, Result, future<Result>>> > auto on_value(Ex ex, F&& f) -> R > { > if (!state) { > future_details::throw_no_state(); > } > > auto local_state = std::move(state); //move state so it will be empty after this call > future_details::on_value<T, Result, false> task{ local_state }; > R ret{ task.get_future() }; > local_state->add_continuation(hsh::bind_executor(ex, hsh::bind_front(std::move(task), std::forward<F>(f)))); > return ret; > } > > > template<typename Ex, > typename F, > typename T1 = T, > enable_if_t<!std::is_void<T1>::value>* = nullptr, > typename R = hsh::invoke_result_t<F, T1&&>> > auto on_value(no_unwrap_t, Ex ex, F&& f) -> future<R> > { > if (!state) { > future_details::throw_no_state(); > } > > auto local_state = std::move(state); //move state so it will be empty after this call > future_details::on_value<T, R, false> task{ local_state }; > auto ret = task.get_future(); > local_state->add_continuation(hsh::bind_executor(ex, hsh::bind_front(std::move(task), std::forward<F>(f)))); > return ret; > } > > template<typename Ex, typename F, enable_if_t<hsh::is_invocable_r<T, F, std::exception_ptr>::value>* = nullptr> > auto on_error(Ex ex, F&& f) -> future<T> > { > if (!state) { > future_details::throw_no_state(); > } > > auto local_state = std::move(state); //move state so it will be empty after this call > future_details::on_error<T, false> task{ local_state }; > auto ret = task.get_future(); > local_state->add_continuation(hsh::bind_executor(ex, hsh::bind_front(std::move(task), std::forward<F>(f)))); > return ret; > } > > template<typename T1 = T, enable_if_t<conjunction<std::is_same<T1, T>, negation<std::is_void<T1>>>::value>* = nullptr> > auto fallback_to(T1&& fallback_val) -> future<T1> > { > if (!state) { > future_details::throw_no_state(); > } > > auto local_state = std::move(state); //move state so it will be empty after this call > future_details::on_error<T, false> task{ local_state }; > auto ret = task.get_future(); > local_state->add_continuation(hsh::bind_front(std::move(task), future_details::fallback_to<T1>(std::forward<T1>(fallback_val)))); > return ret; > } > > template<typename Ex, typename F, enable_if_t<hsh::is_invocable<F&&>::value>* = nullptr> > void add_completion_callback(Ex ex, F&& f) > { > if (!state) { > future_details::throw_no_state(); > } > state->add_continuation(hsh::bind_executor(ex, std::forward<F>(f))); > } > > void swap(future& other) noexcept { this->state.swap(other.state); } > > private: > template<class> > friend class future; > template<class> > friend class packaged_task; > template<class> > friend class shared_future; > friend class promise<T>; > > future(shared_state_ptr state) : state(std::move(state)) {} > > shared_state_ptr state; > }; > > template <typename T> > void swap(future<T>& lhs, future<T>& rhs) noexcept { lhs.swap(rhs); } > > template <class T> > class shared_future > { > using shared_state_ptr = std::shared_ptr<future_details::shared_state<T>>; > public: > shared_future() = default; > ~shared_future() = default; > > /* Copy Constructor/Assignment operator */ > shared_future(const shared_future& other) = default; > shared_future(shared_future&& other) = default; > shared_future(future<T>&& other) noexcept : state(std::move(other.state)) {} > > /* unwrapping constructors */ > explicit shared_future(future<shared_future<T>>&& other) : shared_future(other.unwrap()) {} > explicit shared_future(future<future<T>>&& other) : shared_future(other.unwrap()) {} > explicit shared_future(const shared_future<shared_future<T>>& other) : shared_future(other.unwrap()) {} > explicit shared_future(const shared_future<future<T>>& other) : shared_future(other.unwrap()) {} > explicit shared_future(shared_future<shared_future<T>>&& other) : shared_future(other.unwrap()) {} > explicit shared_future(shared_future<future<T>>&& other) : shared_future(other.unwrap()) {} > > /* Move Constructor/Assignment operator */ > shared_future& operator=(const shared_future& other) = default; > shared_future & operator=(shared_future&& other) = default; > shared_future& operator=(future<T>&& other) noexcept > { > this->state = std::move(other.state); > return *this; > } > > /* unwrap: future<future<T>> -> future<T> */ > template<typename T1 = T, > typename R = enable_if_t<is_future<T1>::value, future_value_type_t<T1>>> > auto unwrap() -> future<R> > { > if (!state) { > future_details::throw_no_state(); > } > /* Create a new state what will be the value of the inner future */ > auto new_state = std::make_shared<future_details::shared_state<R>>(); > /* Add a continuation to the current state so we can process it's result when it's ready */ > auto temp_state = state; > temp_state->add_continuation([temp_state, new_state]() > { > /* if the outter future had an exception we can short circuit, and set the exception */ > if (temp_state->continuation_has_exception()) { > new_state->set_exception(temp_state->continuation_get_exception_ptr()); > return; > } > > /* Otherwise we need to add a continuation to the inner future, so we can process it's return when it's done */ > auto inner_state = temp_state->continuation_get().state; > inner_state->add_continuation([inner_state, new_state]() > { > new_state->unwrap_callback(is_future<T1>::tag(), *inner_state); > } > ); > } > ); > return future<R>{ new_state }; > } > > /* ----- Status Helpers ----- */ > bool valid() const noexcept { return nullptr != state; } > explicit operator bool() const noexcept { return valid(); } > > bool is_ready() const { return valid() && state->is_ready(); } > bool has_exception() const { return valid() && state->has_exception(); } > bool has_value() const { return valid() && state->has_value(); } > > /* Void Overload */ > template<typename T1 = T, enable_if_t<std::is_void<T1>::value>* = nullptr> > T1 get() const > { > if (!state) { > future_details::throw_no_state(); > } > return state->get(); > } > > /* Non-Void Overload */ > template<typename T1 = T, enable_if_t<!std::is_void<T1>::value>* = nullptr> > const T1& get() const > { > if (!state) { > future_details::throw_no_state(); > } > return state->get(); > } > > std::exception_ptr get_exception_ptr() > { > if (!state) { > return std::exception_ptr{}; > } > return state->get_exception_ptr(); > } > > /* ---- Wait Related Functions ---- */ > void wait() const > { > if (!state) { > future_details::throw_no_state(); > } > state->wait(); > } > > template <class Rep, class Period> > std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeout) const > { > if (!state) { > future_details::throw_no_state(); > } > return state->wait_for(timeout); > } > > template <class Clock, class Duration> > std::future_status wait_until(const std::chrono::time_point<Clock, Duration>& timeout) const > { > if (!state) { > future_details::throw_no_state(); > } > return state->wait_until(timeout); > } > > /* ----- Continuation Based Functions ----- */ > > /* unwrapping then */ > template<typename Ex, typename F, > typename Result = hsh::invoke_result_t<F, shared_future>, > typename R = conditional_t<is_future<Result>::value, Result, future<Result>>> > auto then(Ex ex, F&& f) -> R > { > if (!state) { > future_details::throw_no_state(); > } > > packaged_task<Result()> task{ hsh::bind_front([](F& f, shared_state_ptr& state) mutable { return hsh::invoke(f, shared_future{std::move(state)}); }, std::forward<F>(f), state) }; > R ret{ task.get_future() }; > state->add_continuation( hsh::bind_executor(ex, std::move(task)) ); > return ret; > } > > /* overload for disabling the default unwrapping behavior of then */ > template<typename Ex, > typename F, > typename R = hsh::invoke_result_t<F, shared_future>> > auto then(no_unwrap_t, Ex ex, F&& f) -> future<R> > { > if (!state) { > future_details::throw_no_state(); > } > > packaged_task<R()> task{ hsh::bind_front([](F& f, shared_state_ptr& state) mutable { return hsh::invoke(f, future<T>{std::move(state)}); }, std::forward<F>(f), state) }; > auto ret = task.get_future(); > state->add_continuation(hsh::bind_executor(ex, std::move(task))); > return ret; > } > > /* Void on-value overload */ > template<typename Ex, > typename F, > typename T1 = T, > enable_if_t<std::is_void<T1>::value>* = nullptr, > typename Result = hsh::invoke_result_t<F&&>, > typename R = conditional_t<is_future<Result>::value, Result, future<Result>>> > auto on_value(Ex ex, F&& f) -> R > { > if (!state) { > future_details::throw_no_state(); > } > > future_details::on_value<T, Result, true> task{ state }; > R ret{ task.get_future() }; > state->add_continuation(hsh::bind_executor(ex, hsh::bind_front(std::move(task), std::forward<F>(f)))); > return ret; > } > > /* Void on-value overload (no unwrap) */ > template<typename Ex, > typename F, > typename T1 = T, > enable_if_t<std::is_void<T1>::value>* = nullptr, > typename R = hsh::invoke_result_t<F&&>> > auto on_value(no_unwrap_t, Ex ex, F&& f) -> future<R> > { > if (!state) { > future_details::throw_no_state(); > } > > future_details::on_value<T, R, true> task{ state }; > auto ret = task.get_future(); > state->add_continuation(hsh::bind_executor(ex, hsh::bind_front(std::move(task), std::forward<F>(f)))); > return ret; > } > > /* Non-Void on-value overload */ > template<typename Ex, > typename F, > typename T1 = T, > enable_if_t<!std::is_void<T1>::value>* = nullptr, > typename Result = hsh::invoke_result_t<F&&, const T1&>, > typename R = conditional_t<is_future<Result>::value, Result, future<Result>>> > auto on_value(Ex ex, F&& f) -> R > { > if (!state) { > future_details::throw_no_state(); > } > > future_details::on_value<T, Result, true> task{ state }; > R ret{ task.get_future() }; > state->add_continuation(hsh::bind_executor(ex, hsh::bind_front(std::move(task), std::forward<F>(f)))); > return ret; > } > > /* Non-Void on-value overload (no unwrap) */ > template<typename Ex, > typename F, > typename T1 = T, > enable_if_t<!std::is_void<T1>::value>* = nullptr, > typename R = hsh::invoke_result_t<F&&, const T1&>> > auto on_value(no_unwrap_t, Ex ex, F&& f) -> future<R> > { > if (!state) { > future_details::throw_no_state(); > } > > future_details::on_value<T, R, true> task{ state }; > auto ret = task.get_future(); > state->add_continuation(hsh::bind_executor(ex, hsh::bind_front(std::move(task), std::forward<F>(f)))); > return ret; > } > > template<typename Ex, typename F, enable_if_t<hsh::is_invocable_r<T, F, std::exception_ptr>::value>* = nullptr> > auto on_error(Ex ex, F&& f) -> future<T> > { > if (!state) { > future_details::throw_no_state(); > } > > future_details::on_error<T, true> task{ state }; > auto ret = task.get_future(); > state->add_continuation(hsh::bind_executor(ex, hsh::bind_front(std::move(task), std::forward<F>(f)))); > return ret; > } > > template<typename T1 = T, enable_if_t<!std::is_void<T1>::value>* = nullptr> > auto fallback_to(T1&& fallback_val) -> future<T1> > { > if (!state) { > future_details::throw_no_state(); > } > > future_details::on_error<T, true> task{ state }; > auto ret = task.get_future(); > state->add_continuation(hsh::bind_front(std::move(task), future_details::fallback_to<T1>(std::forward<T1>(fallback_val)))); > return ret; > } > > template<typename Ex, typename F, enable_if_t<hsh::is_invocable<F&&>::value>* = nullptr> > void add_completion_callback(Ex ex, F&& f) > { > if (!state) { > future_details::throw_no_state(); > } > state->add_continuation(hsh::bind_executor(ex, std::forward<F>(f))); > } > > void swap(shared_future& other) noexcept { this->state.swap(other.state); } > > private: > template<class> > friend class shared_future; > > template<class> > friend class future; > > shared_future(shared_state_ptr&& state) : state(state) {} > shared_state_ptr state; > }; > > template <typename T> > void swap(shared_future<T>& lhs, shared_future<T>& rhs) noexcept { lhs.swap(rhs); } > > template <typename T> > class promise > { > using shared_state = future_details::shared_state<T>; > public: > promise() : state(std::make_shared<shared_state>()) {} > > template<class Alloc> > promise(std::allocator_arg_t, const Alloc& alloc) : state(std::allocate_shared<shared_state>(alloc)) {} > > promise(const promise& rhs) = delete; > promise(promise&& rhs) = default; > > ~promise() { if (state) { state->abandon_state(); } } > > promise& operator=(const promise& rhs) = delete; > promise& operator=(promise&& rhs) noexcept > { > if (state) { state->abandon_state(); } > this->state = std::move(rhs.state); > this->has_future = rhs.has_future; > return *this; > } > > future<T> get_future() > { > if (!state) { > future_details::throw_no_state(); > } > if (!has_future) { > future_details::throw_future_already_retrieved(); > } > has_future = false; > return state; > } > > template<typename T1 = T, enable_if_t<std::is_void<T1>::value>* = nullptr> > void set_value() > { > if (!state) { > future_details::throw_no_state(); > } > return state->set_value(); > } > > /* Non-Void Perfect Forwarding */ > template<typename T1 = T, enable_if_t<!std::is_void<T1>::value>* = nullptr> > void set_value(T1&& val) > { > if (!state) { > future_details::throw_no_state(); > } > return state->set_value(std::forward<T1>(val)); > } > > template<typename F, typename... Args> > void set_with(F&& f, Args&&... args) > { > if (!state) { > future_details::throw_no_state(); > } > return state->set_with( std::forward<F>(f), std::forward<Args>(args)... ); > } > > template <typename... Args, typename T1 = T, enable_if_t<std::is_constructible<T1, Args...>::value>* = nullptr> > void emplace(Args&&... args) > { > if ( !state ) { > future_details::throw_no_state(); > } > state->emplace(std::forward<Args>(args)...); > } > > template <typename E> > void set_exception(const E& e) > { > if (!state) { > future_details::throw_no_state(); > } > return state->set_exception(e); > } > > void set_exception(std::exception_ptr e) > { > if ( !state ) { > future_details::throw_no_state(); > } > return state->set_exception(std::move(e)); > } > > void swap(promise& other) noexcept > { > std::swap(this->has_future, other.has_future); > this->state.swap(other.state); > } > > private: > std::shared_ptr<shared_state> state; > bool has_future = true; > }; > > template <typename T> > void swap(promise<T>& lhs, promise<T>& rhs) noexcept { lhs.swap(rhs); } > > template <class R, class... Args> > class packaged_task<R(Args...)> > { > using shared_state = future_details::shared_state<R>; > public: > packaged_task() = default; > > template <class F, enable_if_t<conjunction<negation<std::is_same<decay_t<F>, packaged_task>>, is_invocable_r<R, F&&, Args...>>::value>* = nullptr> > explicit packaged_task(F&& f) : func(hsh::forward<F>(f)), state(std::make_shared<shared_state>()) {} > > packaged_task(const packaged_task&) = delete; > packaged_task(packaged_task&&) = default; > > ~packaged_task() { if (state) { state->abandon_state(); } } > > packaged_task& operator=(const packaged_task&) = delete; > packaged_task& operator=(packaged_task&& other) noexcept > { > if (state) { state->abandon_state(); } > this->has_future = other.has_future; > this->state = std::move(other.state); > this->func = std::move(other.func); > return *this; > } > > explicit operator bool() const noexcept { return valid(); } > bool valid() const noexcept { return state && func; } > > future<R> get_future() > { > if (!state) { > future_details::throw_no_state(); > } > if (!has_future) { > future_details::throw_future_already_retrieved(); > } > has_future = false; > return future<R>{ state }; > } > > void operator()(Args... args) > { > if (!state) { > future_details::throw_no_state(); > } > state->set_with(func, std::forward<Args>(args)...); > } > > void reset() > { > if (!state) { > future_details::throw_no_state(); > } else if ( !state->is_ready() ) { > /* if we never filled in the promise, we need to 'fullfill' it be setting a exception of broken promise */ > state->set_exception(std::future_error(std::future_errc::broken_promise)); > } > state = std::make_shared<shared_state>(); > has_future = true; > } > > void swap(packaged_task& rhs) noexcept > { > std::swap(this->has_future, rhs.has_future); > this->state.swap( rhs.state ); > this->func.swap( rhs.func ); > } > > private: > std::shared_ptr<shared_state> state; > unique_function<R(Args...)> func; > bool has_future = true; > }; > > template <class R, class... Args> > void swap(packaged_task<R(Args...)>& lhs, packaged_task<R(Args...)>& rhs) noexcept { lhs.swap(rhs); } > > /* Make Ready Future */ > inline auto make_ready_future() -> hsh::future<void> > { > hsh::promise<void> p; > p.set_value(); > return p.get_future(); > } > > template<typename T, typename T1 = decay_t<T>> > auto make_ready_future(T&& value) -> future<T1> > { > hsh::promise<T1> p; > p.set_value(std::forward<T>(value)); > return p.get_future(); > } > > template<class T, typename... Args, enable_if_t<std::is_constructible<T, Args...>::value>* = nullptr> > auto make_ready_future(Args&&... args) -> future<T> > { > hsh::promise<T> p; > p.emplace(std::forward<Args>(args)...); > return p.get_future(); > } > > /* Make Exception Future */ > template <typename T> > auto make_exceptional_future(const std::exception_ptr& ex) -> future<T> > { > hsh::promise<T> p; > p.set_exception(ex); > return p.get_future(); > } > > template <typename T, typename E> > auto make_exceptional_future(const E& ex) -> future<T> > { > hsh::promise<T> p; > p.set_exception(ex); > return p.get_future(); > } > > template <typename T> > auto make_exceptional_future() -> future<T> > { > promise<T> p; > p.set_exception(std::current_exception()); > return p.get_future(); > } > > /* ----- ASYNC ---------- */ > template<typename Ex, > typename F, > typename... Args, > typename R = invoke_result_t<F&&, Args&&...>> > auto async(Ex ex, F&& f, Args&&... args) -> future<R> > { > packaged_task<R()> packaged_task{ hsh::bind_front(std::forward<F>(f), std::forward<Args>(args)...) }; > auto future = packaged_task.get_future(); > ex.execute(std::move(packaged_task)); > return future; > } > > /* ----- WAIT_ALL ----------- */ > template<typename F1, typename... Fs, enable_if_t<is_future<F1>::value>* = nullptr> > void wait_for_all(F1& f1, Fs&... fs) > { > hsh::for_each_parameter_pack(future_details::future_waiter{}, f1, fs...); > } > > template<typename Iterator, enable_if_t<is_future<typename std::iterator_traits<Iterator>::value_type>::value>* = nullptr> > void wait_for_all(Iterator begin, Iterator end) > { > for (; begin != end; ++begin) { > begin->wait(); > } > } > > /* ----- WAIT_ANY ----------- */ > namespace future_details > { > class future_any_waiter : public std::enable_shared_from_this<future_any_waiter> > { > private: > struct waiter > { > explicit waiter(unsigned index) noexcept : index(index) {} > > /* move constructor just needed for vector, it shouldn't be used */ > waiter(waiter&& other) : index(index), ready_flag(other.is_ready()) {} > > bool is_ready() const noexcept { return ready_flag; } > > void set_ready() noexcept { ready_flag = true; } > > unsigned index; > bool ready_flag = false; > }; > > public: > explicit future_any_waiter(size_t num_waiters) { waiters.reserve(num_waiters); } > > template <typename F> > void add(F& f) > { > auto current_index = static_cast<unsigned>(waiters.size()); > waiters.emplace_back(current_index); > f.add_completion_callback(hsh::inline_executor{}, hsh::bind_front(&future_any_waiter::notify, shared_from_this(), std::ref(waiters.back())) ); > } > > template <typename F1, typename ... Fs> > void add(F1& f1, Fs&... fs) > { > add(f1); > add(fs...); > } > > unsigned wait() > { > std::unique_lock<std::mutex> ulock{ lock }; > while (true) > { > for (auto& waiter : waiters) > { > if (waiter.is_ready()) { > return waiter.index; > } > } > cv.wait(ulock); > } > } > > void notify(waiter& waiter) > { > { > std::unique_lock<std::mutex> ulock{ lock }; > waiter.set_ready(); > } > cv.notify_one(); > } > > private: > std::mutex lock; > std::condition_variable cv; > std::vector<waiter> waiters; > }; > } > > template<typename F1, typename... Fs, enable_if_t<is_future<F1>::value>* = nullptr> > unsigned wait_for_any(F1& f1, Fs&... fs) > { > auto waiter = std::make_shared<future_details::future_any_waiter>(sizeof...(Fs) + 1); > waiter->add( f1, fs... ); > return waiter->wait(); > } > > template<typename Iterator, enable_if_t<is_future<typename std::iterator_traits<Iterator>::value_type>::value>* = nullptr> > Iterator wait_for_any(Iterator begin, Iterator end) > { > if (begin == end) { > return end; > } > > auto waiter = std::make_shared<future_details::future_any_waiter>(static_cast<unsigned>(std::distance(begin, end))); > for ( auto current = begin; current != end; ++current ) { > if ( current->is_ready() ) { > return current; > } else { > waiter->add( *current ); > } > } > return std::next( begin, waiter->wait() ); > } > > namespace future_details > { > template<typename FutureType> > class future_when_all_iterator : public std::enable_shared_from_this<future_when_all_iterator<FutureType>> > { > public: > template<typename Iterator, enable_if_t<std::is_same<future_tag_t, typename is_future<typename std::iterator_traits<Iterator>::value_type>::tag>::value>* = nullptr> > future_when_all_iterator(Iterator begin, Iterator end) : futures(std::make_move_iterator(begin), std::make_move_iterator(end)) {} > > template<typename Iterator, enable_if_t<std::is_same<shared_future_tag_t, typename is_future<typename std::iterator_traits<Iterator>::value_type>::tag>::value>* = nullptr> > future_when_all_iterator(Iterator begin, Iterator end) : futures(begin, end) {} > > ~future_when_all_iterator(){ > /* all futures callbacks have been called, and we're being destroyed, complete the promise */ > promise.set_value(std::move(futures)); > } > > /* Future Overload move */ > hsh::future<std::vector<FutureType>> run() > { > for (auto& future : futures) { > future.add_completion_callback( hsh::inline_executor{}, hsh::bind_front(&future_when_all_iterator::empty_func, this->shared_from_this()) ); > } > return promise.get_future(); > } > > private: > void empty_func() {} > > hsh::promise<std::vector<FutureType>> promise; > std::vector<FutureType> futures; > }; > > template<typename... Fs> > class future_when_all_variadic : public std::enable_shared_from_this<future_when_all_variadic<Fs...>> > { > using TUPLE_TYPE = std::tuple<decay_t<Fs>...>; > public: > future_when_all_variadic(Fs&&... fs) : futures(std::forward<Fs>(fs)...) {} > > ~future_when_all_variadic() { > /* all futures callbacks have been called, and we're being destroyed, complete the promise */ > promise.set_value(std::move(futures)); > } > > hsh::future<TUPLE_TYPE> run() > { > /* add a continuation to every future in the tuple */ > hsh::for_each_in_tuple(continuation_helper{ this->shared_from_this() }, futures); > return promise.get_future(); > } > > private: > struct continuation_helper > { > explicit continuation_helper(std::shared_ptr<future_when_all_variadic>&& ptr) : ptr(std::move(ptr)){} > > template<typename F> > void operator()(F&& future){ > future.add_completion_callback(hsh::inline_executor{}, hsh::bind_front(&future_when_all_variadic::empty_func, ptr)); > } > > std::shared_ptr<future_when_all_variadic> ptr; > }; > > void empty_func() {} > > hsh::promise<TUPLE_TYPE> promise; > TUPLE_TYPE futures; > }; > > > template<typename FutureType> > class future_when_any_iterator : public std::enable_shared_from_this<future_when_any_iterator<FutureType>> > { > public: > /* construct from future iterator, need to move all futures */ > template<typename Iterator, enable_if_t<std::is_same<future_tag_t, typename is_future<typename std::iterator_traits<Iterator>::value_type>::tag>::value>* = nullptr> > future_when_any_iterator(Iterator begin, Iterator end) : futures(std::make_move_iterator(begin), std::make_move_iterator(end)) {} > > /* construct from shared_future iterator, need to copy all shared_futures */ > template<typename Iterator, enable_if_t<std::is_same<shared_future_tag_t, typename is_future<typename std::iterator_traits<Iterator>::value_type>::tag>::value>* = nullptr> > future_when_any_iterator(Iterator begin, Iterator end) : futures(begin, end) {} > > hsh::future<std::vector<FutureType>> run() > { > for (auto& future : futures) { > future.add_completion_callback(hsh::inline_executor{}, hsh::bind_front(&future_when_any_iterator::notify, this->shared_from_this())); > } > /* now that we've added completion callbacks to all the threads, we can mark ready, which might fullfill the future, or will at least let the next callback fullfill the future*/ > mark_ready(); > return promise.get_future(); > } > > void notify() > { > std::lock_guard<std::mutex> scoped_lock{ lock }; > if (ready_flag && !completion_flag) { > promise.set_value(std::move(futures)); > } > completion_flag = true; > } > > void mark_ready() > { > std::lock_guard<std::mutex> scoped_lock{ lock }; > if (completion_flag) { > promise.set_value(std::move(futures)); > } > ready_flag = true; > } > > private: > hsh::promise<std::vector<FutureType>> promise; > std::vector<FutureType> futures; > std::mutex lock; > bool completion_flag = false; > bool ready_flag = false; > }; > > template<typename... Fs> > class future_when_any_variadic : public std::enable_shared_from_this<future_when_any_variadic<Fs...>> > { > using TUPLE_TYPE = std::tuple<decay_t<Fs>...>; > public: > future_when_any_variadic(Fs&&... fs) : futures(std::forward<Fs>(fs)...) {} > > hsh::future<TUPLE_TYPE> run() > { > /* add a continuation to every future in the tuple */ > hsh::for_each_in_tuple(continuation_helper{ this->shared_from_this() }, futures); > /* now that we've added completion callbacks to all the threads, we can mark ready, which might fullfill the future, or will at least let the next callback fullfill the future*/ > mark_ready(); > return promise.get_future(); > } > > void notify() > { > std::lock_guard<std::mutex> scoped_lock{ lock }; > if (ready_flag && !completion_flag) { > promise.set_value(std::move(futures)); > } > completion_flag = true; > } > > void mark_ready() > { > std::lock_guard<std::mutex> scoped_lock{ lock }; > if (completion_flag) { > promise.set_value(std::move(futures)); > } > ready_flag = true; > } > > private: > struct continuation_helper > { > explicit continuation_helper(std::shared_ptr<future_when_any_variadic>&& ptr) : ptr(std::move(ptr)) {} > > template<typename F> > void operator()(F&& future) > { > future.add_completion_callback(hsh::inline_executor{}, hsh::bind_front(¬ify, ptr)); > } > > std::shared_ptr<future_when_any_variadic> ptr; > }; > > hsh::promise<TUPLE_TYPE> promise; > TUPLE_TYPE futures; > std::mutex lock; > bool completion_flag = false; > bool ready_flag = false; > }; > > } > > /* ----- WHEN_ALL ---------- */ > template<typename Iterator, > typename F_TYPE = typename std::iterator_traits<Iterator>::value_type, enable_if_t<is_future<F_TYPE>::value>* = nullptr> > future<std::vector<F_TYPE>> when_all(Iterator begin, Iterator end) > { > if (begin == end) { > return make_ready_future<std::vector<F_TYPE>>(); > } > auto when_all_helper = std::make_shared<future_details::future_when_all_iterator<F_TYPE>>(begin, end); > return when_all_helper->run(); > } > > template<typename... Fs> > future<std::tuple<decay_t<Fs>...>> when_all(Fs&&... fs) > { > if (0 == sizeof...(fs)) { > /* empty parameter pack */ > return make_ready_future<std::tuple<decay_t<Fs>...>>(); > } > > auto when_all_helper = std::make_shared<future_details::future_when_all_variadic<Fs...>>(std::forward<Fs>(fs)...); > return when_all_helper->run(); > } > > > /* ----- WHEN_ANY ----------*/ > template<typename Iterator, > typename F_TYPE = typename std::iterator_traits<Iterator>::value_type, enable_if_t<is_future<F_TYPE>::value>* = nullptr> > future<std::vector<F_TYPE>> when_any(Iterator begin, Iterator end) > { > if (begin == end) { > return make_ready_future<std::vector<F_TYPE>>(); > } > auto when_any_helper = std::make_shared<future_details::future_when_any_iterator<F_TYPE>>(begin, end); > return when_any_helper->run(); > } > > template<typename... Fs> > future<std::tuple<decay_t<Fs>...>> when_any(Fs&&... fs) > { > if ( 0 == sizeof...(fs) ) { > /* empty parameter pack */ > return make_ready_future<std::tuple<decay_t<Fs>...>>(); > } > > auto when_any_helper = std::make_shared<future_details::future_when_any_variadic<Fs...>>(std::forward<Fs>(fs)...); > return when_any_helper->run(); > } > > using milliseconds = std::chrono::milliseconds; > using time_point = std::chrono::system_clock::time_point; > >class bad_variant_access : public std::logic_error > { > public: > bad_variant_access() : std::logic_error("hsh::bad_variant_access exception") {} > }; > inline void throw_bad_variant_access() { throw bad_variant_access{}; } > > template <class... Types> > class variant; > > template <class T> > struct variant_size; > > template <class T> > struct variant_size<const T> : std::integral_constant<size_t, variant_size<T>::value> {}; > > template <class T> > struct variant_size<volatile T> : std::integral_constant<size_t, variant_size<T>::value> {}; > > template <class T> > struct variant_size<const volatile T> : std::integral_constant<size_t, variant_size<T>::value> {}; > > template <class... Types> > struct variant_size<variant<Types...>> : std::integral_constant<size_t, sizeof...(Types)> {}; > > template <size_t I, class T> > struct variant_alternative; > > template <size_t I, class... Types> > struct variant_alternative<I, variant<Types...>> > { > static_assert(I < sizeof...(Types), "Index is out of range"); > using type = typename std::tuple_element<I, std::tuple<Types...>>::type; > }; > > template <size_t I, class T> > struct variant_alternative<I, const T> : add_const_t<variant_alternative<I, T>> {}; > > template <size_t I, class T> > struct variant_alternative<I, volatile T> : add_volatile_t<variant_alternative<I, T>>{}; > > template <size_t I, class T> > struct variant_alternative<I, const volatile T> : add_cv_t<variant_alternative<I,T>>{}; > > template <size_t I, class T> > using variant_alternative_t = typename variant_alternative<I, T>::type; > > constexpr size_t variant_npos = static_cast<size_t>(-1); > > /* forward declare helper functions */ > template <std::size_t I, typename... Ts> > inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept; > > template <typename T, typename... Ts> > inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept; > > namespace variant_detail > { > inline namespace type_traits > { > template <typename T> > struct is_in_place_index : std::false_type {}; > > template <std::size_t I> > struct is_in_place_index<in_place_index_t<I>> : std::true_type {}; > > template <typename T> > struct is_in_place_type : std::false_type {}; > > template <typename T> > struct is_in_place_type<in_place_type_t<T>> : std::true_type {}; > > > template <typename... Types> > struct implicit_convertible_operators; > > template <> > struct implicit_convertible_operators<> > { > void operator()() const; > }; > > template <typename T, typename... Types> > struct implicit_convertible_operators<T, Types...> : implicit_convertible_operators<Types...> > { > using implicit_convertible_operators<Types...>::operator(); > type_identity<T> operator()(T) const; > }; > > /* This Type Trait is used to detected which variant alternative should be constructed given a value of Type T */ > template <class T, class... Types> > using best_alternative_t = typename invoke_result_t<implicit_convertible_operators<Types...>, T>::type; > > /* Type Trait for getting type T from a matrix of static_arrays */ > template <typename T> > struct remove_all_static_array_extents : type_identity<T> {}; > > template <typename T, std::size_t N> > struct remove_all_static_array_extents<static_array<T, N>> : remove_all_static_array_extents<T> {}; > > template <typename T> > using remove_all_static_array_extents_t = typename remove_all_static_array_extents<T>::type; > > /* Type Trait for combining multiple integer sequences */ > template <typename Is, std::size_t J> > struct push_back; > > template <std::size_t... Is, std::size_t J> > struct push_back<index_sequence<Is...>, J> { > using type = index_sequence<Is..., J>; > }; > > template <typename Is, std::size_t J> > using push_back_t = typename push_back<Is, J>::type; > } > > > inline namespace find_index > { > constexpr std::size_t not_found = static_cast<std::size_t>(-1); > constexpr std::size_t ambiguous = static_cast<std::size_t>(-2); > > /* Base Recursize Case: return result, we've gone through all the types */ > inline constexpr std::size_t find_index_impl(std::size_t result, std::size_t) { return result; } > > /* Recursively iterate through the is_same_type trait parameter pack > - if the type doesn't match the current type, check the next type > - if the type does match the current type: > - if this is the first type that's matched keep interating until the end, in case there is another type that matches > - if we've already found another type that matches, return ambigous, since this variant won't support lookups by Type(has duplicate types in it) > */ > template <typename... Bools> > inline constexpr std::size_t find_index_impl(std::size_t result, std::size_t index, bool type_match, Bools... bools) > { > return type_match ? (result != not_found ? ambiguous : find_index_impl(index, index + 1, bools...)) : find_index_impl(result, index + 1, bools...); > } > > /* Helper that's used to find the index of a given type T, in a parameter pack */ > template <typename T, typename... Types> > inline constexpr std::size_t find_index() { return find_index_impl(not_found, 0, std::is_same<T, Types>::value...); } > > template <std::size_t I> > using find_index_sfinae_impl = enable_if_t<I != not_found && I != ambiguous, size_constant<I>>; > > template <typename T, typename... Ts> > using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>; > > template <std::size_t I> > struct find_index_checked_impl : size_constant<I> > { > static_assert(I != not_found, "the specified type is not found."); > static_assert(I != ambiguous, "the specified type is ambiguous."); > }; > > template <typename T, typename... Ts> > using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>; > > } //namespace find_index > > inline namespace access > { > template <std::size_t I> > struct get_alt_impl > { > template <typename V> > inline constexpr auto operator()(V &&v) const -> decltype(get_alt_impl<I - 1>{}(hsh::forward<V>(v).tail)) > { > return get_alt_impl<I - 1>{}(hsh::forward<V>(v).tail); > } > }; > > template<> > struct get_alt_impl<0> > { > template <typename V> > inline constexpr auto operator()(V &&v) const -> decltype((hsh::forward<V>(v).head)) > { > return hsh::forward<V>(v).head; > } > }; > > struct variant_getter > { > template <size_t I, typename V> > static constexpr auto get_alt(V&& v) -> decltype(get_alt_impl<I>{}(storage(hsh::forward<V>(v).impl))) > { > return get_alt_impl<I>{}(storage(hsh::forward<V>(v).impl)); > } > }; > > template <std::size_t I, typename V> > struct generic_get_impl > { > constexpr generic_get_impl(int) noexcept {} > > constexpr auto operator()(V &&v) const -> decltype(variant_getter::get_alt<I>(hsh::forward<V>(v))) > { > return variant_getter::get_alt<I>(hsh::forward<V>(v)); > } > }; > > template <std::size_t I, typename V> > inline constexpr auto generic_get(V &&v) > -> decltype(generic_get_impl<I, V>(holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(hsh::forward<V>(v))) > { > return generic_get_impl<I, V>(holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(hsh::forward<V>(v)); > } > } //namespace access > > inline namespace vistitation > { > /* Helper Functions for Recursively indexing into a function ptr matrix */ > template <typename T> > inline static constexpr const T& at(const T &elem) noexcept { return elem; } > > template <typename T, std::size_t N, typename... Is> > inline static constexpr const remove_all_static_array_extents_t<T>& at(const static_array<T, N> &elems, std::size_t i, Is... is) noexcept { return at(elems[i], is...); } > > /* Helper Function for creating an Array of Function Ptrs for a Parameter Pack of Functions */ > template <typename F, typename... Fs> > inline static constexpr static_array<decay_t<F>, sizeof...(Fs) + 1> make_farray(F &&f, Fs &&... fs) { return { {hsh::forward<F>(f), hsh::forward<Fs>(fs)...} }; } > > /* Helper function to determine what the return Type of a visitor should be */ > template <typename Visitor, typename... Vs> > using dispatch_result_t = decltype(hsh::invoke(std::declval<Visitor>(), get_alt_impl<0>{}(storage(std::declval<Vs>()))...)); > > template <typename F, typename... Vs> > struct make_fmatrix_impl > { > > template <std::size_t... Is> > inline static constexpr dispatch_result_t<F, Vs...> dispatch(F &&f, Vs &&... vs) > { > > static_assert(is_invocable<F&&, decltype(get_alt_impl<Is>{}(storage(hsh::forward<Vs>(vs))))... > ::value, > "All Variant values must be invocable by the visitor"); > static_assert(std::is_same<dispatch_result_t<F, Vs...>, > decltype(hsh::invoke(hsh::forward<F>(f), get_alt_impl<Is>{}(storage(hsh::forward<Vs>(vs)))...)) > ::value, > "All Vistor functions need to have the same return type"); > return hsh::invoke(hsh::forward<F>(f), get_alt_impl<Is>{}(storage(hsh::forward<Vs>(vs)))...); > } > > template <typename...> > struct impl; > > template <std::size_t... Is> > struct impl<index_sequence<Is...>> > { > inline constexpr auto operator()() const -> decltype(&dispatch<Is...>) > { > return &dispatch<Is...>; > } > }; > > template <typename Is, std::size_t... Js, typename... Ls> > struct impl<Is, index_sequence<Js...>, Ls...> > { > inline constexpr auto operator()() const -> decltype(make_farray(impl<push_back_t<Is, Js>, Ls...>{}()...)) > { > return make_farray(impl<push_back_t<Is, Js>, Ls...>{}()...); > } > }; > }; > > /* This function takes a vistor F, and a parameter pack of variants, and creates a Function Pointer array. */ > template <typename F, typename... Vs> > inline static constexpr auto make_fmatrix() -> > decltype(typename make_fmatrix_impl<F, Vs...>::template impl<index_sequence<>, make_index_sequence<decay_t<Vs>::size()>...>{}()) > { > return typename make_fmatrix_impl<F, Vs...>::template impl<index_sequence<>, make_index_sequence<decay_t<Vs>::size()>...>{}(); > } > > template <typename F, typename... Vs> > struct make_fdiagonal_impl > { > template <std::size_t I> > inline static constexpr dispatch_result_t<F, Vs...> dispatch(F &&f, Vs &&... vs) > { > static_assert(is_invocable < F&&, decltype(get_alt_impl<I>{}(storage(hsh::forward<Vs>(vs))))... > ::value, > "All Variant values must be invocable by the visitor"); > static_assert(std::is_same < dispatch_result_t<F, Vs...>, > decltype(hsh::invoke(hsh::forward<F>(f), get_alt_impl<I>{}(storage(hsh::forward<Vs>(vs)))...)) > ::value, > "All Vistor functions need to have the same return type"); > return hsh::invoke(hsh::forward<F>(f), get_alt_impl<I>{}(storage(hsh::forward<Vs>(vs)))...); > } > > > template <std::size_t... Is> > inline static constexpr auto impl(index_sequence<Is...>) -> decltype(make_farray(&dispatch<Is>...)) > { > return make_farray(&dispatch<Is>...); > } > }; > > template <typename F, typename... Vs> > struct make_indexed_fdiagonal_impl > { > template <std::size_t I> > inline static constexpr auto indexed_dispatch(F &&f, Vs &&... vs) -> decltype(hsh::invoke(hsh::forward<F>(f), size_constant<I>{}, get_alt_impl<I>{}(storage(hsh::forward<Vs>(vs)))...)) > { > static_assert(is_invocable < F&&, size_constant<I>, decltype(get_alt_impl<I>{}(storage(hsh::forward<Vs>(vs))))... > ::value, > "All Variant values must be invocable by the visitor"); > /*static_assert(std::is_same<dispatch_result_t<F, Vs...>, > decltype(hsh::invoke(hsh::forward<F>(f), size_constant<I>{}, get_alt_impl<I>{}(storage(hsh::forward<Vs>(vs)))...))>::value, > "All Vistor functions need to have the same return type"); */ > return hsh::invoke(hsh::forward<F>(f), size_constant<I>{}, get_alt_impl<I>{}(storage(hsh::forward<Vs>(vs)))...); > } > > template <std::size_t... Is> > inline static constexpr auto impl(index_sequence<Is...>) -> decltype(make_farray(&indexed_dispatch<Is>...)) > { > return make_farray(&indexed_dispatch<Is>...); > } > }; > > template <typename F, typename V, typename... Vs> > inline static constexpr auto make_fdiagonal() > -> decltype(make_fdiagonal_impl<F, V, Vs...>::impl(make_index_sequence<decay_t<V>::size()>{})) > { > static_assert(static_conjunction(decay_t<V>::size() == decay_t<Vs>::size()...), > "all of the variants must be the same size."); > return make_fdiagonal_impl<F, V, Vs...>::impl(make_index_sequence<decay_t<V>::size()>{}); > } > > template <typename F, typename V, typename... Vs> > inline static constexpr auto make_indexed_fdiagonal() > -> decltype(make_indexed_fdiagonal_impl<F, V, Vs...>::impl(make_index_sequence<decay_t<V>::size()>{})) > { > static_assert(static_conjunction(decay_t<V>::size() == decay_t<Vs>::size()...), > "all of the variants must be the same size."); > return make_indexed_fdiagonal_impl<F, V, Vs...>::impl(make_index_sequence<decay_t<V>::size()>{}); > } > > template <typename Visitor, typename... Vs> > inline constexpr auto visit_base(Visitor&& visitor, Vs&&... vs) > -> decltype(at(make_fmatrix<Visitor&&, decltype(hsh::forward<Vs>(vs))...>(), vs.index()...)(hsh::forward<Visitor>(visitor), hsh::forward<Vs>(vs)...)) > { > return at(make_fmatrix<Visitor&&, decltype(hsh::forward<Vs>(vs))...>(), vs.index()...)(hsh::forward<Visitor>(visitor), hsh::forward<Vs>(vs)...); > } > > template <typename Visitor, typename... Vs> > inline constexpr auto visit_at_base(size_t index, Visitor&& visitor, Vs&&... vs) > -> decltype(at(make_fdiagonal<Visitor&&, decltype(hsh::forward<Vs>(vs))...>(), index)(hsh::forward<Visitor>(visitor), hsh::forward<Vs>(vs)...)) > { > return at(make_fdiagonal<Visitor&&, decltype(hsh::forward<Vs>(vs))...>(), index)(hsh::forward<Visitor>(visitor), hsh::forward<Vs>(vs)...); > } > > template <typename Visitor, typename... Vs> > inline constexpr auto visit_at_indexed(size_t index, Visitor&& visitor, Vs&&... vs) > -> decltype(at(make_indexed_fdiagonal<Visitor&&, decltype(hsh::forward<Vs>(vs))...>(), index)(hsh::forward<Visitor>(visitor), hsh::forward<Vs>(vs)...)) > { > return at(make_indexed_fdiagonal<Visitor&&, decltype(hsh::forward<Vs>(vs))...>(), index)(hsh::forward<Visitor>(visitor), hsh::forward<Vs>(vs)...); > } > > struct variant_visitor > { > template <typename Visitor, typename... Vs> > constexpr static auto visit(Visitor&& visitor, Vs&&... vs) > -> decltype(visit_base(hsh::forward<Visitor>(visitor), as_base(hsh::forward<Vs>(vs).impl)...)) > { > return visit_base(hsh::forward<Visitor>(visitor), as_base(hsh::forward<Vs>(vs).impl)...); > } > > template <typename Visitor, typename... Vs> > constexpr static auto visit_at(size_t index, Visitor&& visitor, Vs&&... vs) > -> decltype(visit_at_base(index, hsh::forward<Visitor>(visitor), as_base(hsh::forward<Vs>(vs).impl)...)) > { > return visit_at_base(index, hsh::forward<Visitor>(visitor), as_base(hsh::forward<Vs>(vs).impl)...); > } > }; > } //namespace vistitation > > inline namespace visitor_comparators > { > struct less_visitor > { > template <typename LHS, typename RHS> > inline constexpr bool operator()(LHS &&lhs, RHS &&rhs) const noexcept(noexcept(hsh::forward<LHS>(lhs) < hsh::forward<RHS>(rhs))) > { > return hsh::forward<LHS>(lhs) < hsh::forward<RHS>(rhs); > } > }; > > struct less_equal_visitor > { > template <typename LHS, typename RHS> > inline constexpr bool operator()(LHS &&lhs, RHS &&rhs) const noexcept(noexcept(hsh::forward<LHS>(lhs) <= hsh::forward<RHS>(rhs))) > { > return hsh::forward<LHS>(lhs) <= hsh::forward<RHS>(rhs); > } > }; > > struct greater_visitor > { > template <typename LHS, typename RHS> > inline constexpr bool operator()(LHS &&lhs, RHS &&rhs) const noexcept(noexcept(hsh::forward<LHS>(lhs) > hsh::forward<RHS>(rhs))) > { > return hsh::forward<LHS>(lhs) > hsh::forward<RHS>(rhs); > } > }; > > struct greater_equal_visitor > { > template <typename LHS, typename RHS> > inline constexpr bool operator()(LHS &&lhs, RHS &&rhs) const noexcept(noexcept(hsh::forward<LHS>(lhs) >= hsh::forward<RHS>(rhs))) > { > return hsh::forward<LHS>(lhs) >= hsh::forward<RHS>(rhs); > } > }; > > struct equal_visitor > { > template <typename LHS, typename RHS> > inline constexpr bool operator()(LHS &&lhs, RHS &&rhs) const noexcept(noexcept(hsh::forward<LHS>(lhs) == hsh::forward<RHS>(rhs))) > { > return hsh::forward<LHS>(lhs) == hsh::forward<RHS>(rhs); > } > }; > > struct not_equal_visitor > { > template <typename LHS, typename RHS> > inline constexpr bool operator()(LHS &&lhs, RHS &&rhs) const noexcept(noexcept(hsh::forward<LHS>(lhs) != hsh::forward<RHS>(rhs))) > { > return hsh::forward<LHS>(lhs) != hsh::forward<RHS>(rhs); > } > }; > } //namspace visitor_comparators > > inline namespace variant_data > { > struct valueless_t {}; > > template <bool TrivialDestructable, size_t Index, class... Types> > class variant_storage {}; > > template <class... Types> > using variant_storage_t = variant_storage<conjunction<is_trivially_destructible<Types>...>::value, 0, Types...>; > > /* Storage for trivally destrucible variant types */ > template <size_t Index, class T, class... Ts> > class variant_storage<true, Index, T, Ts...> > { > public: > explicit constexpr variant_storage(valueless_t tag) noexcept : valueless{} {} > > template <class... Args> > explicit constexpr variant_storage(in_place_index_t<0>, Args&&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value) > : head(hsh::forward<Args>(args)...) {} > > template <size_t I, class... Args> > explicit constexpr variant_storage(in_place_index_t<I>, Args&&... args) > : tail{ in_place_index_t<I - 1>{}, hsh::forward<Args>(args)... } {} > > variant_storage(const variant_storage&) = default; > variant_storage(variant_storage&&) = default; > > ~variant_storage() noexcept = default; > > variant_storage& operator=(const variant_storage&) = default; > variant_storage& operator=(variant_storage&&) = default; > > union > { > T head; > variant_storage<true, Index + 1, Ts...> tail; > char valueless; > }; > }; > > /* Storage for non-trivally destrucible variant types */ > template <size_t Index, class T, class... Ts> > class variant_storage<false, Index, T, Ts...> > { > public: > explicit constexpr variant_storage(valueless_t tag) noexcept : valueless{} {} > > template <class... Args> > constexpr explicit variant_storage(in_place_index_t<0>, Args&&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value) > : head(hsh::forward<Args>(args)...) {} // initialize _Head with _Args... > > template <size_t I, class... Args, enable_if_t<(I > 0), int> = 0> > constexpr explicit variant_storage(in_place_index_t<I>, Args&&... args) > noexcept(std::is_nothrow_constructible<variant_storage_t<Ts...>, std::integral_constant<size_t, I - 1>, Args...>::value) > : tail(in_place_index<I - 1>, hsh::forward<Args>(args)...) {} > > variant_storage(variant_storage&&) = default; > variant_storage(const variant_storage&) = default; > > /* explicitly non-trivial destructor (which would otherwise be defined as deleted since the class has a variant member with a non-trivial destructor) */ > ~variant_storage() noexcept { } > > variant_storage& operator=(variant_storage&&) = default; > variant_storage& operator=(const variant_storage&) = default; > > union > { > T head; > variant_storage<false, Index + 1, Ts...> tail; > char valueless; > }; > }; > > /* Base Class that handles most of the basic logic of variant */ > template <class... Types> > class variant_impl_base > { > using storage_t = variant_storage_t<Types...>; > public: > explicit constexpr variant_impl_base(valueless_t valueless_tag) noexcept : data(valueless_tag), _index(invalid_index) {} > > template <size_t I, class... Args, enable_if_t<std::is_constructible<variant_alternative_t<I, variant<Types...>>, Args...>::value, int> = 0> > constexpr explicit variant_impl_base(in_place_index_t<I>, Args&&... args) noexcept(std::is_nothrow_constructible<variant_alternative_t<I, variant<Types...>>, Args...>::value) > : data(in_place_index<I>, hsh::forward<Args>(args)...), _index{ static_cast<uint8_t>(I) } {} > > constexpr bool valueless_by_exception() const noexcept { return invalid_index == _index; } > > constexpr size_t index() const noexcept { return valueless_by_exception() ? variant_npos : _index; } > > void set_index(const size_t index) noexcept { _index = static_cast<uint8_t>(index); } > > static constexpr size_t size() { return num_alternatives; } > > static constexpr size_t num_alternatives = sizeof...(Types); > static constexpr uint8_t invalid_index = static_cast<uint8_t>(-1); > > friend inline constexpr storage_t& storage(variant_impl_base& base) { return base.data; } > friend inline constexpr const storage_t& storage(const variant_impl_base& base) { return base.data; } > friend inline constexpr storage_t&& storage(variant_impl_base&& base) { return hsh::move(base).data; } > friend inline constexpr const storage_t&& storage(const variant_impl_base&& base) { return hsh::move(base).data; } > > friend inline constexpr variant_impl_base& as_base(variant_impl_base& base) { return base; } > friend inline constexpr const variant_impl_base& as_base(const variant_impl_base& base) { return base; } > friend inline constexpr variant_impl_base&& as_base(variant_impl_base&& base) { return hsh::move(base); } > friend inline constexpr const variant_impl_base&& as_base(const variant_impl_base&& base) { return hsh::move(base); } > > storage_t data; > uint8_t _index = invalid_index; > }; > > // trivial destructor case > template <bool IsTrivallyDestructible, class... Types> > class variant_impl_destructor : public variant_impl_base<Types...> > { > using super = variant_impl_base<Types...>; > public: > using super::super; > variant_impl_destructor(variant_impl_destructor&&) = default; > variant_impl_destructor(const variant_impl_destructor&) = default; > > ~variant_impl_destructor() = default; > > using super::operator=; > variant_impl_destructor& operator=(variant_impl_destructor&&) = default; > variant_impl_destructor& operator=(const variant_impl_destructor&) = default; > > void destroy() { this->_index = super::invalid_index; } > }; > > template <class... Types> > class variant_impl_destructor<false, Types...> : public variant_impl_base<Types...> > { > using super = variant_impl_base<Types...>; > public: > using super::super; > variant_impl_destructor(variant_impl_destructor&&) = default; > variant_impl_destructor(const variant_impl_destructor&) = default; > > ~variant_impl_destructor() { destroy(); } > > using super::operator=; > variant_impl_destructor& operator=(variant_impl_destructor&&) = default; > variant_impl_destructor& operator=(const variant_impl_destructor&) = default; > > void destroy() > { > if ( !this->valueless_by_exception() ) > { > visit_base(destructor_visitor{}, *this); > } > this->_index = super::invalid_index; > } > > private: > /* visitation helper for destruction, if we had generic lambdas this wouldn't be needed */ > struct destructor_visitor > { > template <typename T> > inline void operator()(T &t) const noexcept { t.~T(); } > }; > }; > > template <class... Types> > class variant_impl_constructor : public variant_impl_destructor<conjunction<is_trivially_destructible<Types>...>::value, Types...> > { > using super = variant_impl_destructor<conjunction<is_trivially_destructible<Types>...>::value, Types...>; > public: > using super::super; > using super::operator=; > > protected: > template <class T, class... Args> > static T& construct_alt(T& alt, Args&&... args) > { > return *new((void*)&alt)T{ hsh::forward<Args>(args)...}; > } > > template <class Rhs> > static void generic_construct(variant_impl_constructor& lhs, Rhs&& rhs) > { > lhs.destroy(); > if (!rhs.valueless_by_exception()) > { > > visit_at_base(rhs.index(), construct_visitor{}, lhs, hsh::forward<Rhs>(rhs)); > lhs._index = rhs._index; > } > } > > private: > /* visitation helper for construction, if we had generic lambdas this wouldn't be needed */ > struct construct_visitor > { > template <typename T, typename U> > inline void operator()(T &lhs, U &&rhs) const > { > variant_impl_constructor::construct_alt(lhs, hsh::forward<U>(rhs)); > } > }; > }; > > /* trivial copy constructor case */ > template <operation_support_trait, class... Types> > class variant_impl_copy_constructor : public variant_impl_constructor<Types...> > { > using super = variant_impl_constructor<Types...>; > public: > using super::super; > variant_impl_copy_constructor(const variant_impl_copy_constructor&) = default; > variant_impl_copy_constructor(variant_impl_copy_constructor&&) = default; > > ~variant_impl_copy_constructor() = default; > > using super::operator=; > variant_impl_copy_constructor& operator=(variant_impl_copy_constructor&&) = default; > variant_impl_copy_constructor& operator=(const variant_impl_copy_constructor&) = default; > }; > > // non-trivial copy constructor case > template <class... Types> > class variant_impl_copy_constructor<operation_support_trait::available, Types...> : public variant_impl_constructor<Types...> > { > using super = variant_impl_constructor<Types...>; > public: > using super::super; > > variant_impl_copy_constructor(const variant_impl_copy_constructor& other) noexcept(conjunction<std::is_nothrow_copy_constructible<Types>...>::value) > : variant_impl_copy_constructor(valueless_t{}) > { > this->generic_construct(*this, other); > } > > variant_impl_copy_constructor(variant_impl_copy_constructor&&) = default; > > ~variant_impl_copy_constructor() = default; > > using super::operator=; > variant_impl_copy_constructor& operator=(variant_impl_copy_constructor&&) = default; > variant_impl_copy_constructor& operator=(const variant_impl_copy_constructor&) = default; > }; > > // deleted copy constructor case > template <class... Types> > class variant_impl_copy_constructor<operation_support_trait::unavailable, Types...> : public variant_impl_constructor<Types...> > { > using super = variant_impl_constructor<Types...>; > public: > using super::super; > variant_impl_copy_constructor(const variant_impl_copy_constructor&) = delete; > variant_impl_copy_constructor(variant_impl_copy_constructor&&) = default; > > ~variant_impl_copy_constructor() = default; > > using super::operator=; > variant_impl_copy_constructor& operator=(variant_impl_copy_constructor&&) = default; > variant_impl_copy_constructor& operator=(const variant_impl_copy_constructor&) = default; > }; > > // trivial move constructor case > template <operation_support_trait, class... Types> > class variant_impl_move_constructor : public variant_impl_copy_constructor<copy_constructor_traits<Types...>::traits, Types...> > { > using super = variant_impl_copy_constructor<copy_constructor_traits<Types...>::traits, Types...>; > public: > using super::super; > variant_impl_move_constructor(const variant_impl_move_constructor&) = default; > variant_impl_move_constructor(variant_impl_move_constructor&&) = default; > > ~variant_impl_move_constructor() = default; > > using super::operator=; > variant_impl_move_constructor& operator=(const variant_impl_move_constructor&) = default; > variant_impl_move_constructor& operator=(variant_impl_move_constructor&&) = default; > }; > > // non-trivial move constructor case > template <class... Types> > class variant_impl_move_constructor<operation_support_trait::available, Types...> : public variant_impl_copy_constructor<copy_constructor_traits<Types...>::traits, Types...> > { > using super = variant_impl_copy_constructor<copy_constructor_traits<Types...>::traits, Types...>; > public: > using super::super; > variant_impl_move_constructor(const variant_impl_move_constructor&) = default; > variant_impl_move_constructor(variant_impl_move_constructor&& other) noexcept(conjunction<std::is_nothrow_move_constructible<Types>...>::value) > : variant_impl_move_constructor(valueless_t{}) > { > this->generic_construct(*this, hsh::move(other)); > } > > ~variant_impl_move_constructor() = default; > > using super::operator=; > variant_impl_move_constructor& operator=(const variant_impl_move_constructor&) = default; > variant_impl_move_constructor& operator=(variant_impl_move_constructor&&) = default; > }; > > // deleted move constructor case > template <class... Types> > class variant_impl_move_constructor<operation_support_trait::unavailable, Types...> : public variant_impl_copy_constructor<copy_constructor_traits<Types...>::traits, Types...> > { > using super = variant_impl_copy_constructor<copy_constructor_traits<Types...>::traits, Types...>; > public: > using super::super; > variant_impl_move_constructor(const variant_impl_move_constructor&) = default; > variant_impl_move_constructor(variant_impl_move_constructor&&) = delete; > > ~variant_impl_move_constructor() = default; > > using super::operator=; > variant_impl_move_constructor& operator=(const variant_impl_move_constructor&) = default; > variant_impl_move_constructor& operator=(variant_impl_move_constructor&&) = default; > }; > > template <class... Types> > class variant_impl_assignment : public variant_impl_move_constructor<move_constructor_traits<Types...>::traits, Types...> > { > using super = variant_impl_move_constructor<move_constructor_traits<Types...>::traits, Types...>; > public: > using super::super; > using super::operator=; > > template <size_t I, class... Args> > auto emplace(Args&&... args) -> decltype(this->construct_alt(get_alt_impl<I>{}(storage(*this)), std::forward<Args>(args)...)) > { > this->destroy(); > auto& result_alternative = this->construct_alt(get_alt_impl<I>{}(storage(*this)), std::forward<Args>(args)...); > this->_index = I; > return result_alternative; > } > > protected: > template <size_t I, class T, class Arg> > void assign_alt(size_constant<I>, T& alt, Arg&& arg) > { > // If the alternative being assigned is the same index, > // the assignment operator of the union element > if ( this->index() == I ) > { > alt = hsh::forward<Arg>(arg); > } > else > { > emplace_alt<I, T>(hsh::forward<Arg>(arg)); > } > } > > template <size_t I, class T, class Arg, enable_if_t<disjunction<std::is_nothrow_constructible<T, Arg>, negation<std::is_nothrow_move_constructible<T>>>::value>* = nullptr> > void emplace_alt(Arg&& arg) > { > this->emplace<I>(hsh::forward<Arg>(arg)); > } > > template <size_t I, class T, class Arg, enable_if_t<negation<disjunction<std::is_nothrow_constructible<T, Arg>, negation<std::is_nothrow_move_constructible<T>>>>::value>* = nullptr> > void emplace_alt(Arg&& arg) > { > this->emplace<I>(T(hsh::forward<Arg>(arg))); > } > > template <typename OtherVariant> > inline void generic_assign(OtherVariant &&other) > { > if (this->valueless_by_exception() && other.valueless_by_exception()) > { > // do nothing. > } > else if (other.valueless_by_exception()) > { > this->destroy(); > } > else > { > visit_at_indexed(other.index(), assign_visitor<OtherVariant>{this}, *this, hsh::forward<OtherVariant>(other)); > } > } > > private: > /* visitation helper for assignment, if we had generic lambdas this wouldn't be needed */ > template <typename That> > struct assign_visitor > { > template <size_t I, typename T, typename U> > inline void operator()(size_constant<I>, T &this_alt, U &&other) const > { > self->assign_alt(size_constant<I>{}, this_alt, hsh::forward<U>(other)); > } > variant_impl_assignment *self; > }; > }; > > // trivial copy assignment case > template <operation_support_trait, class... Types> > class variant_impl_copy_assignment : public variant_impl_assignment<Types...> > { > using super = variant_impl_assignment<Types...>; > public: > using super::super; > variant_impl_copy_assignment(const variant_impl_copy_assignment&) = default; > variant_impl_copy_assignment(variant_impl_copy_assignment&&) = default; > > ~variant_impl_copy_assignment() = default; > > using super::operator=; > variant_impl_copy_assignment& operator=(const variant_impl_copy_assignment&) = default; > variant_impl_copy_assignment& operator=(variant_impl_copy_assignment&&) = default; > }; > > // non-trivial copy assignment case > template <class... Types> > class variant_impl_copy_assignment<operation_support_trait::available, Types...> : public variant_impl_assignment<Types...> > { > using super = variant_impl_assignment<Types...>; > public: > using super::super; > variant_impl_copy_assignment(const variant_impl_copy_assignment&) = default; > variant_impl_copy_assignment(variant_impl_copy_assignment&&) = default; > > ~variant_impl_copy_assignment() = default; > > using super::operator=; > variant_impl_copy_assignment& operator=(const variant_impl_copy_assignment& rhs) noexcept(conjunction<conjunction<std::is_nothrow_copy_assignable<Types>,std::is_nothrow_copy_constructible<Types>>...>::value) > { > this->generic_assign(rhs); > return *this; > } > variant_impl_copy_assignment& operator=(variant_impl_copy_assignment&&) = default; > }; > > // deleted copy assignment case > template <class... Types> > class variant_impl_copy_assignment<operation_support_trait::unavailable, Types...> : public variant_impl_assignment<Types...> > { > using super = variant_impl_assignment<Types...>; > public: > using super::super; > variant_impl_copy_assignment(const variant_impl_copy_assignment&) = default; > variant_impl_copy_assignment(variant_impl_copy_assignment&&) = default; > > ~variant_impl_copy_assignment() = default; > > using super::operator=; > variant_impl_copy_assignment& operator=(const variant_impl_copy_assignment&) = delete; > variant_impl_copy_assignment& operator=(variant_impl_copy_assignment&&) = default; > }; > > // trivial move assignment case > template <operation_support_trait, class... Types> > class variant_impl_move_assignment : public variant_impl_copy_assignment<copy_assignment_traits<Types...>::traits, Types...> > { > using super = variant_impl_copy_assignment<copy_assignment_traits<Types...>::traits, Types...>; > public: > using super::super; > variant_impl_move_assignment(const variant_impl_move_assignment&) = default; > variant_impl_move_assignment(variant_impl_move_assignment&&) = default; > > ~variant_impl_move_assignment() = default; > > using super::operator=; > variant_impl_move_assignment& operator=(const variant_impl_move_assignment&) = default; > variant_impl_move_assignment& operator=(variant_impl_move_assignment&&) = default; > }; > > // non-trivial move assignment case > template <class... Types> > class variant_impl_move_assignment<operation_support_trait::available, Types...> : public variant_impl_copy_assignment<copy_assignment_traits<Types...>::traits, Types...> > { > using super = variant_impl_copy_assignment<copy_assignment_traits<Types...>::traits, Types...>; > public: > using super::super; > variant_impl_move_assignment(const variant_impl_move_assignment&) = default; > variant_impl_move_assignment(variant_impl_move_assignment&&) = default; > > ~variant_impl_move_assignment() = default; > > using super::operator=; > variant_impl_move_assignment& operator=(const variant_impl_move_assignment&) = default; > variant_impl_move_assignment& operator=(variant_impl_move_assignment&& rhs) noexcept(conjunction<conjunction<std::is_nothrow_move_assignable<Types>, std::is_nothrow_move_constructible<Types>>...>::value) > { > this->generic_assign( hsh::move(rhs) ); > return *this; > } > }; > > // deleted move assignment case > template <class... Types> > class variant_impl_move_assignment<operation_support_trait::unavailable, Types...> : public variant_impl_copy_assignment<copy_assignment_traits<Types...>::traits, Types...> > { > using super = variant_impl_copy_assignment<copy_assignment_traits<Types...>::traits, Types...>; > public: > using super::super; > variant_impl_move_assignment(const variant_impl_move_assignment&) = default; > variant_impl_move_assignment(variant_impl_move_assignment&&) = default; > > ~variant_impl_move_assignment() = default; > > using super::operator=; > variant_impl_move_assignment& operator=(const variant_impl_move_assignment&) = default; > variant_impl_move_assignment& operator=(variant_impl_move_assignment&&) = delete; > }; > > template <class... Types> > class variant_impl : public variant_impl_move_assignment<move_assignment_traits<Types...>::traits, Types...> > { > using super = variant_impl_move_assignment<move_assignment_traits<Types...>::traits, Types...>; > public: > using super::super; > variant_impl(const variant_impl&) = default; > variant_impl(variant_impl&&) = default; > > ~variant_impl() = default; > > using super::operator=; > variant_impl &operator=(const variant_impl &) = default; > variant_impl &operator=(variant_impl &&) = default; > > template <std::size_t I, typename Arg> > inline void assign(Arg &&arg) > { > this->assign_alt(size_constant<I>{}, get_alt_impl<I>{}(storage(*this)), hsh::forward<Arg>(arg)); > } > > inline constexpr bool move_nothrow() const > { > return this->valueless_by_exception() || > static_array<bool, sizeof...(Types)>{{std::is_nothrow_move_constructible<Types>::value...}}[this->index()]; > } > > inline void swap(variant_impl& other) > { > if (this->valueless_by_exception() && other.valueless_by_exception()) { > //do nothing > } > else if (this->index() == other.index()) > { > /* same Type is set in both, just visit and call the approite swap */ > visit_at_base(this->index(), swap_visitor{}, *this, other); > } > else > { > variant_impl *lhs = this; > variant_impl *rhs = hsh::addressof(other); > > /* We have to create a temporary, and we want to use a nothrow movable type if possible */ > if (lhs->move_nothrow() && !rhs->move_nothrow()) > { > //swap the pointer so we create a temp, from the non-throwing type > std::swap(lhs, rhs); > } > /* create temp from RHS */ > variant_impl tmp(hsh::move(*rhs)); > try > { > /* Move lhs into RHS */ > this->generic_construct(*rhs, hsh::move(*lhs)); > } > catch (...) > { > if (tmp.move_nothrow()) { > this->generic_construct(*rhs, hsh::move(tmp)); > } > throw; > } > /* Move the temp into LHS */ > this->generic_construct(*lhs, hsh::move(tmp)); > } > } > > private: > struct swap_visitor > { > template <typename ThisAlt, typename ThatAlt> > inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const { > using std::swap; > swap(this_alt, that_alt); > } > }; > }; > > > } //namespace variant_data > > > } //namespace variant_detail > > template <class... Types> > class variant > { > static_assert( sizeof...(Types) < std::numeric_limits<uint8_t>::max(), "Too many alternatives"); > static_assert( sizeof...(Types) > 0, "variant cannot be empty"); > static_assert( negation<disjunction<std::is_void<Types>...>>::value, "variant does not allow void"); > static_assert( negation<disjunction<std::is_reference<Types>...>>::value, "variant does not allow references"); > static_assert( negation<disjunction<std::is_array<Types>...>>::value, "variant does not allow arrays"); > > public: > /* Default Constructor */ > template <typename Front = variant_alternative_t<0, variant<Types...>>, enable_if_t<std::is_default_constructible<Front>::value, int> = 0> > constexpr variant() noexcept(std::is_nothrow_default_constructible<Front>::value) > : impl(in_place_index<0>) {} > > /* Converting Constructor */ > template < > typename Arg, > typename Decayed = decay_t<Arg>, > enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0, > enable_if_t<!variant_detail::is_in_place_index<Decayed>::value, int> = 0, > enable_if_t<!variant_detail::is_in_place_type<Decayed>::value, int> = 0, > typename T = variant_detail::best_alternative_t<Arg, Types...>, > std::size_t I = variant_detail::find_index_sfinae<T, Types...>::value, > enable_if_t<std::is_constructible<T, Arg>::value, int> = 0> > constexpr variant(Arg &&arg) noexcept(std::is_nothrow_constructible<T, Arg>::value) > : impl(in_place_index<I>, hsh::forward<Arg>(arg)) {} > > /* In Place by Index Constructor */ > template < > std::size_t I, > typename... Args, > typename T = variant_alternative_t<I, variant<Types...>>, > enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> > explicit constexpr variant(in_place_index_t<I>, Args &&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value) > : impl(in_place_index_t<I>{}, hsh::forward<Args>(args)...) {} > > /* In Place by Index Constructor (initializer list) */ > template < > std::size_t I, > typename U, > typename... Args, > typename T = variant_alternative_t<I, variant<Types...>>, > enable_if_t<std::is_constructible<T, std::initializer_list<U> &, Args...>::value, int> = 0> > inline explicit constexpr variant(in_place_index_t<I>, std::initializer_list<U> il, Args &&... args) > noexcept(std::is_nothrow_constructible<T, std::initializer_list<U> &, Args...>::value) > : impl(in_place_index_t<I>{}, il, hsh::forward<Args>(args)...) {} > > /* In Place by Type Constructor */ > template < > typename T, > typename... Args, > std::size_t I = variant_detail::find_index_sfinae<T, Types...>::value, > enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> > inline explicit constexpr variant(in_place_type_t<T>, Args &&... args) noexcept(std::is_nothrow_constructible<T, Args...>::value) > : impl(in_place_index_t<I>{}, hsh::forward<Args>(args)...) {} > > /* In Place by Type Constructor (initializer list) */ > template < > typename T, > typename U, > typename... Args, > std::size_t I = variant_detail::find_index_sfinae<T, Types...>::value, > enable_if_t<std::is_constructible<T, std::initializer_list<U> &, Args...>::value, int> = 0> > inline explicit constexpr variant(in_place_type_t<T>, std::initializer_list<U> il, Args &&... args) > noexcept(std::is_nothrow_constructible<T, std::initializer_list<U>&, Args...>::value) > : impl(in_place_index_t<I>{}, il, hsh::forward<Args>(args)...) {} > > /* Copy and Move Constructor */ > variant(const variant &) = default; > variant(variant &&) = default; > > ~variant() = default; > > /* Copy and Move Assignment Operator */ > variant &operator=(const variant &) = default; > variant &operator=(variant &&) = default; > > template <typename Arg, > enable_if_t<!std::is_same<decay_t<Arg>, variant>::value, int> = 0, > typename T = variant_detail::best_alternative_t<Arg, Types...>, > std::size_t I = variant_detail::find_index_sfinae<T, Types...>::value, > enable_if_t<(std::is_assignable<T &, Arg>::value && std::is_constructible<T, Arg>::value), int> = 0> > inline variant &operator=(Arg &&arg) noexcept(conjunction<std::is_nothrow_assignable<T&, Arg>, std::is_nothrow_constructible<T, Arg>>::value) > { > impl.template assign<I>(hsh::forward<Arg>(arg)); > return *this; > } > > template <std::size_t I, > typename... Args, > typename T = variant_alternative_t<I, variant<Types...>>, > enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> > inline T &emplace(Args&&... args) { > return impl.template emplace<I>(hsh::forward<Args>(args)...); > } > > template < > std::size_t I, > typename U, > typename... Args, > typename T = variant_alternative_t<I, variant<Types...>>, > enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args...>::value, int> = 0> > inline T &emplace(std::initializer_list<U> il, Args &&... args) { > return impl.template emplace<I>(il, hsh::forward<Args>(args)...); > } > > template < > typename T, > typename... Args, > std::size_t I = variant_detail::find_index_sfinae<T, Types...>::value, > enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> > inline T &emplace(Args &&... args) { > return impl.template emplace<I>(hsh::forward<Args>(args)...); > } > > template < > typename T, > typename U, > typename... Args, > std::size_t I = variant_detail::find_index_sfinae<T, Types...>::value, > enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args...>::value, int> = 0> > inline T &emplace(std::initializer_list<U> il, Args &&... args) > { > return impl.template emplace<I>(il, hsh::forward<Args>(args)...); > } > > constexpr size_t index() const noexcept { return impl.index(); } > > constexpr bool valueless_by_exception() const noexcept { return impl.valueless_by_exception(); } > > template <bool Placeholder = true, > enable_if_t<conjunction<bool_constant<Placeholder && is_swappable<Types>::value && std::is_move_constructible<Types>::value>...>::value, bool> = false> > inline void swap(variant &that) noexcept(conjunction<conjunction<is_nothrow_swappable<Types>, std::is_nothrow_move_constructible<Types>>...>::value) > { > impl.swap(that.impl); > } > > private: > friend variant_detail::variant_getter; > friend variant_detail::variant_visitor; > variant_detail::variant_impl<Types...> impl; > }; > > template <class... Types> > void swap(variant<Types...>& lhs, variant<Types...>& rhs) noexcept(noexcept(lhs.swap(rhs))) > { > lhs.swap(rhs); > } > > /* Comparision Operators */ > template <typename... Ts> > inline constexpr bool operator==(const variant<Ts...> &lhs, const variant<Ts...> &rhs) > { > return lhs.index() == rhs.index() && > (lhs.valueless_by_exception() || > variant_detail::variant_visitor::visit_at(lhs.index(), variant_detail::equal_visitor{}, lhs, rhs)); > } > > template <typename... Ts> > inline constexpr bool operator!=(const variant<Ts...> &lhs, const variant<Ts...> &rhs) > { > return lhs.index() != rhs.index() || > (!lhs.valueless_by_exception() && > variant_detail::variant_visitor::visit_at(lhs.index(), variant_detail::not_equal_visitor{}, lhs, rhs)); > } > > template <typename... Ts> > inline constexpr bool operator<(const variant<Ts...> &lhs, const variant<Ts...> &rhs) > { > return !rhs.valueless_by_exception() && > (lhs.valueless_by_exception() || lhs.index() < rhs.index() || > (lhs.index() == rhs.index() && variant_detail::variant_visitor::visit_at(lhs.index(), variant_detail::less_visitor{}, lhs, rhs))); > } > > template <typename... Ts> > inline constexpr bool operator<=(const variant<Ts...> &lhs, const variant<Ts...> &rhs) > { > return lhs.valueless_by_exception() || (!rhs.valueless_by_exception() && > (lhs.index() < rhs.index() || (lhs.index() == rhs.index() && > variant_detail::variant_visitor::visit_at(lhs.index(), variant_detail::less_equal_visitor{}, lhs, rhs)))); > } > > template <typename... Ts> > inline constexpr bool operator>(const variant<Ts...> &lhs, const variant<Ts...> &rhs) > { > > return !lhs.valueless_by_exception() && > (rhs.valueless_by_exception() || lhs.index() > rhs.index() || > (lhs.index() == rhs.index() && variant_detail::variant_visitor::visit_at(lhs.index(), variant_detail::greater_visitor{}, lhs, rhs))); > } > > template <typename... Ts> > inline constexpr bool operator>=(const variant<Ts...> &lhs, const variant<Ts...> &rhs) > { > return rhs.valueless_by_exception() || > (!lhs.valueless_by_exception() && > (lhs.index() > rhs.index() || > (lhs.index() == rhs.index() && > variant_detail::variant_visitor::visit_at(lhs.index(), variant_detail::greater_equal_visitor{}, lhs, rhs)))); > } > > template <std::size_t I, typename... Ts> > inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept { return v.index() == I; } > > template <typename T, typename... Ts> > inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept { > return holds_alternative<variant_detail::find_index_checked<T, Ts...>::value>(v); > } > > template <std::size_t I, typename... Ts> > inline constexpr variant_alternative_t<I, variant<Ts...>>& get(variant<Ts...> &v) > { > return variant_detail::generic_get<I>(v); > } > > template <std::size_t I, typename... Ts> > inline constexpr variant_alternative_t<I, variant<Ts...>>&& get(variant<Ts...> &&v) > { > return variant_detail::generic_get<I>(hsh::move(v)); > } > > template <std::size_t I, typename... Ts> > inline constexpr const variant_alternative_t<I, variant<Ts...>>& get(const variant<Ts...> &v) > { > return variant_detail::generic_get<I>(v); > } > > template <std::size_t I, typename... Ts> > inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(const variant<Ts...> &&v) > { > return variant_detail::generic_get<I>(hsh::move(v)); > } > > template <typename T, typename... Ts> > inline constexpr T &get(variant<Ts...> &v) > { > return hsh::get<variant_detail::find_index_checked<T, Ts...>::value>(v); > } > > template <typename T, typename... Ts> > inline constexpr T &&get(variant<Ts...> &&v) > { > return hsh::get<variant_detail::find_index_checked<T, Ts...>::value>(hsh::move(v)); > } > > template <typename T, typename... Ts> > inline constexpr const T &get(const variant<Ts...> &v) > { > return hsh::get<variant_detail::find_index_checked<T, Ts...>::value>(v); > } > > template <typename T, typename... Ts> > inline constexpr const T &&get(const variant<Ts...> &&v) > { > return hsh::get<variant_detail::find_index_checked<T, Ts...>::value>(hsh::move(v)); > } > > template <std::size_t I, typename... Ts> > inline constexpr add_pointer_t<variant_alternative_t<I, variant<Ts...>>> get_if(variant<Ts...> *v) noexcept > { > return v && hsh::holds_alternative<I>(*v) ? hsh::addressof(hsh::get<I>(*v)) : nullptr; > } > > template <std::size_t I, typename... Ts> > inline constexpr add_pointer_t<const variant_alternative_t<I, variant<Ts...>>> get_if(const variant<Ts...> *v) noexcept > { > return v && hsh::holds_alternative<I>(*v) ? hsh::addressof(hsh::get<I>(*v)) : nullptr; > } > > template <typename T, typename... Ts> > inline constexpr add_pointer_t<T> get_if(variant<Ts...> *v) noexcept > { > return hsh::get_if<variant_detail::find_index_checked<T, Ts...>::value>(v); > } > > template <typename T, typename... Ts> > inline constexpr add_pointer_t<const T> get_if(const variant<Ts...> *v) noexcept > { > return hsh::get_if<variant_detail::find_index_checked<T, Ts...>::value>(v); > } > > template <typename Visitor, typename... Vs> > inline constexpr auto visit(Visitor&& visitor, Vs&&... vs) > -> decltype(variant_detail::variant_visitor::visit(hsh::forward<Visitor>(visitor), hsh::forward<Vs>(vs)...)) > { > return (static_conjunction(!vs.valueless_by_exception()...) ? (void)0 : throw_bad_variant_access()), > variant_detail::variant_visitor::visit(hsh::forward<Visitor>(visitor), hsh::forward<Vs>(vs)...); > } >} > >class thread_mon >{ >public: > void unregister_thread(thread_mon*, const char*, int32_t){} > void heartbeat_thread(thread_mon*, const char*, int32_t) {} > int32_t register_thread(thread_mon*, const char*, uint32_t time_out) { return 1; } > void ignore_thread_timed(thread_mon*, const char*, int32_t, uint32_t time_out) {} >}; > >using THREAD_MON = thread_mon*; > >class executor_heartbeater >{ >public: > virtual ~executor_heartbeater() = default; > virtual void ignored(hsh::milliseconds ignore) noexcept = 0; > virtual hsh::milliseconds heartbeat_timeout() const noexcept = 0; > virtual hsh::milliseconds period() const noexcept = 0; > virtual hsh::time_point next_period() const noexcept = 0; > virtual bool elapsed() noexcept = 0; > virtual void schedule_heartbeat() = 0; >}; > >template <typename Ex> >class executor_heartbeat_wrapper : public std::enable_shared_from_this<executor_heartbeat_wrapper<Ex>>, public executor_heartbeater >{ >public: > executor_heartbeat_wrapper(THREAD_MON tm, Ex ex, hsh::milliseconds hb_timeout, hsh::milliseconds hb_frequency) > : tm(tm), ex(ex), hb_timeout(hb_timeout) {} > > ~executor_heartbeat_wrapper() > { > if (is_registered()){ > tm->unregister_thread(tm, nullptr, hb_id); > } > } > > virtual void ignored(hsh::milliseconds ignore) noexcept override { tm->ignore_thread_timed(tm, nullptr, hb_id, (uint32_t)ignore.count()); } > virtual hsh::milliseconds period() const noexcept override { return {}; } > virtual hsh::milliseconds heartbeat_timeout() const noexcept override { return hb_timeout; } > virtual hsh::time_point next_period() const noexcept override { return hsh::time_point{}; } > virtual bool elapsed() noexcept override { return true; } > virtual void schedule_heartbeat() override { > ex.execute(hsh::bind_front(&executor_heartbeat_wrapper::internal_heartbeat, this->shared_from_this())); > } > > /* functions for registering the EX to heartbeat */ > bool is_registered() const noexcept { return -1 != hb_id; } > > void register_thread(const std::string& name) { > auto bound_f = hsh::bind_front(&executor_heartbeat_wrapper::register_internal, this->shared_from_this(), name); > bound_f(); > ex.execute(hsh::bind_front(&executor_heartbeat_wrapper::register_internal, this->shared_from_this(), name)); > } > > bool wait_timed(hsh::milliseconds timeout) { return true; } > > void cancel() noexcept { cancelled = true; } > >private: > void internal_heartbeat() > { > if (!cancelled) > { > tm->heartbeat_thread(tm, nullptr, hb_id); > } > } > > void register_internal(const std::string& name) > { > if (!cancelled) > { > hb_id = tm->register_thread( tm, name.c_str(), (uint32_t)hb_timeout.count()); > } > } > > hsh::milliseconds hb_timeout; > THREAD_MON tm; > Ex ex; > int32_t hb_id = -1; > bool cancelled = false; >}; > > >template<class Ex> >std::shared_ptr<executor_heartbeater> make_executor_heartbeater(THREAD_MON tm, Ex ex, const std::string& name, hsh::milliseconds hb_timeout, hsh::milliseconds hb_frequency, hsh::milliseconds timeout = std::chrono::seconds{5}) >{ > auto hbtr = std::make_shared<executor_heartbeat_wrapper<Ex>>(tm, ex, hb_timeout, hb_frequency); > hbtr->register_thread(name); > if ( !hbtr->wait_timed(timeout) || !hbtr->is_registered() ) > { > hbtr->cancel(); > return nullptr; > } > return hbtr; >} > > > >int main() >{ > auto b = hsh::bind_front([](int a, int b) { return a + b;}, 1, 1)(); > > > return b; >}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 1918957
:
1749502
|
1749517
| 1749573