// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. #pragma once #if !defined(RXCPP_RX_UTIL_HPP) #define RXCPP_RX_UTIL_HPP #include "rx-includes.hpp" #if !defined(RXCPP_ON_IOS) && !defined(RXCPP_ON_ANDROID) && !defined(RXCPP_THREAD_LOCAL) #if defined(_MSC_VER) #define RXCPP_THREAD_LOCAL __declspec(thread) #else #define RXCPP_THREAD_LOCAL __thread #endif #endif #if !defined(RXCPP_DELETE) #if defined(_MSC_VER) #define RXCPP_DELETE __pragma(warning(disable: 4822)) =delete #else #define RXCPP_DELETE =delete #endif #endif #define RXCPP_CONCAT(Prefix, Suffix) Prefix ## Suffix #define RXCPP_CONCAT_EVALUATE(Prefix, Suffix) RXCPP_CONCAT(Prefix, Suffix) #define RXCPP_MAKE_IDENTIFIER(Prefix) RXCPP_CONCAT_EVALUATE(Prefix, __LINE__) // Provide replacements for try/catch keywords, using which is a compilation error // when exceptions are disabled with -fno-exceptions. #if RXCPP_USE_EXCEPTIONS #define RXCPP_TRY try #define RXCPP_CATCH(...) catch(__VA_ARGS__) // See also rxu::throw_exception for 'throw' keyword replacement. #else #define RXCPP_TRY if ((true)) #define RXCPP_CATCH(...) if ((false)) // See also rxu::throw_exception, which will std::terminate without exceptions. #endif namespace rxcpp { namespace util { template<class T> using value_type_t = typename std::decay<T>::type::value_type; template<class T> using decay_t = typename std::decay<T>::type; template<class... TN> using result_of_t = typename std::result_of<TN...>::type; template<class T, std::size_t size> std::vector<T> to_vector(const T (&arr) [size]) { return std::vector<T>(std::begin(arr), std::end(arr)); } template<class T> std::vector<T> to_vector(std::initializer_list<T> il) { return std::vector<T>(il); } template<class T0, class... TN> typename std::enable_if<!std::is_array<T0>::value && std::is_pod<T0>::value, std::vector<T0>>::type to_vector(T0 t0, TN... tn) { return to_vector({t0, tn...}); } // lifted from https://github.com/ericniebler/range-v3/blob/630fc70baa07cbfd222f329e44a3122ab64ce364/include/range/v3/range_fwd.hpp // removed constexpr & noexcept to support older VC compilers template<typename T> /*constexpr*/ T const &as_const(T & t) /*noexcept*/ { return t; } template<typename T> void as_const(T const &&) = delete; template<class T, T... ValueN> struct values {}; template<class T, int Remaining, T Step = 1, T Cursor = 0, T... ValueN> struct values_from; template<class T, T Step, T Cursor, T... ValueN> struct values_from<T, 0, Step, Cursor, ValueN...> { typedef values<T, ValueN...> type; }; template<class T, int Remaining, T Step, T Cursor, T... ValueN> struct values_from { typedef typename values_from<T, Remaining - 1, Step, Cursor + Step, ValueN..., Cursor>::type type; }; template<bool... BN> struct all_true; template<bool B> struct all_true<B> { static const bool value = B; }; template<bool B, bool... BN> struct all_true<B, BN...> { static const bool value = B && all_true<BN...>::value; }; template<bool... BN> using enable_if_all_true_t = typename std::enable_if<all_true<BN...>::value>::type; template<class... BN> struct all_true_type; template<class B> struct all_true_type<B> { static const bool value = B::value; }; template<class B, class... BN> struct all_true_type<B, BN...> { static const bool value = B::value && all_true_type<BN...>::value; }; template<class... BN> using enable_if_all_true_type_t = typename std::enable_if<all_true_type<BN...>::value>::type; struct all_values_true { template<class... ValueN> bool operator()(ValueN... vn) const; template<class Value0> bool operator()(Value0 v0) const { return v0; } template<class Value0, class... ValueN> bool operator()(Value0 v0, ValueN... vn) const { return v0 && all_values_true()(vn...); } }; struct any_value_true { template<class... ValueN> bool operator()(ValueN... vn) const; template<class Value0> bool operator()(Value0 v0) const { return v0; } template<class Value0, class... ValueN> bool operator()(Value0 v0, ValueN... vn) const { return v0 || any_value_true()(vn...); } }; template<class... TN> struct types {}; // // based on Walter Brown's void_t proposal // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3911.pdf // struct types_checked {}; namespace detail { template<class... TN> struct types_checked_from {typedef types_checked type;}; } template<class... TN> struct types_checked_from {typedef typename detail::types_checked_from<TN...>::type type;}; template<class... TN> using types_checked_t = typename types_checked_from<TN...>::type; template<class Types, class =types_checked> struct expand_value_types { struct type; }; template<class... TN> struct expand_value_types<types<TN...>, types_checked_t<typename std::decay<TN>::type::value_type...>> { using type = types<typename std::decay<TN>::type::value_type...>; }; template<class... TN> using value_types_t = typename expand_value_types<types<TN...>>::type; template<class T, class C = types_checked> struct value_type_from : public std::false_type {typedef types_checked type;}; template<class T> struct value_type_from<T, typename types_checked_from<value_type_t<T>>::type> : public std::true_type {typedef value_type_t<T> type;}; namespace detail { template<class F, class... ParamN, int... IndexN> auto apply(std::tuple<ParamN...> p, values<int, IndexN...>, F&& f) -> decltype(f(std::forward<ParamN>(std::get<IndexN>(p))...)) { return f(std::forward<ParamN>(std::get<IndexN>(p))...); } template<class F_inner, class F_outer, class... ParamN, int... IndexN> auto apply_to_each(std::tuple<ParamN...>& p, values<int, IndexN...>, F_inner& f_inner, F_outer& f_outer) -> decltype(f_outer(std::move(f_inner(std::get<IndexN>(p)))...)) { return f_outer(std::move(f_inner(std::get<IndexN>(p)))...); } template<class F_inner, class F_outer, class... ParamN, int... IndexN> auto apply_to_each(std::tuple<ParamN...>& p, values<int, IndexN...>, const F_inner& f_inner, const F_outer& f_outer) -> decltype(f_outer(std::move(f_inner(std::get<IndexN>(p)))...)) { return f_outer(std::move(f_inner(std::get<IndexN>(p)))...); } } template<class F, class... ParamN> auto apply(std::tuple<ParamN...> p, F&& f) -> decltype(detail::apply(std::move(p), typename values_from<int, sizeof...(ParamN)>::type(), std::forward<F>(f))) { return detail::apply(std::move(p), typename values_from<int, sizeof...(ParamN)>::type(), std::forward<F>(f)); } template<class F_inner, class F_outer, class... ParamN> auto apply_to_each(std::tuple<ParamN...>& p, F_inner& f_inner, F_outer& f_outer) -> decltype(detail::apply_to_each(p, typename values_from<int, sizeof...(ParamN)>::type(), f_inner, f_outer)) { return detail::apply_to_each(p, typename values_from<int, sizeof...(ParamN)>::type(), f_inner, f_outer); } template<class F_inner, class F_outer, class... ParamN> auto apply_to_each(std::tuple<ParamN...>& p, const F_inner& f_inner, const F_outer& f_outer) -> decltype(detail::apply_to_each(p, typename values_from<int, sizeof...(ParamN)>::type(), f_inner, f_outer)) { return detail::apply_to_each(p, typename values_from<int, sizeof...(ParamN)>::type(), f_inner, f_outer); } namespace detail { template<class F> struct apply_to { F to; explicit apply_to(F f) : to(std::move(f)) { } template<class... ParamN> auto operator()(std::tuple<ParamN...> p) -> decltype(rxcpp::util::apply(std::move(p), to)) { return rxcpp::util::apply(std::move(p), to); } template<class... ParamN> auto operator()(std::tuple<ParamN...> p) const -> decltype(rxcpp::util::apply(std::move(p), to)) { return rxcpp::util::apply(std::move(p), to); } }; } template<class F> auto apply_to(F f) -> detail::apply_to<F> { return detail::apply_to<F>(std::move(f)); } namespace detail { struct pack { template<class... ParamN> auto operator()(ParamN... pn) -> decltype(std::make_tuple(std::move(pn)...)) { return std::make_tuple(std::move(pn)...); } template<class... ParamN> auto operator()(ParamN... pn) const -> decltype(std::make_tuple(std::move(pn)...)) { return std::make_tuple(std::move(pn)...); } }; } inline auto pack() -> detail::pack { return detail::pack(); } namespace detail { template<int Index> struct take_at { template<class... ParamN> auto operator()(ParamN... pn) -> typename std::tuple_element<Index, std::tuple<decay_t<ParamN>...>>::type { return std::get<Index>(std::make_tuple(std::move(pn)...)); } template<class... ParamN> auto operator()(ParamN... pn) const -> typename std::tuple_element<Index, std::tuple<decay_t<ParamN>...>>::type { return std::get<Index>(std::make_tuple(std::move(pn)...)); } }; } template<int Index> inline auto take_at() -> detail::take_at<Index> { return detail::take_at<Index>(); } template <class D> struct resolve_type; template <template<class... TN> class Deferred, class... AN> struct defer_trait { template<bool R> struct tag_valid {static const bool valid = true; static const bool value = R;}; struct tag_not_valid {static const bool valid = false; static const bool value = false;}; typedef Deferred<typename resolve_type<AN>::type...> resolved_type; template<class... CN> static auto check(int) -> tag_valid<resolved_type::value>; template<class... CN> static tag_not_valid check(...); typedef decltype(check<AN...>(0)) tag_type; static const bool valid = tag_type::valid; static const bool value = tag_type::value; static const bool not_value = valid && !value; }; template <template<class... TN> class Deferred, class... AN> struct defer_type { template<class R> struct tag_valid {typedef R type; static const bool value = true;}; struct tag_not_valid {typedef void type; static const bool value = false;}; typedef Deferred<typename resolve_type<AN>::type...> resolved_type; template<class... CN> static auto check(int) -> tag_valid<resolved_type>; template<class... CN> static tag_not_valid check(...); typedef decltype(check<AN...>(0)) tag_type; typedef typename tag_type::type type; static const bool value = tag_type::value; }; template <template<class... TN> class Deferred, class... AN> struct defer_value_type { template<class R> struct tag_valid {typedef R type; static const bool value = true;}; struct tag_not_valid {typedef void type; static const bool value = false;}; typedef Deferred<typename resolve_type<AN>::type...> resolved_type; template<class... CN> static auto check(int) -> tag_valid<value_type_t<resolved_type>>; template<class... CN> static tag_not_valid check(...); typedef decltype(check<AN...>(0)) tag_type; typedef typename tag_type::type type; static const bool value = tag_type::value; }; template <template<class... TN> class Deferred, class... AN> struct defer_seed_type { template<class R> struct tag_valid {typedef R type; static const bool value = true;}; struct tag_not_valid {typedef void type; static const bool value = false;}; typedef Deferred<typename resolve_type<AN>::type...> resolved_type; template<class... CN> static auto check(int) -> tag_valid<typename resolved_type::seed_type>; template<class... CN> static tag_not_valid check(...); typedef decltype(check<AN...>(0)) tag_type; typedef typename tag_type::type type; static const bool value = tag_type::value; }; template <class D> struct resolve_type { typedef D type; }; template <template<class... TN> class Deferred, class... AN> struct resolve_type<defer_type<Deferred, AN...>> { typedef typename defer_type<Deferred, AN...>::type type; }; template <template<class... TN> class Deferred, class... AN> struct resolve_type<defer_value_type<Deferred, AN...>> { typedef typename defer_value_type<Deferred, AN...>::type type; }; template <template<class... TN> class Deferred, class... AN> struct resolve_type<defer_seed_type<Deferred, AN...>> { typedef typename defer_seed_type<Deferred, AN...>::type type; }; struct plus { template <class LHS, class RHS> auto operator()(LHS&& lhs, RHS&& rhs) const -> decltype(std::forward<LHS>(lhs) + std::forward<RHS>(rhs)) { return std::forward<LHS>(lhs) + std::forward<RHS>(rhs); } }; struct count { template <class T> int operator()(int cnt, T&&) const { return cnt + 1; } }; struct less { template <class LHS, class RHS> auto operator()(LHS&& lhs, RHS&& rhs) const -> decltype(std::forward<LHS>(lhs) < std::forward<RHS>(rhs)) { return std::forward<LHS>(lhs) < std::forward<RHS>(rhs); } }; template <class T> struct ret { template <class LHS> auto operator()(LHS&& ) const -> decltype(T()) { return T(); } }; template<class T = void> struct equal_to { bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } }; template<> struct equal_to<void> { template<class LHS, class RHS> auto operator()(LHS&& lhs, RHS&& rhs) const -> decltype(std::forward<LHS>(lhs) == std::forward<RHS>(rhs)) { return std::forward<LHS>(lhs) == std::forward<RHS>(rhs); } }; namespace detail { template<class OStream, class Delimit> struct print_function { OStream& os; Delimit delimit; print_function(OStream& os, Delimit d) : os(os), delimit(std::move(d)) {} template<class... TN> void operator()(const TN&... tn) const { bool inserts[] = {(os << tn, true)...}; inserts[0] = *reinterpret_cast<bool*>(inserts); // silence warning delimit(); } template<class... TN> void operator()(const std::tuple<TN...>& tpl) const { rxcpp::util::apply(tpl, *this); } }; template<class OStream> struct endline { OStream& os; endline(OStream& os) : os(os) {} void operator()() const { os << std::endl; } private: endline& operator=(const endline&) RXCPP_DELETE; }; template<class OStream, class ValueType> struct insert_value { OStream& os; ValueType value; insert_value(OStream& os, ValueType v) : os(os), value(std::move(v)) {} void operator()() const { os << value; } private: insert_value& operator=(const insert_value&) RXCPP_DELETE; }; template<class OStream, class Function> struct insert_function { OStream& os; Function call; insert_function(OStream& os, Function f) : os(os), call(std::move(f)) {} void operator()() const { call(os); } private: insert_function& operator=(const insert_function&) RXCPP_DELETE; }; template<class OStream, class Delimit> auto print_followed_with(OStream& os, Delimit d) -> detail::print_function<OStream, Delimit> { return detail::print_function<OStream, Delimit>(os, std::move(d)); } } template<class OStream> auto endline(OStream& os) -> detail::endline<OStream> { return detail::endline<OStream>(os); } template<class OStream> auto println(OStream& os) -> decltype(detail::print_followed_with(os, endline(os))) { return detail::print_followed_with(os, endline(os)); } template<class OStream, class Delimit> auto print_followed_with(OStream& os, Delimit d) -> decltype(detail::print_followed_with(os, detail::insert_function<OStream, Delimit>(os, std::move(d)))) { return detail::print_followed_with(os, detail::insert_function<OStream, Delimit>(os, std::move(d))); } template<class OStream, class DelimitValue> auto print_followed_by(OStream& os, DelimitValue dv) -> decltype(detail::print_followed_with(os, detail::insert_value<OStream, DelimitValue>(os, std::move(dv)))) { return detail::print_followed_with(os, detail::insert_value<OStream, DelimitValue>(os, std::move(dv))); } inline std::string what(std::exception_ptr ep) { #if RXCPP_USE_EXCEPTIONS try {std::rethrow_exception(ep);} catch (const std::exception& ex) { return ex.what(); } catch (...) { return std::string("<not derived from std::exception>"); } #endif (void)ep; return std::string("<exceptions are disabled>"); } namespace detail { template <class T> class maybe { bool is_set; typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type storage; public: maybe() : is_set(false) { } maybe(T value) : is_set(false) { new (reinterpret_cast<T*>(&storage)) T(value); is_set = true; } maybe(const maybe& other) : is_set(false) { if (other.is_set) { new (reinterpret_cast<T*>(&storage)) T(other.get()); is_set = true; } } maybe(maybe&& other) : is_set(false) { if (other.is_set) { new (reinterpret_cast<T*>(&storage)) T(std::move(other.get())); is_set = true; other.reset(); } } ~maybe() { reset(); } typedef T value_type; typedef T* iterator; typedef const T* const_iterator; bool empty() const { return !is_set; } std::size_t size() const { return is_set ? 1 : 0; } iterator begin() { return reinterpret_cast<T*>(&storage); } const_iterator begin() const { return reinterpret_cast<T*>(&storage); } iterator end() { return reinterpret_cast<T*>(&storage) + size(); } const_iterator end() const { return reinterpret_cast<T*>(&storage) + size(); } T* operator->() { if (!is_set) std::terminate(); return reinterpret_cast<T*>(&storage); } const T* operator->() const { if (!is_set) std::terminate(); return reinterpret_cast<T*>(&storage); } T& operator*() { if (!is_set) std::terminate(); return *reinterpret_cast<T*>(&storage); } const T& operator*() const { if (!is_set) std::terminate(); return *reinterpret_cast<T*>(&storage); } T& get() { if (!is_set) std::terminate(); return *reinterpret_cast<T*>(&storage); } const T& get() const { if (!is_set) std::terminate(); return *reinterpret_cast<const T*>(&storage); } void reset() { if (is_set) { is_set = false; reinterpret_cast<T*>(&storage)->~T(); //std::fill_n(reinterpret_cast<char*>(&storage), sizeof(T), 0); } } template<class U> void reset(U&& value) { reset(); new (reinterpret_cast<T*>(&storage)) T(std::forward<U>(value)); is_set = true; } maybe& operator=(const T& other) { reset(other); return *this; } maybe& operator=(const maybe& other) { if (!other.empty()) { reset(other.get()); } else { reset(); } return *this; } }; } using detail::maybe; namespace detail { struct surely { template<class... T> auto operator()(T... t) -> decltype(std::make_tuple(t.get()...)) { return std::make_tuple(t.get()...); } template<class... T> auto operator()(T... t) const -> decltype(std::make_tuple(t.get()...)) { return std::make_tuple(t.get()...); } }; } template<class... T> inline auto surely(const std::tuple<T...>& tpl) -> decltype(apply(tpl, detail::surely())) { return apply(tpl, detail::surely()); } namespace detail { template<typename Function> class unwinder { public: ~unwinder() { if (!!function) { RXCPP_TRY { (*function)(); } RXCPP_CATCH(...) { std::terminate(); } } } explicit unwinder(Function* functionArg) : function(functionArg) { } void dismiss() { function = nullptr; } private: unwinder(); unwinder(const unwinder&); unwinder& operator=(const unwinder&); Function* function; }; } #if !defined(RXCPP_THREAD_LOCAL) template<typename T> class thread_local_storage { private: pthread_key_t key; public: thread_local_storage() { pthread_key_create(&key, NULL); } ~thread_local_storage() { pthread_key_delete(key); } thread_local_storage& operator =(T* p) { pthread_setspecific(key, p); return *this; } bool operator !() { return pthread_getspecific(key) == NULL; } T* operator ->() { return static_cast<T*>(pthread_getspecific(key)); } T* get() { return static_cast<T*>(pthread_getspecific(key)); } }; #endif template<typename, typename C = types_checked> struct is_string : std::false_type { }; template <typename T> struct is_string<T, typename types_checked_from< typename T::value_type, typename T::traits_type, typename T::allocator_type>::type> : std::is_base_of< std::basic_string< typename T::value_type, typename T::traits_type, typename T::allocator_type>, T> { }; namespace detail { template <class T, class = types_checked> struct is_duration : std::false_type {}; template <class T> struct is_duration<T, types_checked_t<T, typename T::rep, typename T::period>> : std::is_convertible<T*, std::chrono::duration<typename T::rep, typename T::period>*> {}; } template <class T, class Decayed = decay_t<T>> struct is_duration : detail::is_duration<Decayed> {}; // C++17 negation namespace detail { template<class T> struct not_value : std::conditional<T::value, std::false_type, std::true_type>::type { }; } template <class T> struct negation : detail::not_value<T> {}; } #if !RXCPP_USE_EXCEPTIONS namespace util { namespace detail { struct error_base { virtual const char* what() = 0; virtual ~error_base() {} }; // Use the "Type Erasure" idiom to wrap an std::exception-like // value into an error pointer. // // Supported types: // exception, bad_exception, bad_alloc. template <class E> struct error_specific : public error_base { error_specific(const E& e) : data(e) {} error_specific(E&& e) : data(std::move(e)) {} virtual ~error_specific() {} virtual const char* what() { return data.what(); } E data; }; } } #endif namespace util { #if RXCPP_USE_EXCEPTIONS using error_ptr = std::exception_ptr; #else // Note: std::exception_ptr cannot be used directly when exceptions are disabled. // Any attempt to 'throw' or to call into any of the std functions accepting // an std::exception_ptr will either fail to compile or result in an abort at runtime. using error_ptr = std::shared_ptr<util::detail::error_base>; inline std::string what(error_ptr ep) { return std::string(ep->what()); } #endif // TODO: Do we really need an identity make? // (It was causing some compilation errors deep inside templates). inline error_ptr make_error_ptr(error_ptr e) { return e; } // Replace std::make_exception_ptr (which would immediately terminate // when exceptions are disabled). template <class E> error_ptr make_error_ptr(E&& e) { #if RXCPP_USE_EXCEPTIONS return std::make_exception_ptr(std::forward<E>(e)); #else using e_type = rxcpp::util::decay_t<E>; using pointed_to_type = rxcpp::util::detail::error_specific<e_type>; auto sp = std::make_shared<pointed_to_type>(std::forward<E>(e)); return std::static_pointer_cast<rxcpp::util::detail::error_base>(sp); #endif } // Replace std::rethrow_exception to be compatible with our error_ptr typedef. RXCPP_NORETURN inline void rethrow_exception(error_ptr e) { #if RXCPP_USE_EXCEPTIONS std::rethrow_exception(e); #else // error_ptr != std::exception_ptr so we can't use std::rethrow_exception // // However even if we could, calling std::rethrow_exception just terminates if exceptions are disabled. // // Therefore this function should only be called when we are completely giving up and have no idea // how to handle the error. (void)e; std::terminate(); #endif } // A replacement for the "throw" keyword which is illegal when // exceptions are disabled with -fno-exceptions. template <typename E> RXCPP_NORETURN inline void throw_exception(E&& e) { #if RXCPP_USE_EXCEPTIONS throw std::forward<E>(e); #else // "throw" keyword is unsupported when exceptions are disabled. // Immediately terminate instead. (void)e; std::terminate(); #endif } // TODO: Do we really need this? rxu::rethrow_exception(rxu::current_exception()) // would have the same semantics in either case. RXCPP_NORETURN inline void rethrow_current_exception() { #if RXCPP_USE_EXCEPTIONS std::rethrow_exception(std::current_exception()); #else std::terminate(); #endif } // If called during exception handling, return the currently caught exception. // Otherwise return null. inline error_ptr current_exception() { #if RXCPP_USE_EXCEPTIONS return std::current_exception(); #else // When exceptions are disabled, we can never be inside of a catch block. // Return null similar to std::current_exception returning null outside of catch. return nullptr; #endif } } namespace rxu=util; // // due to an noisy static_assert issue in more than one std lib impl, // rxcpp maintains a whitelist filter for the types that are allowed // to be hashed. this allows is_hashable<T> to work. // // NOTE: this should eventually be removed! // template <class T, typename = void> struct filtered_hash; #if RXCPP_HASH_ENUM template <class T> struct filtered_hash<T, typename std::enable_if<std::is_enum<T>::value>::type> : std::hash<T> { }; #elif RXCPP_HASH_ENUM_UNDERLYING template <class T> struct filtered_hash<T, typename std::enable_if<std::is_enum<T>::value>::type> : std::hash<typename std::underlying_type<T>::type> { }; #endif template <class T> struct filtered_hash<T, typename std::enable_if<std::is_integral<T>::value>::type> : std::hash<T> { }; template <class T> struct filtered_hash<T, typename std::enable_if<std::is_pointer<T>::value>::type> : std::hash<T> { }; template <class T> struct filtered_hash<T, typename std::enable_if<rxu::is_string<T>::value>::type> : std::hash<T> { }; template <class T> struct filtered_hash<T, typename std::enable_if<std::is_convertible<T, std::chrono::duration<typename T::rep, typename T::period>>::value>::type> { using argument_type = T; using result_type = std::size_t; result_type operator()(argument_type const & dur) const { return std::hash<typename argument_type::rep>{}(dur.count()); } }; template <class T> struct filtered_hash<T, typename std::enable_if<std::is_convertible<T, std::chrono::time_point<typename T::clock, typename T::duration>>::value>::type> { using argument_type = T; using result_type = std::size_t; result_type operator()(argument_type const & tp) const { return std::hash<typename argument_type::rep>{}(tp.time_since_epoch().count()); } }; template<typename, typename C = rxu::types_checked> struct is_hashable : std::false_type {}; template<typename T> struct is_hashable<T, typename rxu::types_checked_from< typename filtered_hash<T>::result_type, typename filtered_hash<T>::argument_type, typename std::result_of<filtered_hash<T>(T)>::type>::type> : std::true_type {}; } #define RXCPP_UNWIND(Name, Function) \ RXCPP_UNWIND_EXPLICIT(uwfunc_ ## Name, Name, Function) #define RXCPP_UNWIND_AUTO(Function) \ RXCPP_UNWIND_EXPLICIT(RXCPP_MAKE_IDENTIFIER(uwfunc_), RXCPP_MAKE_IDENTIFIER(unwind_), Function) #define RXCPP_UNWIND_EXPLICIT(FunctionName, UnwinderName, Function) \ auto FunctionName = (Function); \ rxcpp::util::detail::unwinder<decltype(FunctionName)> UnwinderName(std::addressof(FunctionName)) #endif