File indexing completed on 2025-04-26 08:26:31
0001 #ifndef BOOST_COMPAT_FUNCTION_REF_HPP_INCLUDED
0002 #define BOOST_COMPAT_FUNCTION_REF_HPP_INCLUDED
0003
0004
0005
0006
0007
0008 #include <boost/compat/invoke.hpp>
0009 #include <boost/compat/type_traits.hpp>
0010
0011 #include <type_traits>
0012 #include <utility>
0013
0014 #if defined(__cpp_nontype_template_parameter_auto) && __cpp_nontype_template_parameter_auto >= 201606L
0015 #define BOOST_COMPAT_HAS_AUTO_NTTP
0016 #endif
0017
0018 namespace boost {
0019 namespace compat {
0020
0021 template <class... S>
0022 struct function_ref;
0023
0024 #if defined(BOOST_COMPAT_HAS_AUTO_NTTP)
0025
0026 template <auto V>
0027 struct nontype_t {
0028 explicit nontype_t() = default;
0029 };
0030
0031 #endif
0032
0033 namespace detail {
0034
0035 template <bool NoEx>
0036 union thunk_storage {
0037 void* pobj_;
0038 void (*pfn_)() noexcept(NoEx);
0039 };
0040
0041 template <bool NoEx, class Fp, class R, class... Args>
0042 struct invoke_function_holder {
0043 static R invoke_function(thunk_storage<NoEx> s, Args&&... args) noexcept(NoEx) {
0044 auto f = reinterpret_cast<Fp>(s.pfn_);
0045 return compat::invoke_r<R>(f, std::forward<Args>(args)...);
0046 }
0047 };
0048
0049 template <bool Const, bool NoEx, class F, class R, class... Args>
0050 struct invoke_object_holder {
0051 static R invoke_object(thunk_storage<NoEx> s, Args&&... args) noexcept(NoEx) {
0052 using T = remove_reference_t<F>;
0053 using cv_T = conditional_t<Const, add_const_t<T>, T>;
0054 return compat::invoke_r<R>(*static_cast<cv_T*>(s.pobj_), std::forward<Args>(args)...);
0055 }
0056 };
0057
0058 #if defined(BOOST_COMPAT_HAS_AUTO_NTTP)
0059
0060 template <auto f, bool Const, bool NoEx, class R, class... Args>
0061 struct invoke_mem_fn_holder {
0062 static R invoke_mem_fn(thunk_storage<NoEx> , Args&&... args) noexcept(NoEx) {
0063 return compat::invoke_r<R>(f, std::forward<Args>(args)...);
0064 }
0065 };
0066
0067 template <auto f, class U, bool Const, bool NoEx, class R, class... Args>
0068 struct invoke_target_mem_fn_holder {
0069 static R invoke_mem_fn(thunk_storage<NoEx> s, Args&&... args) noexcept(NoEx) {
0070 using T = remove_reference_t<U>;
0071 using cv_T = conditional_t<Const, add_const_t<T>, T>;
0072 return compat::invoke_r<R>(f, *static_cast<cv_T*>(s.pobj_), std::forward<Args>(args)...);
0073 }
0074 };
0075
0076 template <auto f, class T, bool Const, bool NoEx, class R, class... Args>
0077 struct invoke_ptr_mem_fn_holder {
0078 static R invoke_mem_fn(thunk_storage<NoEx> s, Args&&... args) noexcept(NoEx) {
0079 using cv_T = conditional_t<Const, add_const_t<T>, T>;
0080 return compat::invoke_r<R>(f, static_cast<cv_T*>(s.pobj_), std::forward<Args>(args)...);
0081 }
0082 };
0083
0084 #endif
0085
0086 template <bool Const, bool NoEx, class R, class... Args>
0087 struct function_ref_base {
0088 private:
0089 thunk_storage<NoEx> thunk_ = nullptr;
0090 R (*invoke_)(thunk_storage<NoEx>, Args&&...) noexcept(NoEx) = nullptr;
0091
0092 public:
0093 struct fp_tag {};
0094 struct obj_tag {};
0095 struct mem_fn_tag {};
0096
0097 template <class F>
0098 function_ref_base(fp_tag, F* fn) noexcept
0099 : thunk_{}, invoke_(&invoke_function_holder<NoEx, F*, R, Args...>::invoke_function) {
0100 thunk_.pfn_ = reinterpret_cast<decltype(thunk_.pfn_)>(fn);
0101 }
0102
0103 template <class F>
0104 function_ref_base(obj_tag, F&& fn) noexcept
0105 : thunk_{}, invoke_(&invoke_object_holder<Const, NoEx, F, R, Args...>::invoke_object) {
0106 thunk_.pobj_ = const_cast<void*>(static_cast<void const*>(std::addressof(fn)));
0107 }
0108
0109 #if defined(BOOST_COMPAT_HAS_AUTO_NTTP)
0110
0111 template <auto f, class F = decltype(f)>
0112 function_ref_base(mem_fn_tag, nontype_t<f>)
0113 : thunk_{}, invoke_(&invoke_mem_fn_holder<F{f}, Const, NoEx, R, Args...>::invoke_mem_fn) {
0114 thunk_.pobj_ = nullptr;
0115 }
0116
0117 template <auto f, class U, class F = decltype(f)>
0118 function_ref_base(mem_fn_tag, nontype_t<f>, U&& obj)
0119 : thunk_{}, invoke_(&invoke_target_mem_fn_holder<F{f}, U, Const, NoEx, R, Args...>::invoke_mem_fn) {
0120 thunk_.pobj_ = const_cast<void*>(static_cast<void const*>(std::addressof(obj)));
0121 }
0122
0123 template <auto f, class T, class F = decltype(f)>
0124 function_ref_base(mem_fn_tag, nontype_t<f>, T* obj)
0125 : thunk_{}, invoke_(&invoke_ptr_mem_fn_holder<F{f}, T, Const, NoEx, R, Args...>::invoke_mem_fn) {
0126 thunk_.pobj_ = const_cast<void*>(static_cast<void const*>(obj));
0127 }
0128
0129 #endif
0130
0131 function_ref_base(const function_ref_base&) noexcept = default;
0132 function_ref_base& operator=(const function_ref_base&) noexcept = default;
0133
0134 R operator()(Args&&... args) const noexcept(NoEx) { return this->invoke_(thunk_, std::forward<Args>(args)...); }
0135 };
0136
0137 }
0138
0139 template <class R, class... Args>
0140 struct function_ref<R(Args...)> : public detail::function_ref_base<false, false, R, Args...> {
0141 private:
0142 using base_type = detail::function_ref_base<false, false, R, Args...>;
0143 using typename base_type::fp_tag;
0144 using typename base_type::mem_fn_tag;
0145 using typename base_type::obj_tag;
0146
0147 template <class... T>
0148 using is_invocable_using = boost::compat::is_invocable_r<R, T..., Args...>;
0149
0150 public:
0151 template <class F, enable_if_t<std::is_function<F>::value && is_invocable_using<F>::value, int> = 0>
0152 function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {}
0153
0154 template <class F, class T = remove_reference_t<F>,
0155 enable_if_t<!std::is_same<remove_cvref_t<F>, function_ref>::value && !std::is_member_pointer<T>::value &&
0156 is_invocable_using<T&>::value,
0157 int> = 0>
0158 function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}
0159
0160 #if defined(BOOST_COMPAT_HAS_AUTO_NTTP)
0161
0162 template <auto f, class F = decltype(f), enable_if_t<is_invocable_using<F>::value, int> = 0>
0163 function_ref(nontype_t<f> x) noexcept : base_type(mem_fn_tag{}, x) {}
0164
0165 template <auto f, class U, class T = remove_reference_t<U>, class F = decltype(f),
0166 enable_if_t<!std::is_rvalue_reference_v<U&&> && is_invocable_using<F, T&>::value, int> = 0>
0167 function_ref(nontype_t<f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}
0168
0169 template <auto f, class T, class F = decltype(f), enable_if_t<is_invocable_using<F, T*>::value, int> = 0>
0170 function_ref(nontype_t<f> x, T* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}
0171
0172 #endif
0173
0174 function_ref(const function_ref&) noexcept = default;
0175 function_ref& operator=(const function_ref&) noexcept = default;
0176
0177 template <class T, enable_if_t<!std::is_same<T, function_ref>::value && !std::is_pointer<T>::value, int> = 0>
0178 function_ref& operator=(T) = delete;
0179 };
0180
0181 template <class R, class... Args>
0182 struct function_ref<R(Args...) const> : public detail::function_ref_base<true, false, R, Args...> {
0183 private:
0184 using base_type = detail::function_ref_base<true, false, R, Args...>;
0185 using typename base_type::fp_tag;
0186 using typename base_type::mem_fn_tag;
0187 using typename base_type::obj_tag;
0188
0189 template <class... T>
0190 using is_invocable_using = boost::compat::is_invocable_r<R, T..., Args...>;
0191
0192 public:
0193 template <class F, enable_if_t<std::is_function<F>::value && is_invocable_using<F>::value, int> = 0>
0194 function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {}
0195
0196 template <class F, class T = remove_reference_t<F>,
0197 enable_if_t<!std::is_same<remove_cvref_t<F>, function_ref>::value && !std::is_member_pointer<T>::value &&
0198 is_invocable_using<T const&>::value,
0199 int> = 0>
0200 function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}
0201
0202 #if defined(BOOST_COMPAT_HAS_AUTO_NTTP)
0203
0204 template <auto f, class F = decltype(f), enable_if_t<is_invocable_using<F>::value, int> = 0>
0205 function_ref(nontype_t<f> x) noexcept : base_type(mem_fn_tag{}, x) {}
0206
0207 template <auto f, class U, class T = remove_reference_t<U>, class F = decltype(f),
0208 enable_if_t<!std::is_rvalue_reference_v<U&&> && is_invocable_using<F, T const&>::value, int> = 0>
0209 function_ref(nontype_t<f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}
0210
0211 template <auto f, class T, class F = decltype(f), enable_if_t<is_invocable_using<F, T const*>::value, int> = 0>
0212 function_ref(nontype_t<f> x, T const* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}
0213
0214 #endif
0215
0216 function_ref(const function_ref&) noexcept = default;
0217 function_ref& operator=(const function_ref&) noexcept = default;
0218
0219 template <class T, enable_if_t<!std::is_same<T, function_ref>::value && !std::is_pointer<T>::value, int> = 0>
0220 function_ref& operator=(T) = delete;
0221 };
0222
0223 #if defined(__cpp_noexcept_function_type)
0224
0225 template <class R, class... Args>
0226 struct function_ref<R(Args...) noexcept> : public detail::function_ref_base<false, true, R, Args...> {
0227 private:
0228 using base_type = detail::function_ref_base<false, true, R, Args...>;
0229 using typename base_type::fp_tag;
0230 using typename base_type::mem_fn_tag;
0231 using typename base_type::obj_tag;
0232
0233 template <class... T>
0234 using is_invocable_using = boost::compat::is_nothrow_invocable_r<R, T..., Args...>;
0235
0236 public:
0237 template <class F, enable_if_t<std::is_function<F>::value && is_invocable_using<F>::value, int> = 0>
0238 function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {}
0239
0240 template <class F, class T = remove_reference_t<F>,
0241 enable_if_t<!std::is_same<remove_cvref_t<F>, function_ref>::value && !std::is_member_pointer<T>::value &&
0242 is_invocable_using<T&>::value,
0243 int> = 0>
0244 function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}
0245
0246 #if defined(BOOST_COMPAT_HAS_AUTO_NTTP)
0247
0248 template <auto f, class F = decltype(f), enable_if_t<is_invocable_using<F>::value, int> = 0>
0249 function_ref(nontype_t<f> x) noexcept : base_type(mem_fn_tag{}, x) {}
0250
0251 template <auto f, class U, class T = remove_reference_t<U>, class F = decltype(f),
0252 enable_if_t<!std::is_rvalue_reference_v<U&&> && is_invocable_using<F, T&>::value, int> = 0>
0253 function_ref(nontype_t<f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}
0254
0255 template <auto f, class T, class F = decltype(f), enable_if_t<is_invocable_using<F, T*>::value, int> = 0>
0256 function_ref(nontype_t<f> x, T* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}
0257
0258 #endif
0259
0260 function_ref(const function_ref&) noexcept = default;
0261 function_ref& operator=(const function_ref&) noexcept = default;
0262
0263 template <class T, enable_if_t<!std::is_same<T, function_ref>::value && !std::is_pointer<T>::value, int> = 0>
0264 function_ref& operator=(T) = delete;
0265 };
0266
0267 template <class R, class... Args>
0268 struct function_ref<R(Args...) const noexcept> : public detail::function_ref_base<true, true, R, Args...> {
0269 private:
0270 using base_type = detail::function_ref_base<true, true, R, Args...>;
0271 using typename base_type::fp_tag;
0272 using typename base_type::mem_fn_tag;
0273 using typename base_type::obj_tag;
0274
0275 template <class... T>
0276 using is_invocable_using = boost::compat::is_nothrow_invocable_r<R, T..., Args...>;
0277
0278 public:
0279 template <class F, enable_if_t<std::is_function<F>::value && is_invocable_using<F>::value, int> = 0>
0280 function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {}
0281
0282 template <class F, class T = remove_reference_t<F>,
0283 enable_if_t<!std::is_same<remove_cvref_t<F>, function_ref>::value && !std::is_member_pointer<T>::value &&
0284 is_invocable_using<T const&>::value,
0285 int> = 0>
0286 function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {}
0287
0288 #if defined(BOOST_COMPAT_HAS_AUTO_NTTP)
0289
0290 template <auto f, class F = decltype(f), enable_if_t<is_invocable_using<F>::value, int> = 0>
0291 function_ref(nontype_t<f> x) noexcept : base_type(mem_fn_tag{}, x) {}
0292
0293 template <auto f, class U, class T = remove_reference_t<U>, class F = decltype(f),
0294 enable_if_t<!std::is_rvalue_reference_v<U&&> && is_invocable_using<F, T const&>::value, int> = 0>
0295 function_ref(nontype_t<f> x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward<U>(obj)) {}
0296
0297 template <auto f, class T, class F = decltype(f), enable_if_t<is_invocable_using<F, T const*>::value, int> = 0>
0298 function_ref(nontype_t<f> x, T const* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {}
0299
0300 #endif
0301
0302 function_ref(const function_ref&) noexcept = default;
0303 function_ref& operator=(const function_ref&) noexcept = default;
0304
0305 template <class T, enable_if_t<!std::is_same<T, function_ref>::value && !std::is_pointer<T>::value, int> = 0>
0306 function_ref& operator=(T) = delete;
0307 };
0308
0309 #endif
0310
0311 }
0312 }
0313
0314 #endif