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> 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> 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> 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> 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> 133 bool operator()(Value0 v0) const { 134 return v0; 135 } 136 137 template<class Value0, class... ValueN> 138 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> 148 bool operator()(Value0 v0) const { 149 return v0; 150 } 151 152 template<class Value0, class... ValueN> 153 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> 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> 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> 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> 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> 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> 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 242 explicit apply_to(F f) 243 : to(std::move(f)) 244 { 245 } 246 247 template<class... ParamN> 248 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> 253 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> 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> 272 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> 277 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 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> 296 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> 301 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> 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> 411 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> 419 int operator()(int cnt, T&&) const 420 { return cnt + 1; } 421 }; 422 423 struct less 424 { 425 template <class LHS, class RHS> 426 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> 435 auto operator()(LHS&& ) const 436 -> decltype(T()) 437 { return T(); } 438 }; 439 440 template<class T = void> 441 struct equal_to 442 { 443 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> 450 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; 461 print_function(OStream& os, Delimit d) : os(os), delimit(std::move(d)) {} 462 463 template<class... TN> 464 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> 471 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; 480 endline(OStream& os) : os(os) {} 481 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; 493 insert_value(OStream& os, ValueType v) : os(os), value(std::move(v)) {} 494 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; 506 insert_function(OStream& os, Function f) : os(os), call(std::move(f)) {} 507 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> 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> 523 auto endline(OStream& os) 524 -> detail::endline<OStream> { 525 return detail::endline<OStream>(os); 526 } 527 528 template<class OStream> 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> 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> 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 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: 566 maybe() 567 : is_set(false) 568 { 569 } 570 571 maybe(T value) 572 : is_set(false) 573 { 574 new (reinterpret_cast<T*>(&storage)) T(value); 575 is_set = true; 576 } 577 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 } 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 596 ~maybe() 597 { 598 reset(); 599 } 600 601 typedef T value_type; 602 typedef T* iterator; 603 typedef const T* const_iterator; 604 605 bool empty() const { 606 return !is_set; 607 } 608 609 std::size_t size() const { 610 return is_set ? 1 : 0; 611 } 612 613 iterator begin() { 614 return reinterpret_cast<T*>(&storage); 615 } 616 const_iterator begin() const { 617 return reinterpret_cast<T*>(&storage); 618 } 619 620 iterator end() { 621 return reinterpret_cast<T*>(&storage) + size(); 622 } 623 const_iterator end() const { 624 return reinterpret_cast<T*>(&storage) + size(); 625 } 626 627 T* operator->() { 628 if (!is_set) std::terminate(); 629 return reinterpret_cast<T*>(&storage); 630 } 631 const T* operator->() const { 632 if (!is_set) std::terminate(); 633 return reinterpret_cast<T*>(&storage); 634 } 635 636 T& operator*() { 637 if (!is_set) std::terminate(); 638 return *reinterpret_cast<T*>(&storage); 639 } 640 const T& operator*() const { 641 if (!is_set) std::terminate(); 642 return *reinterpret_cast<T*>(&storage); 643 } 644 645 T& get() { 646 if (!is_set) std::terminate(); 647 return *reinterpret_cast<T*>(&storage); 648 } 649 const T& get() const { 650 if (!is_set) std::terminate(); 651 return *reinterpret_cast<const T*>(&storage); 652 } 653 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> 664 void reset(U&& value) { 665 reset(); 666 new (reinterpret_cast<T*>(&storage)) T(std::forward<U>(value)); 667 is_set = true; 668 } 669 670 maybe& operator=(const T& other) { 671 reset(other); 672 return *this; 673 } 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> 691 auto operator()(T... t) 692 -> decltype(std::make_tuple(t.get()...)) { 693 return std::make_tuple(t.get()...); 694 } 695 template<class... T> 696 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> 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: 715 ~unwinder() 716 { 717 if (!!function) 718 { 719 RXCPP_TRY { 720 (*function)(); 721 } RXCPP_CATCH(...) { 722 std::terminate(); 723 } 724 } 725 } 726 727 explicit unwinder(Function* functionArg) 728 : function(functionArg) 729 { 730 } 731 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: 755 thread_local_storage() 756 { 757 pthread_key_create(&key, NULL); 758 } 759 760 ~thread_local_storage() 761 { 762 pthread_key_delete(key); 763 } 764 765 thread_local_storage& operator =(T* p) 766 { 767 pthread_setspecific(key, p); 768 return *this; 769 } 770 771 bool operator !() 772 { 773 return pthread_getspecific(key) == NULL; 774 } 775 776 T* operator ->() 777 { 778 return static_cast<T*>(pthread_getspecific(key)); 779 } 780 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; 839 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 { 849 error_specific(const E& e) : data(e) {} 850 error_specific(E&& e) : data(std::move(e)) {} 851 852 virtual ~error_specific() {} 853 854 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). 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> 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. 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> 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. 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. 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 991 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 1001 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