// Copyright 2015-2020 Denis Blank <denis.blank at outlook dot com> // Distributed under the Boost Software License, Version 1.0 // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt)
// Equivalent to C++17's std::void_t which targets a bug in GCC, // that prevents correct SFINAE behavior. // See http://stackoverflow.com/questions/35753920 for details. template <typename...> struct deduce_to_void : std::common_type<void> {};
template <typename... T> using void_t = typename deduce_to_void<T...>::type;
template <typename T> using unrefcv_t = std::remove_cv_t<std::remove_reference_t<T>>;
/// Configuration trait to configure the function_base class. template <bool Owning, bool Copyable, typename Capacity> struct config { // Is true if the function is owning. static constexpr autoconst is_owning = Owning;
// Is true if the function is copyable. static constexpr autoconst is_copyable = Copyable;
// The internal capacity of the function // used in small functor optimization. // The object shall expose the real capacity through Capacity::capacity // and the intended alignment through Capacity::alignment. using capacity = Capacity;
};
/// A config which isn't compatible to other configs template <bool Throws, bool HasStrongExceptGuarantee, typename... Args> struct property { // Is true when the function throws an exception on empty invocation. static constexpr autoconst is_throwing = Throws;
// Is true when the function throws an exception on empty invocation. static constexpr autoconst is_strong_exception_guaranteed =
HasStrongExceptGuarantee;
};
/// Destroys the box through the given allocator staticvoid box_deallocate(box<IsCopyable, T, Allocator>* me) {
real_allocator allocator_(*static_cast<Allocator const*>(me));
/// The retriever when the object is allocated inplace template <typename T, typename Accessor>
FU2_DETAIL_CXX14_CONSTEXPR auto retrieve(std::true_type /*is_inplace*/,
Accessor from,
std::size_t from_capacity) { using type = transfer_const_t<Accessor, transfer_volatile_t<Accessor, void>>*;
/// Process the command by using the data inside the internal capacity auto storage = &(from->inplace_storage_); auto inplace = const_cast<void*>(static_cast<type>(storage)); return type(std::align(alignof(T), sizeof(T), inplace, from_capacity));
}
/// The retriever which is used when the object is allocated /// through the allocator template <typename T, typename Accessor>
constexpr auto retrieve(std::false_type /*is_inplace*/, Accessor from,
std::size_t /*from_capacity*/) {
/// Deduces to the function pointer to the given signature template <typename Signature> using function_pointer_of = typename function_trait<Signature>::pointer_type;
template <typename... Args> struct invoke_table;
/// We optimize the vtable_t in case there is a single function overload template <typename First> struct invoke_table<First> { using type = function_pointer_of<First>;
/// Return the function pointer itself template <std::size_t Index> static constexpr auto fetch(type pointer) noexcept {
static_assert(Index == 0U, "The index should be 0 here!"); return pointer;
}
/// Returns the thunk of an single overloaded callable template <typename T, bool IsInplace> static constexpr type get_invocation_table_of() noexcept { return &function_trait<First>::template internal_invoker<T,
IsInplace>::invoke;
} /// Returns the thunk of an single overloaded callable template <typename T> static constexpr type get_invocation_view_table_of() noexcept { return &function_trait<First>::template view_invoker<T>::invoke;
} /// Returns the thunk of an empty single overloaded callable template <bool IsThrowing> static constexpr type get_empty_invocation_table() noexcept { return &function_trait<First>::template empty_invoker<IsThrowing>::invoke;
}
}; /// We generate a table in case of multiple function overloads template <typename First, typename Second, typename... Args> struct invoke_table<First, Second, Args...> { using type =
std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
function_pointer_of<Args>...> const*;
/// Return the function pointer at the particular index template <std::size_t Index> static constexpr auto fetch(type table) noexcept { return std::get<Index>(*table);
}
/// Returns the thunk of an multi overloaded callable template <typename T, bool IsInplace> static type get_invocation_table_of() noexcept { static invocation_vtable<T, IsInplace> const table; return &table;
}
/// The invocation vtable for a present object template <typename T> struct invocation_view_vtable
: public std::tuple<function_pointer_of<First>,
function_pointer_of<Second>,
function_pointer_of<Args>...> {
constexpr invocation_view_vtable() noexcept
: std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
function_pointer_of<Args>...>(std::make_tuple(
&function_trait<First>::template view_invoker<T>::invoke,
&function_trait<Second>::template view_invoker<T>::invoke,
&function_trait<Args>::template view_invoker<T>::invoke...)) {
}
};
/// Returns the thunk of an multi overloaded callable template <typename T> static type get_invocation_view_table_of() noexcept { static invocation_view_vtable<T> const table; return &table;
}
/// The invocation table for an empty wrapper template <bool IsThrowing> struct empty_vtable : public std::tuple<function_pointer_of<First>,
function_pointer_of<Second>,
function_pointer_of<Args>...> {
constexpr empty_vtable() noexcept
: std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
function_pointer_of<Args>...>(
std::make_tuple(&function_trait<First>::template empty_invoker<
IsThrowing>::invoke,
&function_trait<Second>::template empty_invoker<
IsThrowing>::invoke,
&function_trait<Args>::template empty_invoker<
IsThrowing>::invoke...)) {
}
};
/// Returns the thunk of an multi single overloaded callable template <bool IsThrowing> static type get_empty_invocation_table() noexcept { static empty_vtable<IsThrowing> const table; return &table;
}
};
template <std::size_t Index, typename Function, typename... Signatures> class operator_impl;
namespace tables { /// Identifies the action which is dispatched on the erased object enumclass opcode {
op_move, ///< Move the object and set the vtable
op_copy, ///< Copy the object and set the vtable
op_destroy, ///< Destroy the object and reset the vtable
op_weak_destroy, ///< Destroy the object without resetting the vtable
op_fetch_empty, ///< Stores true or false into the to storage ///< to indicate emptiness
};
/// Abstraction for a vtable together with a command table /// TODO Add optimization for a single formal argument /// TODO Add optimization to merge both tables if the function is size /// optimized template <typename Property> class vtable; template <bool IsThrowing, bool HasStrongExceptGuarantee, typename... FormalArgs> class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> { using command_function_t = void (*)(vtable* /*this*/, opcode /*op*/,
data_accessor* /*from*/,
std::size_t /*from_capacity*/,
data_accessor* /*to*/,
std::size_t /*to_capacity*/);
using invoke_table_t = invocation_table::invoke_table<FormalArgs...>;
switch (op) { case opcode::op_move: { /// Retrieve the pointer to the object auto box = static_cast<T*>(retrieve<T>(
std::integral_constant<bool, IsInplace>{}, from, from_capacity));
assert(box && "The object must not be over aligned or null!");
if (!IsInplace) { // Just swap both pointers if we allocated on the heap
to->ptr_ = from->ptr_;
#ifndef NDEBUG // We don't need to null the pointer since we know that // we don't own the data anymore through the vtable // which is set to empty.
from->ptr_ = nullptr; #endif
to_table->template set_allocated<T>();
} // The object is allocated inplace else {
construct(std::true_type{}, std::move(*box), to_table, to,
to_capacity);
box->~T();
} return;
} case opcode::op_copy: { auto box = static_cast<T const*>(retrieve<T>(
std::integral_constant<bool, IsInplace>{}, from, from_capacity));
assert(box && "The object must not be over aligned or null!");
assert(std::is_copy_constructible<T>::value && "The box is required to be copyable here!");
// Try to allocate the object inplace
construct(std::is_copy_constructible<T>{}, *box, to_table, to,
to_capacity); return;
} case opcode::op_destroy: case opcode::op_weak_destroy: {
/// The command table staticvoid empty_cmd(vtable* to_table, opcode op, data_accessor* /*from*/,
std::size_t /*from_capacity*/, data_accessor* to,
std::size_t /*to_capacity*/) {
switch (op) { case opcode::op_move: case opcode::op_copy: {
to_table->set_empty(); break;
} case opcode::op_destroy: case opcode::op_weak_destroy: { // Do nothing break;
} case opcode::op_fetch_empty: {
write_empty(to, true); break;
} default: {
FU2_DETAIL_UNREACHABLE();
}
}
}
public:
vtable() noexcept = default;
/// Initialize an object at the given position template <typename T> staticvoid init(vtable& table, T&& object, data_accessor* to,
std::size_t to_capacity) {
trait<std::decay_t<T>>::construct(std::true_type{}, std::forward<T>(object),
&table, to, to_capacity);
}
/// Moves the object at the given position void move(vtable& to_table, data_accessor* from, std::size_t from_capacity,
data_accessor* to,
std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) {
cmd_(&to_table, opcode::op_move, from, from_capacity, to, to_capacity);
set_empty();
}
/// Destroys the object at the given position void copy(vtable& to_table, data_accessor const* from,
std::size_t from_capacity, data_accessor* to,
std::size_t to_capacity) const {
cmd_(&to_table, opcode::op_copy, const_cast<data_accessor*>(from),
from_capacity, to, to_capacity);
}
/// Destroys the object at the given position void destroy(data_accessor* from,
std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) {
cmd_(this, opcode::op_destroy, from, from_capacity, nullptr, 0U);
}
/// Destroys the object at the given position without invalidating the /// vtable void
weak_destroy(data_accessor* from,
std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) {
cmd_(this, opcode::op_weak_destroy, from, from_capacity, nullptr, 0U);
}
/// Returns true when the vtable doesn't hold any erased object bool empty() const noexcept {
data_accessor data;
cmd_(nullptr, opcode::op_fetch_empty, nullptr, 0U, &data, 0U); returnbool(data.inplace_storage_);
}
/// Invoke the function at the given index template <std::size_t Index, typename... Args>
constexpr decltype(auto) invoke(Args&&... args) const { auto thunk = invoke_table_t::template fetch<Index>(vtable_); return thunk(std::forward<Args>(args)...);
} /// Invoke the function at the given index template <std::size_t Index, typename... Args>
constexpr decltype(auto) invoke(Args&&... args) constvolatile { auto thunk = invoke_table_t::template fetch<Index>(vtable_); return thunk(std::forward<Args>(args)...);
}
/// A union which makes the pointer to the heap object share the /// same space with the internal capacity. /// The storage type is distinguished by multiple versions of the /// control and vtable. template <typename Capacity, typename = void> struct internal_capacity { /// We extend the union through a technique similar to the tail object hack typedefunion { /// Tag to access the structure in a type-safe way
data_accessor accessor_; /// The internal capacity we use to allocate in-place
std::aligned_storage_t<Capacity::capacity, Capacity::alignment> capacity_;
} type;
}; template <typename Capacity> struct internal_capacity<
Capacity, std::enable_if_t<(Capacity::capacity < sizeof(void*))>> { typedefstruct { /// Tag to access the structure in a type-safe way
data_accessor accessor_;
} type;
};
template <typename Capacity> class internal_capacity_holder { // Tag to access the structure in a type-safe way typename internal_capacity<Capacity>::type storage_;
/// Returns true when the erasure doesn't hold any erased object
constexpr bool empty() const noexcept { return vtable_.empty();
}
/// Invoke the function of the erasure at the given index /// /// We define this out of class to be able to forward the qualified /// erasure correctly. template <std::size_t Index, typename Erasure, typename... Args> static constexpr decltype(auto) invoke(Erasure&& erasure, Args&&... args) { autoconst capacity = erasure.capacity(); return erasure.vtable_.template invoke<Index>(
std::forward<Erasure>(erasure).opaque_ptr(), capacity,
std::forward<Args>(args)...);
}
};
/// Deduces to a true_type if the type T provides the given signature and the /// signature is noexcept correct callable. template <typename T, typename Signature, typename Trait =
type_erasure::invocation_table::function_trait<Signature>> struct accepts_one
: detail::lazy_and< // both are std::integral_constant
invocation::can_invoke<typename Trait::template callable<T>, typename Trait::arguments>,
invocation::is_noexcept_correct<Trait::is_noexcept::value, typename Trait::template callable<T>, typename Trait::arguments>> {};
/// Deduces to a true_type if the type T provides all signatures template <typename T, typename Signatures, typename = void> struct accepts_all : std::false_type {}; template <typename T, typename... Signatures> struct accepts_all<
T, identity<Signatures...>,
void_t<std::enable_if_t<accepts_one<T, Signatures>::value>...>>
: std::true_type {};
#ifdefined(FU2_HAS_NO_EMPTY_PROPAGATION) template <typename T> struct use_bool_op : std::false_type {}; #elifdefined(FU2_HAS_LIMITED_EMPTY_PROPAGATION) /// Implementation for use_bool_op based on the behaviour of std::function, /// propagating empty state for pointers, `std::function` and /// `fu2::detail::function` types only. template <typename T> struct use_bool_op : std::false_type {};
/// Deduces to a true_type if the type T is implementing operator bool() /// or if the type is convertible to bool directly, this also implements an /// optimizations for function references `void(&)()` which are can never /// be null and for such a conversion to bool would never return false. template <typename T> struct use_bool_op : has_bool_op<T> {};
template <typename Config, typename T> struct assert_wrong_copy_assign {
static_assert(!Config::is_owning || !Config::is_copyable ||
std::is_copy_constructible<std::decay_t<T>>::value, "Can't wrap a non copyable object into a unique function!");
using type = void;
};
template <bool IsStrongExceptGuaranteed, typename T> struct assert_no_strong_except_guarantee {
static_assert(
!IsStrongExceptGuaranteed ||
(std::is_nothrow_move_constructible<T>::value &&
std::is_nothrow_destructible<T>::value), "Can't wrap a object an object that has no strong exception guarantees " "if this is required by the wrapper!");
using type = void;
};
/// SFINAES out if the given callable is not copyable correct to the left one. template <typename LeftConfig, typename RightConfig> using enable_if_copyable_correct_t =
std::enable_if_t<(!LeftConfig::is_copyable || RightConfig::is_copyable)>;
/// SFINAES out if the given function2 is not owning correct to this one template <typename LeftConfig, typename RightConfig> using enable_if_owning_correct_t =
std::enable_if_t<is_owning_correct<LeftConfig, RightConfig>::value>;
/// Clears the function
function& operator=(std::nullptr_t np) {
erasure_ = np; return *this;
}
/// Returns true when the function is empty bool empty() const noexcept {
--> --------------------
--> maximum size reached
--> --------------------
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.56Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.