/* * Boost Software License - Version 1.0 - August 17th, 2003 * * Permission is hereby granted, free of charge, to any person or organization * obtaining a copy of the software and accompanying documentation covered by * this license (the "Software") to use, reproduce, display, distribute, * execute, and transmit the Software, and to prepare derivative works of the * Software, and to permit third-parties to whom the Software is furnished to * do so, all subject to the following: * * The copyright notices in the Software and this entire statement, including * the above license grant, this restriction and the following disclaimer, * must be included in all copies of the Software, in whole or in part, and * all derivative works of the Software, unless such copies or derivative * works are solely in the form of machine-executable object code generated by * a source language processor. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #pragma once #include #include #include #ifndef SG14_INPLACE_FUNCTION_THROW #define SG14_INPLACE_FUNCTION_THROW(x) throw (x) #endif namespace stdext { namespace inplace_function_detail { static constexpr size_t InplaceFunctionDefaultCapacity = 32; #ifndef SG14_USE_STD_ALIGNED_STORAGE // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61458 // MSVC 32-bit has the same bug. // libc++ and MSVC 64-bit seem to work fine right now, but why run the risk? template union aligned_storage_helper { struct double1 { double a; }; struct double4 { double a[4]; }; template using maybe = std::conditional_t<(Cap >= sizeof(T)), T, char>; char real_data[Cap]; maybe a; maybe b; maybe c; maybe d; maybe e; maybe f; maybe g; maybe h; }; template)> struct aligned_storage { using type = std::aligned_storage_t; }; template)> using aligned_storage_t = typename aligned_storage::type; static_assert(sizeof(aligned_storage_t) == sizeof(void*), "A"); static_assert(alignof(aligned_storage_t) == alignof(void*), "B"); #else using std::aligned_storage; using std::aligned_storage_t; static_assert(sizeof(std::aligned_storage_t) == sizeof(void*), "C"); static_assert(alignof(std::aligned_storage_t) == alignof(void*), "D"); #endif template struct wrapper { using type = T; }; template struct vtable { using storage_ptr_t = void*; using invoke_ptr_t = R(*)(storage_ptr_t, Args&&...); using process_ptr_t = void(*)(storage_ptr_t, storage_ptr_t); using destructor_ptr_t = void(*)(storage_ptr_t); const invoke_ptr_t invoke_ptr; const process_ptr_t copy_ptr; const process_ptr_t relocate_ptr; const destructor_ptr_t destructor_ptr; explicit constexpr vtable() noexcept : invoke_ptr{ [](storage_ptr_t, Args&&...) -> R { SG14_INPLACE_FUNCTION_THROW(std::bad_function_call()); } }, copy_ptr{ [](storage_ptr_t, storage_ptr_t) -> void {} }, relocate_ptr{ [](storage_ptr_t, storage_ptr_t) -> void {} }, destructor_ptr{ [](storage_ptr_t) -> void {} } {} template explicit constexpr vtable(wrapper) noexcept : invoke_ptr{ [](storage_ptr_t storage_ptr, Args&&... args) -> R { return (*static_cast(storage_ptr))( static_cast(args)... ); } }, copy_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr) -> void { ::new (dst_ptr) C{ (*static_cast(src_ptr)) }; } }, relocate_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr) -> void { ::new (dst_ptr) C{ std::move(*static_cast(src_ptr)) }; static_cast(src_ptr)->~C(); } }, destructor_ptr{ [](storage_ptr_t src_ptr) -> void { static_cast(src_ptr)->~C(); } } {} vtable(const vtable&) = delete; vtable(vtable&&) = delete; vtable& operator= (const vtable&) = delete; vtable& operator= (vtable&&) = delete; ~vtable() = default; }; template #if __cplusplus >= 201703L inline constexpr #endif vtable empty_vtable{}; template struct is_valid_inplace_dst : std::true_type { static_assert(DstCap >= SrcCap, "Can't squeeze larger inplace_function into a smaller one" ); static_assert(DstAlign % SrcAlign == 0, "Incompatible inplace_function alignments" ); }; // C++11 MSVC compatible implementation of std::is_invocable_r. template void accept(R); template struct is_invocable_r_impl : std::false_type {}; template struct is_invocable_r_impl< decltype(std::declval()(std::declval()...), void()), void, F, Args... > : std::true_type {}; template struct is_invocable_r_impl< decltype(std::declval()(std::declval()...), void()), const void, F, Args... > : std::true_type {}; template struct is_invocable_r_impl< decltype(accept(std::declval()(std::declval()...))), R, F, Args... > : std::true_type {}; template using is_invocable_r = is_invocable_r_impl< void, R, F, Args... >; } // namespace inplace_function_detail template< class Signature, size_t Capacity = inplace_function_detail::InplaceFunctionDefaultCapacity, size_t Alignment = alignof(inplace_function_detail::aligned_storage_t) > class inplace_function; // unspecified namespace inplace_function_detail { template struct is_inplace_function : std::false_type {}; template struct is_inplace_function> : std::true_type {}; } // namespace inplace_function_detail template< class R, class... Args, size_t Capacity, size_t Alignment > class inplace_function { using storage_t = inplace_function_detail::aligned_storage_t; using vtable_t = inplace_function_detail::vtable; using vtable_ptr_t = const vtable_t*; template friend class inplace_function; public: using capacity = std::integral_constant; using alignment = std::integral_constant; inplace_function() noexcept : vtable_ptr_{std::addressof(inplace_function_detail::empty_vtable)} {} template< class T, class C = std::decay_t, class = std::enable_if_t< !inplace_function_detail::is_inplace_function::value && inplace_function_detail::is_invocable_r::value > > inplace_function(T&& closure) { static_assert(std::is_copy_constructible::value, "inplace_function cannot be constructed from non-copyable type" ); static_assert(sizeof(C) <= Capacity, "inplace_function cannot be constructed from object with this (large) size" ); static_assert(Alignment % alignof(C) == 0, "inplace_function cannot be constructed from object with this (large) alignment" ); static const vtable_t vt{inplace_function_detail::wrapper{}}; vtable_ptr_ = std::addressof(vt); ::new (std::addressof(storage_)) C{std::forward(closure)}; } template inplace_function(const inplace_function& other) : inplace_function(other.vtable_ptr_, other.vtable_ptr_->copy_ptr, std::addressof(other.storage_)) { static_assert(inplace_function_detail::is_valid_inplace_dst< Capacity, Alignment, Cap, Align >::value, "conversion not allowed"); } template inplace_function(inplace_function&& other) noexcept : inplace_function(other.vtable_ptr_, other.vtable_ptr_->relocate_ptr, std::addressof(other.storage_)) { static_assert(inplace_function_detail::is_valid_inplace_dst< Capacity, Alignment, Cap, Align >::value, "conversion not allowed"); other.vtable_ptr_ = std::addressof(inplace_function_detail::empty_vtable); } inplace_function(std::nullptr_t) noexcept : vtable_ptr_{std::addressof(inplace_function_detail::empty_vtable)} {} inplace_function(const inplace_function& other) : vtable_ptr_{other.vtable_ptr_} { vtable_ptr_->copy_ptr( std::addressof(storage_), std::addressof(other.storage_) ); } inplace_function(inplace_function&& other) noexcept : vtable_ptr_{std::exchange(other.vtable_ptr_, std::addressof(inplace_function_detail::empty_vtable))} { vtable_ptr_->relocate_ptr( std::addressof(storage_), std::addressof(other.storage_) ); } inplace_function& operator= (std::nullptr_t) noexcept { vtable_ptr_->destructor_ptr(std::addressof(storage_)); vtable_ptr_ = std::addressof(inplace_function_detail::empty_vtable); return *this; } inplace_function& operator= (inplace_function other) noexcept { vtable_ptr_->destructor_ptr(std::addressof(storage_)); vtable_ptr_ = std::exchange(other.vtable_ptr_, std::addressof(inplace_function_detail::empty_vtable)); vtable_ptr_->relocate_ptr( std::addressof(storage_), std::addressof(other.storage_) ); return *this; } ~inplace_function() { vtable_ptr_->destructor_ptr(std::addressof(storage_)); } R operator() (Args... args) const { return vtable_ptr_->invoke_ptr( std::addressof(storage_), std::forward(args)... ); } constexpr bool operator== (std::nullptr_t) const noexcept { return !operator bool(); } constexpr bool operator!= (std::nullptr_t) const noexcept { return operator bool(); } explicit constexpr operator bool() const noexcept { return vtable_ptr_ != std::addressof(inplace_function_detail::empty_vtable); } void swap(inplace_function& other) noexcept { if (this == std::addressof(other)) return; storage_t tmp; vtable_ptr_->relocate_ptr( std::addressof(tmp), std::addressof(storage_) ); other.vtable_ptr_->relocate_ptr( std::addressof(storage_), std::addressof(other.storage_) ); vtable_ptr_->relocate_ptr( std::addressof(other.storage_), std::addressof(tmp) ); std::swap(vtable_ptr_, other.vtable_ptr_); } friend void swap(inplace_function& lhs, inplace_function& rhs) noexcept { lhs.swap(rhs); } private: vtable_ptr_t vtable_ptr_; mutable storage_t storage_; inplace_function( vtable_ptr_t vtable_ptr, typename vtable_t::process_ptr_t process_ptr, typename vtable_t::storage_ptr_t storage_ptr ) : vtable_ptr_{vtable_ptr} { process_ptr(std::addressof(storage_), storage_ptr); } }; } // namespace stdext