1 // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
2
3 #pragma once
4
5 #if !defined(RXCPP_RX_UTIL_HPP)
6 #define RXCPP_RX_UTIL_HPP
7
8 #include "rx-includes.hpp"
9
10 #if !defined(RXCPP_ON_IOS) && !defined(RXCPP_ON_ANDROID) && !defined(RXCPP_THREAD_LOCAL)
11 #if defined(_MSC_VER)
12 #define RXCPP_THREAD_LOCAL __declspec(thread)
13 #else
14 #define RXCPP_THREAD_LOCAL __thread
15 #endif
16 #endif
17
18 #if !defined(RXCPP_DELETE)
19 #if defined(_MSC_VER)
20 #define RXCPP_DELETE __pragma(warning(disable: 4822)) =delete
21 #else
22 #define RXCPP_DELETE =delete
23 #endif
24 #endif
25
26 #define RXCPP_CONCAT(Prefix, Suffix) Prefix ## Suffix
27 #define RXCPP_CONCAT_EVALUATE(Prefix, Suffix) RXCPP_CONCAT(Prefix, Suffix)
28
29 #define RXCPP_MAKE_IDENTIFIER(Prefix) RXCPP_CONCAT_EVALUATE(Prefix, __LINE__)
30
31 // Provide replacements for try/catch keywords, using which is a compilation error
32 // when exceptions are disabled with -fno-exceptions.
33 #if RXCPP_USE_EXCEPTIONS
34 #define RXCPP_TRY try
35 #define RXCPP_CATCH(...) catch(__VA_ARGS__)
36 // See also rxu::throw_exception for 'throw' keyword replacement.
37 #else
38 #define RXCPP_TRY if ((true))
39 #define RXCPP_CATCH(...) if ((false))
40 // See also rxu::throw_exception, which will std::terminate without exceptions.
41 #endif
42
43 namespace rxcpp {
44
45 namespace util {
46
47 template<class T> using value_type_t = typename std::decay<T>::type::value_type;
48 template<class T> using decay_t = typename std::decay<T>::type;
49 template<class... TN> using result_of_t = typename std::result_of<TN...>::type;
50
51 template<class T, std::size_t size>
to_vector(const T (& arr)[size])52 std::vector<T> to_vector(const T (&arr) [size]) {
53 return std::vector<T>(std::begin(arr), std::end(arr));
54 }
55
56 template<class T>
to_vector(std::initializer_list<T> il)57 std::vector<T> to_vector(std::initializer_list<T> il) {
58 return std::vector<T>(il);
59 }
60
61 template<class T0, class... TN>
to_vector(T0 t0,TN...tn)62 typename std::enable_if<!std::is_array<T0>::value && std::is_pod<T0>::value, std::vector<T0>>::type to_vector(T0 t0, TN... tn) {
63 return to_vector({t0, tn...});
64 }
65
66 // lifted from https://github.com/ericniebler/range-v3/blob/630fc70baa07cbfd222f329e44a3122ab64ce364/include/range/v3/range_fwd.hpp
67 // removed constexpr & noexcept to support older VC compilers
68 template<typename T>
as_const(T & t)69 /*constexpr*/ T const &as_const(T & t) /*noexcept*/
70 {
71 return t;
72 }
73 template<typename T>
74 void as_const(T const &&) = delete;
75
76 template<class T, T... ValueN>
77 struct values {};
78
79 template<class T, int Remaining, T Step = 1, T Cursor = 0, T... ValueN>
80 struct values_from;
81
82 template<class T, T Step, T Cursor, T... ValueN>
83 struct values_from<T, 0, Step, Cursor, ValueN...>
84 {
85 typedef values<T, ValueN...> type;
86 };
87
88 template<class T, int Remaining, T Step, T Cursor, T... ValueN>
89 struct values_from
90 {
91 typedef typename values_from<T, Remaining - 1, Step, Cursor + Step, ValueN..., Cursor>::type type;
92 };
93
94 template<bool... BN>
95 struct all_true;
96
97 template<bool B>
98 struct all_true<B>
99 {
100 static const bool value = B;
101 };
102 template<bool B, bool... BN>
103 struct all_true<B, BN...>
104 {
105 static const bool value = B && all_true<BN...>::value;
106 };
107
108 template<bool... BN>
109 using enable_if_all_true_t = typename std::enable_if<all_true<BN...>::value>::type;
110
111 template<class... BN>
112 struct all_true_type;
113
114 template<class B>
115 struct all_true_type<B>
116 {
117 static const bool value = B::value;
118 };
119 template<class B, class... BN>
120 struct all_true_type<B, BN...>
121 {
122 static const bool value = B::value && all_true_type<BN...>::value;
123 };
124
125 template<class... BN>
126 using enable_if_all_true_type_t = typename std::enable_if<all_true_type<BN...>::value>::type;
127
128 struct all_values_true {
129 template<class... ValueN>
130 bool operator()(ValueN... vn) const;
131
132 template<class Value0>
operator ()rxcpp::util::all_values_true133 bool operator()(Value0 v0) const {
134 return v0;
135 }
136
137 template<class Value0, class... ValueN>
operator ()rxcpp::util::all_values_true138 bool operator()(Value0 v0, ValueN... vn) const {
139 return v0 && all_values_true()(vn...);
140 }
141 };
142
143 struct any_value_true {
144 template<class... ValueN>
145 bool operator()(ValueN... vn) const;
146
147 template<class Value0>
operator ()rxcpp::util::any_value_true148 bool operator()(Value0 v0) const {
149 return v0;
150 }
151
152 template<class Value0, class... ValueN>
operator ()rxcpp::util::any_value_true153 bool operator()(Value0 v0, ValueN... vn) const {
154 return v0 || any_value_true()(vn...);
155 }
156 };
157
158 template<class... TN>
159 struct types {};
160
161 //
162 // based on Walter Brown's void_t proposal
163 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3911.pdf
164 //
165
166 struct types_checked {};
167
168 namespace detail {
169 template<class... TN> struct types_checked_from {typedef types_checked type;};
170 }
171
172 template<class... TN>
173 struct types_checked_from {typedef typename detail::types_checked_from<TN...>::type type;};
174
175 template<class... TN>
176 using types_checked_t = typename types_checked_from<TN...>::type;
177
178
179 template<class Types, class =types_checked>
180 struct expand_value_types { struct type; };
181 template<class... TN>
182 struct expand_value_types<types<TN...>, types_checked_t<typename std::decay<TN>::type::value_type...>>
183 {
184 using type = types<typename std::decay<TN>::type::value_type...>;
185 };
186 template<class... TN>
187 using value_types_t = typename expand_value_types<types<TN...>>::type;
188
189
190 template<class T, class C = types_checked>
191 struct value_type_from : public std::false_type {typedef types_checked type;};
192
193 template<class T>
194 struct value_type_from<T, typename types_checked_from<value_type_t<T>>::type>
195 : public std::true_type {typedef value_type_t<T> type;};
196
197 namespace detail {
198 template<class F, class... ParamN, int... IndexN>
apply(std::tuple<ParamN...> p,values<int,IndexN...>,F && f)199 auto apply(std::tuple<ParamN...> p, values<int, IndexN...>, F&& f)
200 -> decltype(f(std::forward<ParamN>(std::get<IndexN>(p))...)) {
201 return f(std::forward<ParamN>(std::get<IndexN>(p))...);
202 }
203
204 template<class F_inner, class F_outer, class... ParamN, int... IndexN>
apply_to_each(std::tuple<ParamN...> & p,values<int,IndexN...>,F_inner & f_inner,F_outer & f_outer)205 auto apply_to_each(std::tuple<ParamN...>& p, values<int, IndexN...>, F_inner& f_inner, F_outer& f_outer)
206 -> decltype(f_outer(std::move(f_inner(std::get<IndexN>(p)))...)) {
207 return f_outer(std::move(f_inner(std::get<IndexN>(p)))...);
208 }
209
210 template<class F_inner, class F_outer, class... ParamN, int... IndexN>
apply_to_each(std::tuple<ParamN...> & p,values<int,IndexN...>,const F_inner & f_inner,const F_outer & f_outer)211 auto apply_to_each(std::tuple<ParamN...>& p, values<int, IndexN...>, const F_inner& f_inner, const F_outer& f_outer)
212 -> decltype(f_outer(std::move(f_inner(std::get<IndexN>(p)))...)) {
213 return f_outer(std::move(f_inner(std::get<IndexN>(p)))...);
214 }
215
216 }
217 template<class F, class... ParamN>
apply(std::tuple<ParamN...> p,F && f)218 auto apply(std::tuple<ParamN...> p, F&& f)
219 -> decltype(detail::apply(std::move(p), typename values_from<int, sizeof...(ParamN)>::type(), std::forward<F>(f))) {
220 return detail::apply(std::move(p), typename values_from<int, sizeof...(ParamN)>::type(), std::forward<F>(f));
221 }
222
223 template<class F_inner, class F_outer, class... ParamN>
apply_to_each(std::tuple<ParamN...> & p,F_inner & f_inner,F_outer & f_outer)224 auto apply_to_each(std::tuple<ParamN...>& p, F_inner& f_inner, F_outer& f_outer)
225 -> decltype(detail::apply_to_each(p, typename values_from<int, sizeof...(ParamN)>::type(), f_inner, f_outer)) {
226 return detail::apply_to_each(p, typename values_from<int, sizeof...(ParamN)>::type(), f_inner, f_outer);
227 }
228
229 template<class F_inner, class F_outer, class... ParamN>
apply_to_each(std::tuple<ParamN...> & p,const F_inner & f_inner,const F_outer & f_outer)230 auto apply_to_each(std::tuple<ParamN...>& p, const F_inner& f_inner, const F_outer& f_outer)
231 -> decltype(detail::apply_to_each(p, typename values_from<int, sizeof...(ParamN)>::type(), f_inner, f_outer)) {
232 return detail::apply_to_each(p, typename values_from<int, sizeof...(ParamN)>::type(), f_inner, f_outer);
233 }
234
235 namespace detail {
236
237 template<class F>
238 struct apply_to
239 {
240 F to;
241
apply_torxcpp::util::detail::apply_to242 explicit apply_to(F f)
243 : to(std::move(f))
244 {
245 }
246
247 template<class... ParamN>
operator ()rxcpp::util::detail::apply_to248 auto operator()(std::tuple<ParamN...> p)
249 -> decltype(rxcpp::util::apply(std::move(p), to)) {
250 return rxcpp::util::apply(std::move(p), to);
251 }
252 template<class... ParamN>
operator ()rxcpp::util::detail::apply_to253 auto operator()(std::tuple<ParamN...> p) const
254 -> decltype(rxcpp::util::apply(std::move(p), to)) {
255 return rxcpp::util::apply(std::move(p), to);
256 }
257 };
258
259 }
260
261 template<class F>
apply_to(F f)262 auto apply_to(F f)
263 -> detail::apply_to<F> {
264 return detail::apply_to<F>(std::move(f));
265 }
266
267 namespace detail {
268
269 struct pack
270 {
271 template<class... ParamN>
operator ()rxcpp::util::detail::pack272 auto operator()(ParamN... pn)
273 -> decltype(std::make_tuple(std::move(pn)...)) {
274 return std::make_tuple(std::move(pn)...);
275 }
276 template<class... ParamN>
operator ()rxcpp::util::detail::pack277 auto operator()(ParamN... pn) const
278 -> decltype(std::make_tuple(std::move(pn)...)) {
279 return std::make_tuple(std::move(pn)...);
280 }
281 };
282
283 }
284
pack()285 inline auto pack()
286 -> detail::pack {
287 return detail::pack();
288 }
289
290 namespace detail {
291
292 template<int Index>
293 struct take_at
294 {
295 template<class... ParamN>
operator ()rxcpp::util::detail::take_at296 auto operator()(ParamN... pn)
297 -> typename std::tuple_element<Index, std::tuple<decay_t<ParamN>...>>::type {
298 return std::get<Index>(std::make_tuple(std::move(pn)...));
299 }
300 template<class... ParamN>
operator ()rxcpp::util::detail::take_at301 auto operator()(ParamN... pn) const
302 -> typename std::tuple_element<Index, std::tuple<decay_t<ParamN>...>>::type {
303 return std::get<Index>(std::make_tuple(std::move(pn)...));
304 }
305 };
306
307 }
308
309 template<int Index>
take_at()310 inline auto take_at()
311 -> detail::take_at<Index> {
312 return detail::take_at<Index>();
313 }
314
315 template <class D>
316 struct resolve_type;
317
318 template <template<class... TN> class Deferred, class... AN>
319 struct defer_trait
320 {
321 template<bool R>
322 struct tag_valid {static const bool valid = true; static const bool value = R;};
323 struct tag_not_valid {static const bool valid = false; static const bool value = false;};
324 typedef Deferred<typename resolve_type<AN>::type...> resolved_type;
325 template<class... CN>
326 static auto check(int) -> tag_valid<resolved_type::value>;
327 template<class... CN>
328 static tag_not_valid check(...);
329
330 typedef decltype(check<AN...>(0)) tag_type;
331 static const bool valid = tag_type::valid;
332 static const bool value = tag_type::value;
333 static const bool not_value = valid && !value;
334 };
335
336 template <template<class... TN> class Deferred, class... AN>
337 struct defer_type
338 {
339 template<class R>
340 struct tag_valid {typedef R type; static const bool value = true;};
341 struct tag_not_valid {typedef void type; static const bool value = false;};
342 typedef Deferred<typename resolve_type<AN>::type...> resolved_type;
343 template<class... CN>
344 static auto check(int) -> tag_valid<resolved_type>;
345 template<class... CN>
346 static tag_not_valid check(...);
347
348 typedef decltype(check<AN...>(0)) tag_type;
349 typedef typename tag_type::type type;
350 static const bool value = tag_type::value;
351 };
352
353 template <template<class... TN> class Deferred, class... AN>
354 struct defer_value_type
355 {
356 template<class R>
357 struct tag_valid {typedef R type; static const bool value = true;};
358 struct tag_not_valid {typedef void type; static const bool value = false;};
359 typedef Deferred<typename resolve_type<AN>::type...> resolved_type;
360 template<class... CN>
361 static auto check(int) -> tag_valid<value_type_t<resolved_type>>;
362 template<class... CN>
363 static tag_not_valid check(...);
364
365 typedef decltype(check<AN...>(0)) tag_type;
366 typedef typename tag_type::type type;
367 static const bool value = tag_type::value;
368 };
369
370 template <template<class... TN> class Deferred, class... AN>
371 struct defer_seed_type
372 {
373 template<class R>
374 struct tag_valid {typedef R type; static const bool value = true;};
375 struct tag_not_valid {typedef void type; static const bool value = false;};
376 typedef Deferred<typename resolve_type<AN>::type...> resolved_type;
377 template<class... CN>
378 static auto check(int) -> tag_valid<typename resolved_type::seed_type>;
379 template<class... CN>
380 static tag_not_valid check(...);
381
382 typedef decltype(check<AN...>(0)) tag_type;
383 typedef typename tag_type::type type;
384 static const bool value = tag_type::value;
385 };
386
387 template <class D>
388 struct resolve_type
389 {
390 typedef D type;
391 };
392 template <template<class... TN> class Deferred, class... AN>
393 struct resolve_type<defer_type<Deferred, AN...>>
394 {
395 typedef typename defer_type<Deferred, AN...>::type type;
396 };
397 template <template<class... TN> class Deferred, class... AN>
398 struct resolve_type<defer_value_type<Deferred, AN...>>
399 {
400 typedef typename defer_value_type<Deferred, AN...>::type type;
401 };
402 template <template<class... TN> class Deferred, class... AN>
403 struct resolve_type<defer_seed_type<Deferred, AN...>>
404 {
405 typedef typename defer_seed_type<Deferred, AN...>::type type;
406 };
407
408 struct plus
409 {
410 template <class LHS, class RHS>
operator ()rxcpp::util::plus411 auto operator()(LHS&& lhs, RHS&& rhs) const
412 -> decltype(std::forward<LHS>(lhs) + std::forward<RHS>(rhs))
413 { return std::forward<LHS>(lhs) + std::forward<RHS>(rhs); }
414 };
415
416 struct count
417 {
418 template <class T>
operator ()rxcpp::util::count419 int operator()(int cnt, T&&) const
420 { return cnt + 1; }
421 };
422
423 struct less
424 {
425 template <class LHS, class RHS>
operator ()rxcpp::util::less426 auto operator()(LHS&& lhs, RHS&& rhs) const
427 -> decltype(std::forward<LHS>(lhs) < std::forward<RHS>(rhs))
428 { return std::forward<LHS>(lhs) < std::forward<RHS>(rhs); }
429 };
430
431 template <class T>
432 struct ret
433 {
434 template <class LHS>
operator ()rxcpp::util::ret435 auto operator()(LHS&& ) const
436 -> decltype(T())
437 { return T(); }
438 };
439
440 template<class T = void>
441 struct equal_to
442 {
operator ()rxcpp::util::equal_to443 bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; }
444 };
445
446 template<>
447 struct equal_to<void>
448 {
449 template<class LHS, class RHS>
operator ()rxcpp::util::equal_to450 auto operator()(LHS&& lhs, RHS&& rhs) const
451 -> decltype(std::forward<LHS>(lhs) == std::forward<RHS>(rhs))
452 { return std::forward<LHS>(lhs) == std::forward<RHS>(rhs); }
453 };
454
455 namespace detail {
456 template<class OStream, class Delimit>
457 struct print_function
458 {
459 OStream& os;
460 Delimit delimit;
print_functionrxcpp::util::detail::print_function461 print_function(OStream& os, Delimit d) : os(os), delimit(std::move(d)) {}
462
463 template<class... TN>
operator ()rxcpp::util::detail::print_function464 void operator()(const TN&... tn) const {
465 bool inserts[] = {(os << tn, true)...};
466 inserts[0] = *reinterpret_cast<bool*>(inserts); // silence warning
467 delimit();
468 }
469
470 template<class... TN>
operator ()rxcpp::util::detail::print_function471 void operator()(const std::tuple<TN...>& tpl) const {
472 rxcpp::util::apply(tpl, *this);
473 }
474 };
475
476 template<class OStream>
477 struct endline
478 {
479 OStream& os;
endlinerxcpp::util::detail::endline480 endline(OStream& os) : os(os) {}
operator ()rxcpp::util::detail::endline481 void operator()() const {
482 os << std::endl;
483 }
484 private:
485 endline& operator=(const endline&) RXCPP_DELETE;
486 };
487
488 template<class OStream, class ValueType>
489 struct insert_value
490 {
491 OStream& os;
492 ValueType value;
insert_valuerxcpp::util::detail::insert_value493 insert_value(OStream& os, ValueType v) : os(os), value(std::move(v)) {}
operator ()rxcpp::util::detail::insert_value494 void operator()() const {
495 os << value;
496 }
497 private:
498 insert_value& operator=(const insert_value&) RXCPP_DELETE;
499 };
500
501 template<class OStream, class Function>
502 struct insert_function
503 {
504 OStream& os;
505 Function call;
insert_functionrxcpp::util::detail::insert_function506 insert_function(OStream& os, Function f) : os(os), call(std::move(f)) {}
operator ()rxcpp::util::detail::insert_function507 void operator()() const {
508 call(os);
509 }
510 private:
511 insert_function& operator=(const insert_function&) RXCPP_DELETE;
512 };
513
514 template<class OStream, class Delimit>
print_followed_with(OStream & os,Delimit d)515 auto print_followed_with(OStream& os, Delimit d)
516 -> detail::print_function<OStream, Delimit> {
517 return detail::print_function<OStream, Delimit>(os, std::move(d));
518 }
519
520 }
521
522 template<class OStream>
endline(OStream & os)523 auto endline(OStream& os)
524 -> detail::endline<OStream> {
525 return detail::endline<OStream>(os);
526 }
527
528 template<class OStream>
println(OStream & os)529 auto println(OStream& os)
530 -> decltype(detail::print_followed_with(os, endline(os))) {
531 return detail::print_followed_with(os, endline(os));
532 }
533 template<class OStream, class Delimit>
print_followed_with(OStream & os,Delimit d)534 auto print_followed_with(OStream& os, Delimit d)
535 -> decltype(detail::print_followed_with(os, detail::insert_function<OStream, Delimit>(os, std::move(d)))) {
536 return detail::print_followed_with(os, detail::insert_function<OStream, Delimit>(os, std::move(d)));
537 }
538 template<class OStream, class DelimitValue>
print_followed_by(OStream & os,DelimitValue dv)539 auto print_followed_by(OStream& os, DelimitValue dv)
540 -> decltype(detail::print_followed_with(os, detail::insert_value<OStream, DelimitValue>(os, std::move(dv)))) {
541 return detail::print_followed_with(os, detail::insert_value<OStream, DelimitValue>(os, std::move(dv)));
542 }
543
what(std::exception_ptr ep)544 inline std::string what(std::exception_ptr ep) {
545 #if RXCPP_USE_EXCEPTIONS
546 try {std::rethrow_exception(ep);}
547 catch (const std::exception& ex) {
548 return ex.what();
549 } catch (...) {
550 return std::string("<not derived from std::exception>");
551 }
552 #endif
553 (void)ep;
554 return std::string("<exceptions are disabled>");
555 }
556
557 namespace detail {
558
559 template <class T>
560 class maybe
561 {
562 bool is_set;
563 typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type
564 storage;
565 public:
maybe()566 maybe()
567 : is_set(false)
568 {
569 }
570
maybe(T value)571 maybe(T value)
572 : is_set(false)
573 {
574 new (reinterpret_cast<T*>(&storage)) T(value);
575 is_set = true;
576 }
577
maybe(const maybe & other)578 maybe(const maybe& other)
579 : is_set(false)
580 {
581 if (other.is_set) {
582 new (reinterpret_cast<T*>(&storage)) T(other.get());
583 is_set = true;
584 }
585 }
maybe(maybe && other)586 maybe(maybe&& other)
587 : is_set(false)
588 {
589 if (other.is_set) {
590 new (reinterpret_cast<T*>(&storage)) T(std::move(other.get()));
591 is_set = true;
592 other.reset();
593 }
594 }
595
~maybe()596 ~maybe()
597 {
598 reset();
599 }
600
601 typedef T value_type;
602 typedef T* iterator;
603 typedef const T* const_iterator;
604
empty() const605 bool empty() const {
606 return !is_set;
607 }
608
size() const609 std::size_t size() const {
610 return is_set ? 1 : 0;
611 }
612
begin()613 iterator begin() {
614 return reinterpret_cast<T*>(&storage);
615 }
begin() const616 const_iterator begin() const {
617 return reinterpret_cast<T*>(&storage);
618 }
619
end()620 iterator end() {
621 return reinterpret_cast<T*>(&storage) + size();
622 }
end() const623 const_iterator end() const {
624 return reinterpret_cast<T*>(&storage) + size();
625 }
626
operator ->()627 T* operator->() {
628 if (!is_set) std::terminate();
629 return reinterpret_cast<T*>(&storage);
630 }
operator ->() const631 const T* operator->() const {
632 if (!is_set) std::terminate();
633 return reinterpret_cast<T*>(&storage);
634 }
635
operator *()636 T& operator*() {
637 if (!is_set) std::terminate();
638 return *reinterpret_cast<T*>(&storage);
639 }
operator *() const640 const T& operator*() const {
641 if (!is_set) std::terminate();
642 return *reinterpret_cast<T*>(&storage);
643 }
644
get()645 T& get() {
646 if (!is_set) std::terminate();
647 return *reinterpret_cast<T*>(&storage);
648 }
get() const649 const T& get() const {
650 if (!is_set) std::terminate();
651 return *reinterpret_cast<const T*>(&storage);
652 }
653
reset()654 void reset()
655 {
656 if (is_set) {
657 is_set = false;
658 reinterpret_cast<T*>(&storage)->~T();
659 //std::fill_n(reinterpret_cast<char*>(&storage), sizeof(T), 0);
660 }
661 }
662
663 template<class U>
reset(U && value)664 void reset(U&& value) {
665 reset();
666 new (reinterpret_cast<T*>(&storage)) T(std::forward<U>(value));
667 is_set = true;
668 }
669
operator =(const T & other)670 maybe& operator=(const T& other) {
671 reset(other);
672 return *this;
673 }
operator =(const maybe & other)674 maybe& operator=(const maybe& other) {
675 if (!other.empty()) {
676 reset(other.get());
677 } else {
678 reset();
679 }
680 return *this;
681 }
682 };
683
684 }
685 using detail::maybe;
686
687 namespace detail {
688 struct surely
689 {
690 template<class... T>
operator ()rxcpp::util::detail::surely691 auto operator()(T... t)
692 -> decltype(std::make_tuple(t.get()...)) {
693 return std::make_tuple(t.get()...);
694 }
695 template<class... T>
operator ()rxcpp::util::detail::surely696 auto operator()(T... t) const
697 -> decltype(std::make_tuple(t.get()...)) {
698 return std::make_tuple(t.get()...);
699 }
700 };
701 }
702
703 template<class... T>
surely(const std::tuple<T...> & tpl)704 inline auto surely(const std::tuple<T...>& tpl)
705 -> decltype(apply(tpl, detail::surely())) {
706 return apply(tpl, detail::surely());
707 }
708
709 namespace detail {
710
711 template<typename Function>
712 class unwinder
713 {
714 public:
~unwinder()715 ~unwinder()
716 {
717 if (!!function)
718 {
719 RXCPP_TRY {
720 (*function)();
721 } RXCPP_CATCH(...) {
722 std::terminate();
723 }
724 }
725 }
726
unwinder(Function * functionArg)727 explicit unwinder(Function* functionArg)
728 : function(functionArg)
729 {
730 }
731
dismiss()732 void dismiss()
733 {
734 function = nullptr;
735 }
736
737 private:
738 unwinder();
739 unwinder(const unwinder&);
740 unwinder& operator=(const unwinder&);
741
742 Function* function;
743 };
744
745 }
746
747 #if !defined(RXCPP_THREAD_LOCAL)
748 template<typename T>
749 class thread_local_storage
750 {
751 private:
752 pthread_key_t key;
753
754 public:
thread_local_storage()755 thread_local_storage()
756 {
757 pthread_key_create(&key, NULL);
758 }
759
~thread_local_storage()760 ~thread_local_storage()
761 {
762 pthread_key_delete(key);
763 }
764
operator =(T * p)765 thread_local_storage& operator =(T* p)
766 {
767 pthread_setspecific(key, p);
768 return *this;
769 }
770
operator !()771 bool operator !()
772 {
773 return pthread_getspecific(key) == NULL;
774 }
775
operator ->()776 T* operator ->()
777 {
778 return static_cast<T*>(pthread_getspecific(key));
779 }
780
get()781 T* get()
782 {
783 return static_cast<T*>(pthread_getspecific(key));
784 }
785 };
786 #endif
787
788 template<typename, typename C = types_checked>
789 struct is_string : std::false_type {
790 };
791
792 template <typename T>
793 struct is_string<T,
794 typename types_checked_from<
795 typename T::value_type,
796 typename T::traits_type,
797 typename T::allocator_type>::type>
798 : std::is_base_of<
799 std::basic_string<
800 typename T::value_type,
801 typename T::traits_type,
802 typename T::allocator_type>, T> {
803 };
804
805 namespace detail {
806
807 template <class T, class = types_checked>
808 struct is_duration : std::false_type {};
809
810 template <class T>
811 struct is_duration<T, types_checked_t<T, typename T::rep, typename T::period>>
812 : std::is_convertible<T*, std::chrono::duration<typename T::rep, typename T::period>*> {};
813
814 }
815
816 template <class T, class Decayed = decay_t<T>>
817 struct is_duration : detail::is_duration<Decayed> {};
818
819
820 // C++17 negation
821 namespace detail {
822 template<class T>
823 struct not_value : std::conditional<T::value, std::false_type, std::true_type>::type {
824 };
825 }
826
827 template <class T>
828 struct negation : detail::not_value<T> {};
829
830 }
831
832 #if !RXCPP_USE_EXCEPTIONS
833 namespace util {
834
835 namespace detail {
836
837 struct error_base {
838 virtual const char* what() = 0;
~error_baserxcpp::util::detail::error_base839 virtual ~error_base() {}
840 };
841
842 // Use the "Type Erasure" idiom to wrap an std::exception-like
843 // value into an error pointer.
844 //
845 // Supported types:
846 // exception, bad_exception, bad_alloc.
847 template <class E>
848 struct error_specific : public error_base {
error_specificrxcpp::util::detail::error_specific849 error_specific(const E& e) : data(e) {}
error_specificrxcpp::util::detail::error_specific850 error_specific(E&& e) : data(std::move(e)) {}
851
~error_specificrxcpp::util::detail::error_specific852 virtual ~error_specific() {}
853
whatrxcpp::util::detail::error_specific854 virtual const char* what() {
855 return data.what();
856 }
857
858 E data;
859 };
860
861 }
862
863 }
864 #endif
865
866 namespace util {
867
868 #if RXCPP_USE_EXCEPTIONS
869 using error_ptr = std::exception_ptr;
870 #else
871 // Note: std::exception_ptr cannot be used directly when exceptions are disabled.
872 // Any attempt to 'throw' or to call into any of the std functions accepting
873 // an std::exception_ptr will either fail to compile or result in an abort at runtime.
874 using error_ptr = std::shared_ptr<util::detail::error_base>;
875
876 inline std::string what(error_ptr ep) {
877 return std::string(ep->what());
878 }
879 #endif
880
881 // TODO: Do we really need an identity make?
882 // (It was causing some compilation errors deep inside templates).
make_error_ptr(error_ptr e)883 inline error_ptr make_error_ptr(error_ptr e) {
884 return e;
885 }
886
887 // Replace std::make_exception_ptr (which would immediately terminate
888 // when exceptions are disabled).
889 template <class E>
make_error_ptr(E && e)890 error_ptr make_error_ptr(E&& e) {
891 #if RXCPP_USE_EXCEPTIONS
892 return std::make_exception_ptr(std::forward<E>(e));
893 #else
894 using e_type = rxcpp::util::decay_t<E>;
895 using pointed_to_type = rxcpp::util::detail::error_specific<e_type>;
896 auto sp = std::make_shared<pointed_to_type>(std::forward<E>(e));
897 return std::static_pointer_cast<rxcpp::util::detail::error_base>(sp);
898 #endif
899 }
900
901 // Replace std::rethrow_exception to be compatible with our error_ptr typedef.
rethrow_exception(error_ptr e)902 RXCPP_NORETURN inline void rethrow_exception(error_ptr e) {
903 #if RXCPP_USE_EXCEPTIONS
904 std::rethrow_exception(e);
905 #else
906 // error_ptr != std::exception_ptr so we can't use std::rethrow_exception
907 //
908 // However even if we could, calling std::rethrow_exception just terminates if exceptions are disabled.
909 //
910 // Therefore this function should only be called when we are completely giving up and have no idea
911 // how to handle the error.
912 (void)e;
913 std::terminate();
914 #endif
915 }
916
917 // A replacement for the "throw" keyword which is illegal when
918 // exceptions are disabled with -fno-exceptions.
919 template <typename E>
throw_exception(E && e)920 RXCPP_NORETURN inline void throw_exception(E&& e) {
921 #if RXCPP_USE_EXCEPTIONS
922 throw std::forward<E>(e);
923 #else
924 // "throw" keyword is unsupported when exceptions are disabled.
925 // Immediately terminate instead.
926 (void)e;
927 std::terminate();
928 #endif
929 }
930
931 // TODO: Do we really need this? rxu::rethrow_exception(rxu::current_exception())
932 // would have the same semantics in either case.
rethrow_current_exception()933 RXCPP_NORETURN inline void rethrow_current_exception() {
934 #if RXCPP_USE_EXCEPTIONS
935 std::rethrow_exception(std::current_exception());
936 #else
937 std::terminate();
938 #endif
939 }
940
941 // If called during exception handling, return the currently caught exception.
942 // Otherwise return null.
current_exception()943 inline error_ptr current_exception() {
944 #if RXCPP_USE_EXCEPTIONS
945 return std::current_exception();
946 #else
947 // When exceptions are disabled, we can never be inside of a catch block.
948 // Return null similar to std::current_exception returning null outside of catch.
949 return nullptr;
950 #endif
951 }
952
953 }
954 namespace rxu=util;
955
956
957 //
958 // due to an noisy static_assert issue in more than one std lib impl,
959 // rxcpp maintains a whitelist filter for the types that are allowed
960 // to be hashed. this allows is_hashable<T> to work.
961 //
962 // NOTE: this should eventually be removed!
963 //
964 template <class T, typename = void>
965 struct filtered_hash;
966
967 #if RXCPP_HASH_ENUM
968 template <class T>
969 struct filtered_hash<T, typename std::enable_if<std::is_enum<T>::value>::type> : std::hash<T> {
970 };
971 #elif RXCPP_HASH_ENUM_UNDERLYING
972 template <class T>
973 struct filtered_hash<T, typename std::enable_if<std::is_enum<T>::value>::type> : std::hash<typename std::underlying_type<T>::type> {
974 };
975 #endif
976
977 template <class T>
978 struct filtered_hash<T, typename std::enable_if<std::is_integral<T>::value>::type> : std::hash<T> {
979 };
980 template <class T>
981 struct filtered_hash<T, typename std::enable_if<std::is_pointer<T>::value>::type> : std::hash<T> {
982 };
983 template <class T>
984 struct filtered_hash<T, typename std::enable_if<rxu::is_string<T>::value>::type> : std::hash<T> {
985 };
986 template <class T>
987 struct filtered_hash<T, typename std::enable_if<std::is_convertible<T, std::chrono::duration<typename T::rep, typename T::period>>::value>::type> {
988 using argument_type = T;
989 using result_type = std::size_t;
990
operator ()rxcpp::filtered_hash991 result_type operator()(argument_type const & dur) const
992 {
993 return std::hash<typename argument_type::rep>{}(dur.count());
994 }
995 };
996 template <class T>
997 struct filtered_hash<T, typename std::enable_if<std::is_convertible<T, std::chrono::time_point<typename T::clock, typename T::duration>>::value>::type> {
998 using argument_type = T;
999 using result_type = std::size_t;
1000
operator ()rxcpp::filtered_hash1001 result_type operator()(argument_type const & tp) const
1002 {
1003 return std::hash<typename argument_type::rep>{}(tp.time_since_epoch().count());
1004 }
1005 };
1006
1007 template<typename, typename C = rxu::types_checked>
1008 struct is_hashable
1009 : std::false_type {};
1010
1011 template<typename T>
1012 struct is_hashable<T,
1013 typename rxu::types_checked_from<
1014 typename filtered_hash<T>::result_type,
1015 typename filtered_hash<T>::argument_type,
1016 typename std::result_of<filtered_hash<T>(T)>::type>::type>
1017 : std::true_type {};
1018
1019 }
1020
1021 #define RXCPP_UNWIND(Name, Function) \
1022 RXCPP_UNWIND_EXPLICIT(uwfunc_ ## Name, Name, Function)
1023
1024 #define RXCPP_UNWIND_AUTO(Function) \
1025 RXCPP_UNWIND_EXPLICIT(RXCPP_MAKE_IDENTIFIER(uwfunc_), RXCPP_MAKE_IDENTIFIER(unwind_), Function)
1026
1027 #define RXCPP_UNWIND_EXPLICIT(FunctionName, UnwinderName, Function) \
1028 auto FunctionName = (Function); \
1029 rxcpp::util::detail::unwinder<decltype(FunctionName)> UnwinderName(std::addressof(FunctionName))
1030
1031 #endif
1032