1043 lines
28 KiB
C++
1043 lines
28 KiB
C++
// 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;
|
|
#ifdef __cpp_lib_is_invocable
|
|
template <class> struct result_of;
|
|
|
|
template <class F, class... TN>
|
|
struct result_of<F(TN...)>
|
|
{
|
|
using type = std::invoke_result_t<F, TN...>;
|
|
};
|
|
#else
|
|
template<class... TN> using result_of = std::result_of<TN...>;
|
|
#endif
|
|
template<class... TN> using result_of_t = typename 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, std::size_t 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, std::size_t 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 rxu::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
|