function_ref.hpp 12 KB

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