|
| 1 | +/* |
| 2 | + * Boost Software License - Version 1.0 - August 17th, 2003 |
| 3 | + * |
| 4 | + * Permission is hereby granted, free of charge, to any person or organization |
| 5 | + * obtaining a copy of the software and accompanying documentation covered by |
| 6 | + * this license (the "Software") to use, reproduce, display, distribute, |
| 7 | + * execute, and transmit the Software, and to prepare derivative works of the |
| 8 | + * Software, and to permit third-parties to whom the Software is furnished to |
| 9 | + * do so, all subject to the following: |
| 10 | + * |
| 11 | + * The copyright notices in the Software and this entire statement, including |
| 12 | + * the above license grant, this restriction and the following disclaimer, |
| 13 | + * must be included in all copies of the Software, in whole or in part, and |
| 14 | + * all derivative works of the Software, unless such copies or derivative |
| 15 | + * works are solely in the form of machine-executable object code generated by |
| 16 | + * a source language processor. |
| 17 | + * |
| 18 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 19 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 20 | + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT |
| 21 | + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |
| 22 | + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |
| 23 | + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| 24 | + * DEALINGS IN THE SOFTWARE. |
| 25 | + */ |
| 26 | + |
| 27 | +#pragma once |
| 28 | + |
| 29 | +#include <cassert> |
| 30 | +#include <type_traits> |
| 31 | +#include <utility> |
| 32 | +#include <functional> |
| 33 | + |
| 34 | +#ifndef SG14_INPLACE_FUNCTION_THROW |
| 35 | +#define SG14_INPLACE_FUNCTION_THROW(x) assert(0 && "Bad function call") |
| 36 | +#endif |
| 37 | + |
| 38 | +namespace stdext { |
| 39 | + |
| 40 | +namespace inplace_function_detail { |
| 41 | + |
| 42 | +static constexpr size_t InplaceFunctionDefaultCapacity = 32; |
| 43 | + |
| 44 | +#ifndef SG14_USE_STD_ALIGNED_STORAGE |
| 45 | +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61458 |
| 46 | +// MSVC 32-bit has the same bug. |
| 47 | +// libc++ and MSVC 64-bit seem to work fine right now, but why run the risk? |
| 48 | +template<size_t Cap> |
| 49 | +union aligned_storage_helper { |
| 50 | + struct double1 { double a; }; |
| 51 | + struct double4 { double a[4]; }; |
| 52 | + template<class T> using maybe = std::conditional_t<(Cap >= sizeof(T)), T, char>; |
| 53 | + char real_data[Cap]; |
| 54 | + maybe<int> a; |
| 55 | + maybe<long> b; |
| 56 | + maybe<long long> c; |
| 57 | + maybe<void*> d; |
| 58 | + maybe<void(*)()> e; |
| 59 | + maybe<double1> f; |
| 60 | + maybe<double4> g; |
| 61 | + maybe<long double> h; |
| 62 | +}; |
| 63 | + |
| 64 | +template<size_t Cap, size_t Align = alignof(aligned_storage_helper<Cap>)> |
| 65 | +struct aligned_storage { |
| 66 | + using type = std::aligned_storage_t<Cap, Align>; |
| 67 | +}; |
| 68 | + |
| 69 | +template<size_t Cap, size_t Align = alignof(aligned_storage_helper<Cap>)> |
| 70 | +using aligned_storage_t = typename aligned_storage<Cap, Align>::type; |
| 71 | +static_assert(sizeof(aligned_storage_t<sizeof(void*)>) == sizeof(void*), "A"); |
| 72 | +static_assert(alignof(aligned_storage_t<sizeof(void*)>) == alignof(void*), "B"); |
| 73 | +#else |
| 74 | +using std::aligned_storage; |
| 75 | +using std::aligned_storage_t; |
| 76 | +static_assert(sizeof(std::aligned_storage_t<sizeof(void*)>) == sizeof(void*), "C"); |
| 77 | +static_assert(alignof(std::aligned_storage_t<sizeof(void*)>) == alignof(void*), "D"); |
| 78 | +#endif |
| 79 | + |
| 80 | +template<class T> struct wrapper |
| 81 | +{ |
| 82 | + using type = T; |
| 83 | +}; |
| 84 | + |
| 85 | +template<class R, class... Args> struct vtable |
| 86 | +{ |
| 87 | + using storage_ptr_t = void*; |
| 88 | + |
| 89 | + using invoke_ptr_t = R(*)(storage_ptr_t, Args&&...); |
| 90 | + using process_ptr_t = void(*)(storage_ptr_t, storage_ptr_t); |
| 91 | + using destructor_ptr_t = void(*)(storage_ptr_t); |
| 92 | + |
| 93 | + const invoke_ptr_t invoke_ptr; |
| 94 | + const process_ptr_t copy_ptr; |
| 95 | + const process_ptr_t relocate_ptr; |
| 96 | + const destructor_ptr_t destructor_ptr; |
| 97 | + |
| 98 | + explicit constexpr vtable() noexcept : |
| 99 | + invoke_ptr{ [](storage_ptr_t, Args&&...) -> R |
| 100 | + { SG14_INPLACE_FUNCTION_THROW(std::bad_function_call()); } |
| 101 | + }, |
| 102 | + copy_ptr{ [](storage_ptr_t, storage_ptr_t) -> void {} }, |
| 103 | + relocate_ptr{ [](storage_ptr_t, storage_ptr_t) -> void {} }, |
| 104 | + destructor_ptr{ [](storage_ptr_t) -> void {} } |
| 105 | + {} |
| 106 | + |
| 107 | + template<class C> explicit constexpr vtable(wrapper<C>) noexcept : |
| 108 | + invoke_ptr{ [](storage_ptr_t storage_ptr, Args&&... args) -> R |
| 109 | + { return (*static_cast<C*>(storage_ptr))( |
| 110 | + static_cast<Args&&>(args)... |
| 111 | + ); } |
| 112 | + }, |
| 113 | + copy_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr) -> void |
| 114 | + { ::new (dst_ptr) C{ (*static_cast<C*>(src_ptr)) }; } |
| 115 | + }, |
| 116 | + relocate_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr) -> void |
| 117 | + { |
| 118 | + ::new (dst_ptr) C{ std::move(*static_cast<C*>(src_ptr)) }; |
| 119 | + static_cast<C*>(src_ptr)->~C(); |
| 120 | + } |
| 121 | + }, |
| 122 | + destructor_ptr{ [](storage_ptr_t src_ptr) -> void |
| 123 | + { static_cast<C*>(src_ptr)->~C(); } |
| 124 | + } |
| 125 | + {} |
| 126 | + |
| 127 | + vtable(const vtable&) = delete; |
| 128 | + vtable(vtable&&) = delete; |
| 129 | + |
| 130 | + vtable& operator= (const vtable&) = delete; |
| 131 | + vtable& operator= (vtable&&) = delete; |
| 132 | + |
| 133 | + ~vtable() = default; |
| 134 | +}; |
| 135 | + |
| 136 | +template<class R, class... Args> |
| 137 | +#if __cplusplus >= 201703L |
| 138 | +inline constexpr |
| 139 | +#endif |
| 140 | +vtable<R, Args...> empty_vtable{}; |
| 141 | + |
| 142 | +template<size_t DstCap, size_t DstAlign, size_t SrcCap, size_t SrcAlign> |
| 143 | +struct is_valid_inplace_dst : std::true_type |
| 144 | +{ |
| 145 | + static_assert(DstCap >= SrcCap, |
| 146 | + "Can't squeeze larger inplace_function into a smaller one" |
| 147 | + ); |
| 148 | + |
| 149 | + static_assert(DstAlign % SrcAlign == 0, |
| 150 | + "Incompatible inplace_function alignments" |
| 151 | + ); |
| 152 | +}; |
| 153 | + |
| 154 | +// C++11 MSVC compatible implementation of std::is_invocable_r. |
| 155 | + |
| 156 | +template<class R> void accept(R); |
| 157 | + |
| 158 | +template<class, class R, class F, class... Args> struct is_invocable_r_impl : std::false_type {}; |
| 159 | + |
| 160 | +template<class F, class... Args> struct is_invocable_r_impl< |
| 161 | + decltype(std::declval<F>()(std::declval<Args>()...), void()), |
| 162 | + void, |
| 163 | + F, |
| 164 | + Args... |
| 165 | +> : std::true_type {}; |
| 166 | + |
| 167 | +template<class F, class... Args> struct is_invocable_r_impl< |
| 168 | + decltype(std::declval<F>()(std::declval<Args>()...), void()), |
| 169 | + const void, |
| 170 | + F, |
| 171 | + Args... |
| 172 | +> : std::true_type {}; |
| 173 | + |
| 174 | +template<class R, class F, class... Args> struct is_invocable_r_impl< |
| 175 | + decltype(accept<R>(std::declval<F>()(std::declval<Args>()...))), |
| 176 | + R, |
| 177 | + F, |
| 178 | + Args... |
| 179 | +> : std::true_type {}; |
| 180 | + |
| 181 | +template<class R, class F, class... Args> using is_invocable_r = is_invocable_r_impl< |
| 182 | + void, |
| 183 | + R, |
| 184 | + F, |
| 185 | + Args... |
| 186 | +>; |
| 187 | +} // namespace inplace_function_detail |
| 188 | + |
| 189 | +template< |
| 190 | + class Signature, |
| 191 | + size_t Capacity = inplace_function_detail::InplaceFunctionDefaultCapacity, |
| 192 | + size_t Alignment = alignof(inplace_function_detail::aligned_storage_t<Capacity>) |
| 193 | +> |
| 194 | +class inplace_function; // unspecified |
| 195 | + |
| 196 | +namespace inplace_function_detail { |
| 197 | + template<class> struct is_inplace_function : std::false_type {}; |
| 198 | + template<class Sig, size_t Cap, size_t Align> |
| 199 | + struct is_inplace_function<inplace_function<Sig, Cap, Align>> : std::true_type {}; |
| 200 | +} // namespace inplace_function_detail |
| 201 | + |
| 202 | +template< |
| 203 | + class R, |
| 204 | + class... Args, |
| 205 | + size_t Capacity, |
| 206 | + size_t Alignment |
| 207 | +> |
| 208 | +class inplace_function<R(Args...), Capacity, Alignment> |
| 209 | +{ |
| 210 | + using storage_t = inplace_function_detail::aligned_storage_t<Capacity, Alignment>; |
| 211 | + using vtable_t = inplace_function_detail::vtable<R, Args...>; |
| 212 | + using vtable_ptr_t = const vtable_t*; |
| 213 | + |
| 214 | + template <class, size_t, size_t> friend class inplace_function; |
| 215 | + |
| 216 | +public: |
| 217 | + using capacity = std::integral_constant<size_t, Capacity>; |
| 218 | + using alignment = std::integral_constant<size_t, Alignment>; |
| 219 | + |
| 220 | + inplace_function() noexcept : |
| 221 | + vtable_ptr_{std::addressof(inplace_function_detail::empty_vtable<R, Args...>)} |
| 222 | + {} |
| 223 | + |
| 224 | + template< |
| 225 | + class T, |
| 226 | + class C = std::decay_t<T>, |
| 227 | + class = std::enable_if_t< |
| 228 | + !inplace_function_detail::is_inplace_function<C>::value |
| 229 | + && inplace_function_detail::is_invocable_r<R, C&, Args...>::value |
| 230 | + > |
| 231 | + > |
| 232 | + inplace_function(T&& closure) |
| 233 | + { |
| 234 | + static_assert(std::is_copy_constructible<C>::value, |
| 235 | + "inplace_function cannot be constructed from non-copyable type" |
| 236 | + ); |
| 237 | + |
| 238 | + static_assert(sizeof(C) <= Capacity, |
| 239 | + "inplace_function cannot be constructed from object with this (large) size" |
| 240 | + ); |
| 241 | + |
| 242 | + static_assert(Alignment % alignof(C) == 0, |
| 243 | + "inplace_function cannot be constructed from object with this (large) alignment" |
| 244 | + ); |
| 245 | + |
| 246 | + static const vtable_t vt{inplace_function_detail::wrapper<C>{}}; |
| 247 | + vtable_ptr_ = std::addressof(vt); |
| 248 | + |
| 249 | + ::new (std::addressof(storage_)) C{std::forward<T>(closure)}; |
| 250 | + } |
| 251 | + |
| 252 | + template<size_t Cap, size_t Align> |
| 253 | + inplace_function(const inplace_function<R(Args...), Cap, Align>& other) |
| 254 | + : inplace_function(other.vtable_ptr_, other.vtable_ptr_->copy_ptr, std::addressof(other.storage_)) |
| 255 | + { |
| 256 | + static_assert(inplace_function_detail::is_valid_inplace_dst< |
| 257 | + Capacity, Alignment, Cap, Align |
| 258 | + >::value, "conversion not allowed"); |
| 259 | + } |
| 260 | + |
| 261 | + template<size_t Cap, size_t Align> |
| 262 | + inplace_function(inplace_function<R(Args...), Cap, Align>&& other) noexcept |
| 263 | + : inplace_function(other.vtable_ptr_, other.vtable_ptr_->relocate_ptr, std::addressof(other.storage_)) |
| 264 | + { |
| 265 | + static_assert(inplace_function_detail::is_valid_inplace_dst< |
| 266 | + Capacity, Alignment, Cap, Align |
| 267 | + >::value, "conversion not allowed"); |
| 268 | + |
| 269 | + other.vtable_ptr_ = std::addressof(inplace_function_detail::empty_vtable<R, Args...>); |
| 270 | + } |
| 271 | + |
| 272 | + inplace_function(std::nullptr_t) noexcept : |
| 273 | + vtable_ptr_{std::addressof(inplace_function_detail::empty_vtable<R, Args...>)} |
| 274 | + {} |
| 275 | + |
| 276 | + inplace_function(const inplace_function& other) : |
| 277 | + vtable_ptr_{other.vtable_ptr_} |
| 278 | + { |
| 279 | + vtable_ptr_->copy_ptr( |
| 280 | + std::addressof(storage_), |
| 281 | + std::addressof(other.storage_) |
| 282 | + ); |
| 283 | + } |
| 284 | + |
| 285 | + inplace_function(inplace_function&& other) noexcept : |
| 286 | + vtable_ptr_{std::exchange(other.vtable_ptr_, std::addressof(inplace_function_detail::empty_vtable<R, Args...>))} |
| 287 | + { |
| 288 | + vtable_ptr_->relocate_ptr( |
| 289 | + std::addressof(storage_), |
| 290 | + std::addressof(other.storage_) |
| 291 | + ); |
| 292 | + } |
| 293 | + |
| 294 | + inplace_function& operator= (std::nullptr_t) noexcept |
| 295 | + { |
| 296 | + vtable_ptr_->destructor_ptr(std::addressof(storage_)); |
| 297 | + vtable_ptr_ = std::addressof(inplace_function_detail::empty_vtable<R, Args...>); |
| 298 | + return *this; |
| 299 | + } |
| 300 | + |
| 301 | + inplace_function& operator= (inplace_function other) noexcept |
| 302 | + { |
| 303 | + vtable_ptr_->destructor_ptr(std::addressof(storage_)); |
| 304 | + |
| 305 | + vtable_ptr_ = std::exchange(other.vtable_ptr_, std::addressof(inplace_function_detail::empty_vtable<R, Args...>)); |
| 306 | + vtable_ptr_->relocate_ptr( |
| 307 | + std::addressof(storage_), |
| 308 | + std::addressof(other.storage_) |
| 309 | + ); |
| 310 | + return *this; |
| 311 | + } |
| 312 | + |
| 313 | + ~inplace_function() |
| 314 | + { |
| 315 | + vtable_ptr_->destructor_ptr(std::addressof(storage_)); |
| 316 | + } |
| 317 | + |
| 318 | + R operator() (Args... args) const |
| 319 | + { |
| 320 | + return vtable_ptr_->invoke_ptr( |
| 321 | + std::addressof(storage_), |
| 322 | + std::forward<Args>(args)... |
| 323 | + ); |
| 324 | + } |
| 325 | + |
| 326 | + constexpr bool operator== (std::nullptr_t) const noexcept |
| 327 | + { |
| 328 | + return !operator bool(); |
| 329 | + } |
| 330 | + |
| 331 | + constexpr bool operator!= (std::nullptr_t) const noexcept |
| 332 | + { |
| 333 | + return operator bool(); |
| 334 | + } |
| 335 | + |
| 336 | + explicit constexpr operator bool() const noexcept |
| 337 | + { |
| 338 | + return vtable_ptr_ != std::addressof(inplace_function_detail::empty_vtable<R, Args...>); |
| 339 | + } |
| 340 | + |
| 341 | + void swap(inplace_function& other) noexcept |
| 342 | + { |
| 343 | + if (this == std::addressof(other)) return; |
| 344 | + |
| 345 | + storage_t tmp; |
| 346 | + vtable_ptr_->relocate_ptr( |
| 347 | + std::addressof(tmp), |
| 348 | + std::addressof(storage_) |
| 349 | + ); |
| 350 | + |
| 351 | + other.vtable_ptr_->relocate_ptr( |
| 352 | + std::addressof(storage_), |
| 353 | + std::addressof(other.storage_) |
| 354 | + ); |
| 355 | + |
| 356 | + vtable_ptr_->relocate_ptr( |
| 357 | + std::addressof(other.storage_), |
| 358 | + std::addressof(tmp) |
| 359 | + ); |
| 360 | + |
| 361 | + std::swap(vtable_ptr_, other.vtable_ptr_); |
| 362 | + } |
| 363 | + |
| 364 | + friend void swap(inplace_function& lhs, inplace_function& rhs) noexcept |
| 365 | + { |
| 366 | + lhs.swap(rhs); |
| 367 | + } |
| 368 | + |
| 369 | +private: |
| 370 | + vtable_ptr_t vtable_ptr_; |
| 371 | + mutable storage_t storage_; |
| 372 | + |
| 373 | + inplace_function( |
| 374 | + vtable_ptr_t vtable_ptr, |
| 375 | + typename vtable_t::process_ptr_t process_ptr, |
| 376 | + typename vtable_t::storage_ptr_t storage_ptr |
| 377 | + ) : vtable_ptr_{vtable_ptr} |
| 378 | + { |
| 379 | + process_ptr(std::addressof(storage_), storage_ptr); |
| 380 | + } |
| 381 | +}; |
| 382 | + |
| 383 | +} // namespace stdext |
0 commit comments