function_ref.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. #ifndef BOOST_COMPAT_FUNCTION_REF_HPP_INCLUDED
  2. #define BOOST_COMPAT_FUNCTION_REF_HPP_INCLUDED
  3. // Copyright 2024 Christian Mazakas.
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // https://www.boost.org/LICENSE_1_0.txt
  6. #include <boost/compat/detail/nontype.hpp>
  7. #include <boost/compat/invoke.hpp>
  8. #include <boost/compat/type_traits.hpp>
  9. #include <type_traits>
  10. #include <utility>
  11. namespace boost {
  12. namespace compat {
  13. template <class... S>
  14. struct function_ref;
  15. namespace detail {
  16. template <bool NoEx>
  17. union thunk_storage {
  18. void* pobj_;
  19. void (*pfn_)() noexcept(NoEx);
  20. };
  21. template <bool NoEx, class Fp, class R, class... Args>
  22. struct invoke_function_holder {
  23. static R invoke_function(thunk_storage<NoEx> s, Args&&... args) noexcept(NoEx) {
  24. auto f = reinterpret_cast<Fp>(s.pfn_);
  25. return compat::invoke_r<R>(f, std::forward<Args>(args)...);
  26. }
  27. };
  28. template <bool Const, bool NoEx, class F, class R, class... Args>
  29. struct invoke_object_holder {
  30. static R invoke_object(thunk_storage<NoEx> s, Args&&... args) noexcept(NoEx) {
  31. using T = remove_reference_t<F>;
  32. using cv_T = conditional_t<Const, add_const_t<T>, T>;
  33. return compat::invoke_r<R>(*static_cast<cv_T*>(s.pobj_), std::forward<Args>(args)...);
  34. }
  35. };
  36. template <class F, F f, bool Const, bool NoEx, class R, class... Args>
  37. struct invoke_mem_fn_holder {
  38. static R invoke_mem_fn(thunk_storage<NoEx> /* s */, Args&&... args) noexcept(NoEx) {
  39. return compat::invoke_r<R>(f, std::forward<Args>(args)...);
  40. }
  41. };
  42. template <class F, F f, class U, bool Const, bool NoEx, class R, class... Args>
  43. struct invoke_target_mem_fn_holder {
  44. static R invoke_mem_fn(thunk_storage<NoEx> s, Args&&... args) noexcept(NoEx) {
  45. using T = remove_reference_t<U>;
  46. using cv_T = conditional_t<Const, add_const_t<T>, T>;
  47. return compat::invoke_r<R>(f, *static_cast<cv_T*>(s.pobj_), std::forward<Args>(args)...);
  48. }
  49. };
  50. template <class F, F f, class T, bool Const, bool NoEx, class R, class... Args>
  51. struct invoke_ptr_mem_fn_holder {
  52. static R invoke_mem_fn(thunk_storage<NoEx> s, Args&&... args) noexcept(NoEx) {
  53. using cv_T = conditional_t<Const, add_const_t<T>, T>;
  54. return compat::invoke_r<R>(f, static_cast<cv_T*>(s.pobj_), std::forward<Args>(args)...);
  55. }
  56. };
  57. template <bool Const, bool NoEx, class R, class... Args>
  58. struct function_ref_base {
  59. private:
  60. thunk_storage<NoEx> thunk_ = nullptr;
  61. R (*invoke_)(thunk_storage<NoEx>, Args&&...) noexcept(NoEx) = nullptr;
  62. public:
  63. struct fp_tag {};
  64. struct obj_tag {};
  65. struct mem_fn_tag {};
  66. template <class F>
  67. function_ref_base(fp_tag, F* fn) noexcept
  68. : thunk_{}, invoke_(&invoke_function_holder<NoEx, F*, R, Args...>::invoke_function) {
  69. thunk_.pfn_ = reinterpret_cast<decltype(thunk_.pfn_)>(fn);
  70. }
  71. template <class F>
  72. function_ref_base(obj_tag, F&& fn) noexcept
  73. : thunk_{}, invoke_(&invoke_object_holder<Const, NoEx, F, R, Args...>::invoke_object) {
  74. thunk_.pobj_ = const_cast<void*>(static_cast<void const*>(std::addressof(fn)));
  75. }
  76. template <class F, F f>
  77. function_ref_base(mem_fn_tag, nttp_holder<F, f>)
  78. : thunk_{}, invoke_(&invoke_mem_fn_holder<F, f, Const, NoEx, R, Args...>::invoke_mem_fn) {
  79. thunk_.pobj_ = nullptr;
  80. }
  81. template <class F, F f, class U>
  82. function_ref_base(mem_fn_tag, nttp_holder<F, f>, U&& obj)
  83. : thunk_{}, invoke_(&invoke_target_mem_fn_holder<F, f, U, Const, NoEx, R, Args...>::invoke_mem_fn) {
  84. thunk_.pobj_ = const_cast<void*>(static_cast<void const*>(std::addressof(obj)));
  85. }
  86. template <class F, F f, class T>
  87. function_ref_base(mem_fn_tag, nttp_holder<F, f>, T* obj)
  88. : thunk_{}, invoke_(&invoke_ptr_mem_fn_holder<F, f, T, Const, NoEx, R, Args...>::invoke_mem_fn) {
  89. thunk_.pobj_ = const_cast<void*>(static_cast<void const*>(obj));
  90. }
  91. function_ref_base(const function_ref_base&) noexcept = default;
  92. function_ref_base& operator=(const function_ref_base&) noexcept = default;
  93. R operator()(Args... args) const noexcept(NoEx) { return this->invoke_(thunk_, std::forward<Args>(args)...); }
  94. };
  95. } // namespace detail
  96. template <class R, class... Args>
  97. struct function_ref<R(Args...)> : public detail::function_ref_base<false, false, R, Args...> {
  98. private:
  99. using base_type = detail::function_ref_base<false, false, R, Args...>;
  100. using typename base_type::fp_tag;
  101. using typename base_type::mem_fn_tag;
  102. using typename base_type::obj_tag;
  103. template <class... T>
  104. using is_invocable_using = boost::compat::is_invocable_r<R, T..., Args...>;
  105. public:
  106. template <class F, enable_if_t<std::is_function<F>::value && is_invocable_using<F>::value, int> = 0>
  107. function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {}
  108. template <class F, class T = remove_reference_t<F>,
  109. enable_if_t<!std::is_same<remove_cvref_t<F>, function_ref>::value && !std::is_member_pointer<T>::value &&
  110. is_invocable_using<T&>::value,
  111. int> = 0>
  112. function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}
  113. template <class F, F f, enable_if_t<is_invocable_using<F>::value, int> = 0>
  114. function_ref(detail::nttp_holder<F, f> x) noexcept : base_type(mem_fn_tag{}, x) {}
  115. template <class F, F f, class U, class T = remove_reference_t<U>,
  116. enable_if_t<!std::is_rvalue_reference<U&&>::value && is_invocable_using<F, T&>::value, int> = 0>
  117. function_ref(detail::nttp_holder<F, f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}
  118. template <class F, F f, class T, enable_if_t<is_invocable_using<F, T*>::value, int> = 0>
  119. function_ref(detail::nttp_holder<F, f> x, T* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}
  120. function_ref(const function_ref&) noexcept = default;
  121. function_ref& operator=(const function_ref&) noexcept = default;
  122. template <class T, enable_if_t<!std::is_same<T, function_ref>::value && !std::is_pointer<T>::value, int> = 0>
  123. function_ref& operator=(T) = delete;
  124. };
  125. template <class R, class... Args>
  126. struct function_ref<R(Args...) const> : public detail::function_ref_base<true, false, R, Args...> {
  127. private:
  128. using base_type = detail::function_ref_base<true, false, R, Args...>;
  129. using typename base_type::fp_tag;
  130. using typename base_type::mem_fn_tag;
  131. using typename base_type::obj_tag;
  132. template <class... T>
  133. using is_invocable_using = boost::compat::is_invocable_r<R, T..., Args...>;
  134. public:
  135. template <class F, enable_if_t<std::is_function<F>::value && is_invocable_using<F>::value, int> = 0>
  136. function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {}
  137. template <class F, class T = remove_reference_t<F>,
  138. enable_if_t<!std::is_same<remove_cvref_t<F>, function_ref>::value && !std::is_member_pointer<T>::value &&
  139. is_invocable_using<T const&>::value,
  140. int> = 0>
  141. function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}
  142. template <class F, F f, enable_if_t<is_invocable_using<F>::value, int> = 0>
  143. function_ref(detail::nttp_holder<F, f> x) noexcept : base_type(mem_fn_tag{}, x) {}
  144. template <class F, F f, class U, class T = remove_reference_t<U>,
  145. enable_if_t<!std::is_rvalue_reference<U&&>::value && is_invocable_using<F, T const&>::value, int> = 0>
  146. function_ref(detail::nttp_holder<F, f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}
  147. template <class F, F f, class T, enable_if_t<is_invocable_using<F, T const*>::value, int> = 0>
  148. function_ref(detail::nttp_holder<F, f> x, T const* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}
  149. function_ref(const function_ref&) noexcept = default;
  150. function_ref& operator=(const function_ref&) noexcept = default;
  151. template <class T, enable_if_t<!std::is_same<T, function_ref>::value && !std::is_pointer<T>::value, int> = 0>
  152. function_ref& operator=(T) = delete;
  153. };
  154. #if defined(__cpp_noexcept_function_type)
  155. template <class R, class... Args>
  156. struct function_ref<R(Args...) noexcept> : public detail::function_ref_base<false, true, R, Args...> {
  157. private:
  158. using base_type = detail::function_ref_base<false, true, R, Args...>;
  159. using typename base_type::fp_tag;
  160. using typename base_type::mem_fn_tag;
  161. using typename base_type::obj_tag;
  162. template <class... T>
  163. using is_invocable_using = boost::compat::is_nothrow_invocable_r<R, T..., Args...>;
  164. public:
  165. template <class F, enable_if_t<std::is_function<F>::value && is_invocable_using<F>::value, int> = 0>
  166. function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {}
  167. template <class F, class T = remove_reference_t<F>,
  168. enable_if_t<!std::is_same<remove_cvref_t<F>, function_ref>::value && !std::is_member_pointer<T>::value &&
  169. is_invocable_using<T&>::value,
  170. int> = 0>
  171. function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}
  172. template <class F, F f, enable_if_t<is_invocable_using<F>::value, int> = 0>
  173. function_ref(detail::nttp_holder<F, f> x) noexcept : base_type(mem_fn_tag{}, x) {}
  174. template <class F, F f, class U, class T = remove_reference_t<U>,
  175. enable_if_t<!std::is_rvalue_reference<U&&>::value && is_invocable_using<F, T&>::value, int> = 0>
  176. function_ref(detail::nttp_holder<F, f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}
  177. template <class F, F f, class T, enable_if_t<is_invocable_using<F, T*>::value, int> = 0>
  178. function_ref(detail::nttp_holder<F, f> x, T* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}
  179. function_ref(const function_ref&) noexcept = default;
  180. function_ref& operator=(const function_ref&) noexcept = default;
  181. template <class T, enable_if_t<!std::is_same<T, function_ref>::value && !std::is_pointer<T>::value, int> = 0>
  182. function_ref& operator=(T) = delete;
  183. };
  184. template <class R, class... Args>
  185. struct function_ref<R(Args...) const noexcept> : public detail::function_ref_base<true, true, R, Args...> {
  186. private:
  187. using base_type = detail::function_ref_base<true, true, R, Args...>;
  188. using typename base_type::fp_tag;
  189. using typename base_type::mem_fn_tag;
  190. using typename base_type::obj_tag;
  191. template <class... T>
  192. using is_invocable_using = boost::compat::is_nothrow_invocable_r<R, T..., Args...>;
  193. public:
  194. template <class F, enable_if_t<std::is_function<F>::value && is_invocable_using<F>::value, int> = 0>
  195. function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {}
  196. template <class F, class T = remove_reference_t<F>,
  197. enable_if_t<!std::is_same<remove_cvref_t<F>, function_ref>::value && !std::is_member_pointer<T>::value &&
  198. is_invocable_using<T const&>::value,
  199. int> = 0>
  200. function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}
  201. template <class F, F f, enable_if_t<is_invocable_using<F>::value, int> = 0>
  202. function_ref(detail::nttp_holder<F, f> x) noexcept : base_type(mem_fn_tag{}, x) {}
  203. template <class F, F f, class U, class T = remove_reference_t<U>,
  204. enable_if_t<!std::is_rvalue_reference<U&&>::value && is_invocable_using<F, T const&>::value, int> = 0>
  205. function_ref(detail::nttp_holder<F, f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}
  206. template <class F, F f, class T, enable_if_t<is_invocable_using<F, T const*>::value, int> = 0>
  207. function_ref(detail::nttp_holder<F, f> x, T const* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}
  208. function_ref(const function_ref&) noexcept = default;
  209. function_ref& operator=(const function_ref&) noexcept = default;
  210. template <class T, enable_if_t<!std::is_same<T, function_ref>::value && !std::is_pointer<T>::value, int> = 0>
  211. function_ref& operator=(T) = delete;
  212. };
  213. #endif
  214. } // namespace compat
  215. } // namespace boost
  216. #endif // #ifndef BOOST_COMPAT_FUNCTION_REF_HPP_INCLUDED