Permalink
Cannot retrieve contributors at this time
| /* | |
| * 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 <type_traits> | |
| #include <utility> | |
| #include <functional> | |
| #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<size_t Cap> | |
| union aligned_storage_helper { | |
| struct double1 { double a; }; | |
| struct double4 { double a[4]; }; | |
| template<class T> using maybe = std::conditional_t<(Cap >= sizeof(T)), T, char>; | |
| char real_data[Cap]; | |
| maybe<int> a; | |
| maybe<long> b; | |
| maybe<long long> c; | |
| maybe<void*> d; | |
| maybe<void(*)()> e; | |
| maybe<double1> f; | |
| maybe<double4> g; | |
| maybe<long double> h; | |
| }; | |
| template<size_t Cap, size_t Align = alignof(aligned_storage_helper<Cap>)> | |
| struct aligned_storage { | |
| using type = std::aligned_storage_t<Cap, Align>; | |
| }; | |
| template<size_t Cap, size_t Align = alignof(aligned_storage_helper<Cap>)> | |
| using aligned_storage_t = typename aligned_storage<Cap, Align>::type; | |
| static_assert(sizeof(aligned_storage_t<sizeof(void*)>) == sizeof(void*), "A"); | |
| static_assert(alignof(aligned_storage_t<sizeof(void*)>) == alignof(void*), "B"); | |
| #else | |
| using std::aligned_storage; | |
| using std::aligned_storage_t; | |
| static_assert(sizeof(std::aligned_storage_t<sizeof(void*)>) == sizeof(void*), "C"); | |
| static_assert(alignof(std::aligned_storage_t<sizeof(void*)>) == alignof(void*), "D"); | |
| #endif | |
| template<class T> struct wrapper | |
| { | |
| using type = T; | |
| }; | |
| template<class R, class... Args> 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<class C> explicit constexpr vtable(wrapper<C>) noexcept : | |
| invoke_ptr{ [](storage_ptr_t storage_ptr, Args&&... args) -> R | |
| { return (*static_cast<C*>(storage_ptr))( | |
| static_cast<Args&&>(args)... | |
| ); } | |
| }, | |
| copy_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr) -> void | |
| { ::new (dst_ptr) C{ (*static_cast<C*>(src_ptr)) }; } | |
| }, | |
| relocate_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr) -> void | |
| { | |
| ::new (dst_ptr) C{ std::move(*static_cast<C*>(src_ptr)) }; | |
| static_cast<C*>(src_ptr)->~C(); | |
| } | |
| }, | |
| destructor_ptr{ [](storage_ptr_t src_ptr) -> void | |
| { static_cast<C*>(src_ptr)->~C(); } | |
| } | |
| {} | |
| vtable(const vtable&) = delete; | |
| vtable(vtable&&) = delete; | |
| vtable& operator= (const vtable&) = delete; | |
| vtable& operator= (vtable&&) = delete; | |
| ~vtable() = default; | |
| }; | |
| template<class R, class... Args> | |
| #if __cplusplus >= 201703L | |
| inline constexpr | |
| #endif | |
| vtable<R, Args...> empty_vtable{}; | |
| template<size_t DstCap, size_t DstAlign, size_t SrcCap, size_t SrcAlign> | |
| 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<class R> void accept(R); | |
| template<class, class R, class F, class... Args> struct is_invocable_r_impl : std::false_type {}; | |
| template<class F, class... Args> struct is_invocable_r_impl< | |
| decltype(std::declval<F>()(std::declval<Args>()...), void()), | |
| void, | |
| F, | |
| Args... | |
| > : std::true_type {}; | |
| template<class F, class... Args> struct is_invocable_r_impl< | |
| decltype(std::declval<F>()(std::declval<Args>()...), void()), | |
| const void, | |
| F, | |
| Args... | |
| > : std::true_type {}; | |
| template<class R, class F, class... Args> struct is_invocable_r_impl< | |
| decltype(accept<R>(std::declval<F>()(std::declval<Args>()...))), | |
| R, | |
| F, | |
| Args... | |
| > : std::true_type {}; | |
| template<class R, class F, class... Args> 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<Capacity>) | |
| > | |
| class inplace_function; // unspecified | |
| namespace inplace_function_detail { | |
| template<class> struct is_inplace_function : std::false_type {}; | |
| template<class Sig, size_t Cap, size_t Align> | |
| struct is_inplace_function<inplace_function<Sig, Cap, Align>> : std::true_type {}; | |
| } // namespace inplace_function_detail | |
| template< | |
| class R, | |
| class... Args, | |
| size_t Capacity, | |
| size_t Alignment | |
| > | |
| class inplace_function<R(Args...), Capacity, Alignment> | |
| { | |
| using storage_t = inplace_function_detail::aligned_storage_t<Capacity, Alignment>; | |
| using vtable_t = inplace_function_detail::vtable<R, Args...>; | |
| using vtable_ptr_t = const vtable_t*; | |
| template <class, size_t, size_t> friend class inplace_function; | |
| public: | |
| using capacity = std::integral_constant<size_t, Capacity>; | |
| using alignment = std::integral_constant<size_t, Alignment>; | |
| inplace_function() noexcept : | |
| vtable_ptr_{std::addressof(inplace_function_detail::empty_vtable<R, Args...>)} | |
| {} | |
| template< | |
| class T, | |
| class C = std::decay_t<T>, | |
| class = std::enable_if_t< | |
| !inplace_function_detail::is_inplace_function<C>::value | |
| && inplace_function_detail::is_invocable_r<R, C&, Args...>::value | |
| > | |
| > | |
| inplace_function(T&& closure) | |
| { | |
| static_assert(std::is_copy_constructible<C>::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<C>{}}; | |
| vtable_ptr_ = std::addressof(vt); | |
| ::new (std::addressof(storage_)) C{std::forward<T>(closure)}; | |
| } | |
| template<size_t Cap, size_t Align> | |
| inplace_function(const inplace_function<R(Args...), Cap, Align>& 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<size_t Cap, size_t Align> | |
| inplace_function(inplace_function<R(Args...), Cap, Align>&& 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<R, Args...>); | |
| } | |
| inplace_function(std::nullptr_t) noexcept : | |
| vtable_ptr_{std::addressof(inplace_function_detail::empty_vtable<R, Args...>)} | |
| {} | |
| 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<R, Args...>))} | |
| { | |
| 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<R, Args...>); | |
| 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<R, Args...>)); | |
| 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>(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<R, Args...>); | |
| } | |
| 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 |