/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License Version 2.0 with LLVM Exceptions * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * https://llvm.org/LICENSE.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace unifex { namespace _any_unique { struct _deallocate_cpo { using type_erased_signature_t = void(this_&) noexcept; template UNIFEX_ALWAYS_INLINE void operator()(T& obj) const noexcept { if constexpr (tag_invocable<_deallocate_cpo, T&>) { static_assert(noexcept(tag_invoke(_deallocate_cpo{}, obj))); tag_invoke(_deallocate_cpo{}, obj); } else { delete std::addressof(obj); } } }; template struct _concrete_impl { struct base { using allocator_type = typename std::allocator_traits< Allocator>::template rebind_alloc; template explicit base(std::allocator_arg_t, allocator_type alloc, Args&&... args) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_constructible_v) : value((Args &&) args...) , alloc(std::move(alloc)) {} friend void tag_invoke(_deallocate_cpo, base& impl) noexcept { allocator_type allocCopy = std::move(impl.alloc); impl.~base(); std::allocator_traits::deallocate( allocCopy, &impl, 1); } friend Concrete& tag_invoke(tag_t, base& self) noexcept { return self.value; } UNIFEX_NO_UNIQUE_ADDRESS Concrete value; UNIFEX_NO_UNIQUE_ADDRESS allocator_type alloc; }; template struct impl { struct type : base, private detail::with_forwarding_tag_invoke... { using base::base; }; }; }; template using concrete_impl = typename _concrete_impl::template impl::type; template struct _byval { class type; }; template class _byval::type : private with_type_erased_tag_invoke... { public: template explicit type( std::allocator_arg_t, Allocator alloc, std::in_place_type_t, Args&&... args) : vtable_(vtable_holder_t::template create< concrete_impl>()) { using concrete_type = concrete_impl; using allocator_type = typename concrete_type::allocator_type; using allocator_traits = std::allocator_traits; allocator_type typedAllocator{std::move(alloc)}; auto ptr = allocator_traits::allocate(typedAllocator, 1); UNIFEX_TRY { // TODO: Ideally we'd use allocator_traits::construct() here but // that makes it difficult to provide consistent behaviour across // std::allocator and std::pmr::polymorphic_allocator as the latter // automatically injects the extra allocator_arg/alloc params which // ends up duplicating them. But std::allocator doesn't do the same // injection of the parameters. ::new ((void*)ptr) concrete_type{std::allocator_arg, typedAllocator, (Args &&) args...}; } UNIFEX_CATCH (...) { allocator_traits::deallocate(typedAllocator, ptr, 1); UNIFEX_RETHROW(); } impl_ = static_cast(ptr); } template(typename Concrete, typename Allocator) (requires (!same_as>) AND (!instance_of_v>)) type(Concrete&& concrete, Allocator alloc) : type( std::allocator_arg, std::move(alloc), std::in_place_type>, (Concrete &&) concrete) {} template explicit type([[maybe_unused]] std::in_place_type_t tag, Args&&... args) : impl_(new Concrete((Args&&) args...)) , vtable_(vtable_holder_t::template create()) {} template(typename Concrete) (requires (!same_as>) AND (!instance_of_v)) type(Concrete&& concrete) : impl_(new auto((Concrete&&) concrete)) , vtable_(vtable_holder_t::template create>()) {} type(type&& other) noexcept : impl_(std::exchange(other.impl_, nullptr)) , vtable_(other.vtable_) {} UNIFEX_ALWAYS_INLINE ~type() { unsafe_deallocate(); } void swap(type& other) noexcept { std::swap(vtable_, other.vtable_); std::swap(impl_, other.impl_); } type& operator=(type other) noexcept { swap(other); return *this; } private: using vtable_holder_t = std::conditional_t< (sizeof...(CPOs) <= 2), detail::inline_vtable_holder<_deallocate_cpo, CPOs...>, detail::indirect_vtable_holder<_deallocate_cpo, CPOs...>>; UNIFEX_ALWAYS_INLINE void unsafe_deallocate() noexcept { // This leaves the any_unique in an invalid state. if (nullptr != impl_) { static_assert(noexcept(vtable_->template get<_deallocate_cpo>())); auto* deallocateFn = vtable_->template get<_deallocate_cpo>(); static_assert(noexcept(deallocateFn(_deallocate_cpo{}, impl_))); deallocateFn(_deallocate_cpo{}, impl_); } } friend void swap(type& left, type& right) noexcept { left.swap(right); } friend const vtable_holder_t& get_vtable(const type& self) noexcept { return self.vtable_; } friend void* get_object_address(const type& self) noexcept { return self.impl_; } void* impl_; vtable_holder_t vtable_; }; } // namespace _any_unique template using any_unique = typename _any_unique::_byval::type; template using any_unique_t = any_unique...>; } // namespace unifex #include