Update function2

This commit is contained in:
Alex Hultman
2019-10-07 12:03:50 +02:00
parent 6c7844d65f
commit d8b2d3cbb9
+200 -54
View File
@@ -36,11 +36,12 @@
#endif
#endif // FU2_WITH_DISABLED_EXCEPTIONS
// - FU2_HAS_NO_FUNCTIONAL_HEADER
#if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) || \
!defined(FU2_NO_FUNCTIONAL_HEADER) || \
#if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) && \
!defined(FU2_NO_FUNCTIONAL_HEADER) && \
!defined(FU2_HAS_DISABLED_EXCEPTIONS)
#define FU2_HAS_NO_FUNCTIONAL_HEADER
#include <functional>
#else
#define FU2_HAS_NO_FUNCTIONAL_HEADER
#endif
// - FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#if defined(FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE)
@@ -57,10 +58,53 @@
#endif
#endif // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE
// - FU2_HAS_NO_EMPTY_PROPAGATION
#if defined(FU2_WITH_NO_EMPTY_PROPAGATION)
#define FU2_HAS_NO_EMPTY_PROPAGATION
#endif // FU2_WITH_NO_EMPTY_PROPAGATION
#if !defined(FU2_HAS_DISABLED_EXCEPTIONS)
#include <exception>
#endif
/// Hint for the compiler that this point should be unreachable
#if defined(_MSC_VER)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __assume(false)
#elif defined(__GNUC__)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable()
#elif defined(__has_builtin) && __has_builtin(__builtin_unreachable)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable()
#else
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() abort()
#endif
/// Causes the application to exit abnormally
#if defined(_MSC_VER)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() __debugbreak()
#elif defined(__GNUC__)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() __builtin_trap()
#elif defined(__has_builtin) && __has_builtin(__builtin_trap)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() __builtin_trap()
#else
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() *(volatile int*)0x11 = 0
#endif
#ifndef NDEBUG
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE() ::fu2::detail::unreachable_debug()
#else
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE() FU2_DETAIL_UNREACHABLE_INTRINSIC()
#endif
namespace fu2 {
inline namespace abi_400 {
namespace detail {
@@ -70,7 +114,7 @@ class function;
template <typename...>
struct identity {};
// Equivalent to C++17's std::void_t which is targets a bug in GCC,
// 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...>
@@ -79,6 +123,9 @@ 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>>;
// Copy enabler helper class
template <bool /*Copyable*/>
struct copyable {};
@@ -115,9 +162,17 @@ struct property {
static constexpr auto const is_throwing = Throws;
// Is true when the function throws an exception on empty invocation.
static constexpr auto const is_strong_exception_guaranteed = Throws;
static constexpr auto const is_strong_exception_guaranteed =
HasStrongExceptGuarantee;
};
#ifndef NDEBUG
[[noreturn]] inline void unreachable_debug() {
FU2_DETAIL_TRAP();
std::abort();
}
#endif
/// Provides utilities for invocing callable objects
namespace invocation {
/// Invokes the given callable object with the given arguments
@@ -191,9 +246,9 @@ struct can_invoke<Pointer, identity<T&>,
: std::true_type {};
template <typename Pointer, typename T>
struct can_invoke<Pointer, identity<T&&>,
decltype((void)(std::declval<T&&>().*
std::declval<Pointer>()))> : std::true_type {
};
decltype(
(void)(std::declval<T&&>().*std::declval<Pointer>()))>
: std::true_type {};
template <typename Pointer, typename T>
struct can_invoke<Pointer, identity<T*>,
decltype(
@@ -334,12 +389,11 @@ struct box_factory<box<IsCopyable, T, Allocator>> {
};
/// Creates a box containing the given value and allocator
template <bool IsCopyable, typename T,
typename Allocator = std::allocator<std::decay_t<T>>>
template <bool IsCopyable, typename T, typename Allocator>
auto make_box(std::integral_constant<bool, IsCopyable>, T&& value,
Allocator&& allocator = Allocator{}) {
return box<IsCopyable, std::decay_t<T>, std::decay_t<Allocator>>{
std::forward<T>(value), std::forward<Allocator>(allocator)};
Allocator&& allocator) {
return box<IsCopyable, std::decay_t<T>, std::decay_t<Allocator>>(
std::forward<T>(value), std::forward<Allocator>(allocator));
}
template <typename T>
@@ -408,13 +462,13 @@ struct bad_function_call : std::exception {
return "bad function call";
}
};
#elif
#else
using std::bad_function_call;
#endif
#endif
#ifdef FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#define FU2_EXPAND_QUALIFIERS_NOEXCEPT(F) \
#define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) \
F(, , noexcept, , &) \
F(const, , noexcept, , &) \
F(, volatile, noexcept, , &) \
@@ -427,11 +481,17 @@ using std::bad_function_call;
F(const, , noexcept, &&, &&) \
F(, volatile, noexcept, &&, &&) \
F(const, volatile, noexcept, &&, &&)
#define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) \
F(, , noexcept) \
F(const, , noexcept) \
F(, volatile, noexcept) \
F(const, volatile, noexcept)
#else // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#define FU2_EXPAND_QUALIFIERS_NOEXCEPT(F)
#define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F)
#define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F)
#endif // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#define FU2_EXPAND_QUALIFIERS(F) \
#define FU2_DETAIL_EXPAND_QUALIFIERS(F) \
F(, , , , &) \
F(const, , , , &) \
F(, volatile, , , &) \
@@ -444,7 +504,13 @@ using std::bad_function_call;
F(const, , , &&, &&) \
F(, volatile, , &&, &&) \
F(const, volatile, , &&, &&) \
FU2_EXPAND_QUALIFIERS_NOEXCEPT(F)
FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F)
#define FU2_DETAIL_EXPAND_CV(F) \
F(, , ) \
F(const, , ) \
F(, volatile, ) \
F(const, volatile, ) \
FU2_DETAIL_EXPAND_CV_NOEXCEPT(F)
/// If the function is qualified as noexcept, the call will never throw
template <bool IsNoexcept>
@@ -518,7 +584,7 @@ using is_noexcept_noexcept = std::true_type;
}; \
};
FU2_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
#undef FU2_DEFINE_FUNCTION_TRAIT
/// Deduces to the function pointer to the given signature
@@ -669,7 +735,9 @@ class operator_impl;
auto parent = static_cast<Function CONST VOLATILE*>(this); \
using erasure_t = std::decay_t<decltype(parent->erasure_)>; \
\
return erasure_t::template invoke<Index>( \
/* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a */ \
/* compiler regression of MSVC 16.3.1, see #29 for details. */ \
return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \
static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \
std::forward<Args>(args)...); \
} \
@@ -678,7 +746,7 @@ class operator_impl;
typename Ret, typename... Args> \
class operator_impl<Index, function<Config, Property>, \
Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> \
: copyable<Config::is_owning || Config::is_copyable> { \
: copyable<!Config::is_owning || Config::is_copyable> { \
\
template <std::size_t, typename, typename...> \
friend class operator_impl; \
@@ -696,13 +764,15 @@ class operator_impl;
static_cast<function<Config, Property> CONST VOLATILE*>(this); \
using erasure_t = std::decay_t<decltype(parent->erasure_)>; \
\
return erasure_t::template invoke<Index>( \
/* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a */ \
/* compiler regression of MSVC 16.3.1, see #29 for details. */ \
return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \
static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \
std::forward<Args>(args)...); \
} \
};
FU2_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
#undef FU2_DEFINE_FUNCTION_TRAIT
} // namespace invocation_table
@@ -759,7 +829,7 @@ class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> {
// Just swap both pointers if we allocated on the heap
to->ptr_ = from->ptr_;
#ifndef _NDEBUG
#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.
@@ -814,9 +884,7 @@ class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> {
}
}
// TODO Use an unreachable intrinsic
assert(false && "Unreachable!");
std::exit(-1);
FU2_DETAIL_UNREACHABLE();
}
template <typename Box>
@@ -865,6 +933,9 @@ class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> {
write_empty(to, true);
break;
}
default: {
FU2_DETAIL_UNREACHABLE();
}
}
}
@@ -1045,7 +1116,8 @@ public:
}
template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
constexpr erasure(T&& callable, Allocator&& allocator = Allocator{}) {
constexpr erasure(std::false_type /*use_bool_op*/, T&& callable,
Allocator&& allocator = Allocator{}) {
vtable_t::init(vtable_,
type_erasure::make_box(
std::integral_constant<bool, Config::is_copyable>{},
@@ -1053,6 +1125,20 @@ public:
std::forward<Allocator>(allocator)),
this->opaque_ptr(), capacity());
}
template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
constexpr erasure(std::true_type /*use_bool_op*/, T&& callable,
Allocator&& allocator = Allocator{}) {
if (bool(callable)) {
vtable_t::init(vtable_,
type_erasure::make_box(
std::integral_constant<bool, Config::is_copyable>{},
std::forward<T>(callable),
std::forward<Allocator>(allocator)),
this->opaque_ptr(), capacity());
} else {
vtable_.set_empty();
}
}
~erasure() {
vtable_.weak_destroy(this->opaque_ptr(), capacity());
@@ -1089,19 +1175,9 @@ public:
return *this;
}
template <typename T>
constexpr erasure& operator=(T&& callable) {
vtable_.weak_destroy(this->opaque_ptr(), capacity());
vtable_t::init(vtable_,
type_erasure::make_box(
std::integral_constant<bool, Config::is_copyable>{},
std::forward<T>(callable)),
this->opaque_ptr(), capacity());
return *this;
}
template <typename T, typename Allocator>
void assign(T&& callable, Allocator&& allocator) {
template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
void assign(std::false_type /*use_bool_op*/, T&& callable,
Allocator&& allocator = {}) {
vtable_.weak_destroy(this->opaque_ptr(), capacity());
vtable_t::init(vtable_,
type_erasure::make_box(
@@ -1111,6 +1187,17 @@ public:
this->opaque_ptr(), capacity());
}
template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
void assign(std::true_type /*use_bool_op*/, T&& callable,
Allocator&& allocator = {}) {
if (bool(callable)) {
assign(std::false_type{}, std::forward<T>(callable),
std::forward<Allocator>(allocator));
} else {
operator=(nullptr);
}
}
/// Returns true when the erasure doesn't hold any erased object
constexpr bool empty() const noexcept {
return vtable_.empty();
@@ -1177,11 +1264,16 @@ public:
template <typename T>
// NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
constexpr erasure(T&& object)
constexpr erasure(std::false_type /*use_bool_op*/, T&& object)
: invoke_table_(invoke_table_t::template get_invocation_view_table_of<
std::decay_t<T>>()),
view_(address_taker<std::decay_t<T>>::take(std::forward<T>(object))) {
}
template <typename T>
// NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
constexpr erasure(std::true_type use_bool_op, T&& object) {
this->assign(use_bool_op, std::forward<T>(object));
}
~erasure() = default;
@@ -1211,11 +1303,19 @@ public:
}
template <typename T>
constexpr erasure& operator=(T&& object) {
constexpr void assign(std::false_type /*use_bool_op*/, T&& callable) {
invoke_table_ = invoke_table_t::template get_invocation_view_table_of<
std::decay_t<T>>();
view_.ptr_ = address_taker<std::decay_t<T>>::take(std::forward<T>(object));
return *this;
view_.ptr_ =
address_taker<std::decay_t<T>>::take(std::forward<T>(callable));
}
template <typename T>
constexpr void assign(std::true_type /*use_bool_op*/, T&& callable) {
if (bool(callable)) {
assign(std::false_type{}, std::forward<T>(callable));
} else {
operator=(nullptr);
}
}
/// Returns true when the erasure doesn't hold any erased object
@@ -1254,9 +1354,48 @@ struct accepts_all<
void_t<std::enable_if_t<accepts_one<T, Signatures>::value>...>>
: std::true_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.
#if defined(FU2_HAS_NO_EMPTY_PROPAGATION)
template <typename T>
struct use_bool_op : std::false_type {};
#else
template <typename T, typename = void>
struct has_bool_op : std::false_type {};
template <typename T>
struct has_bool_op<T, void_t<decltype(bool(std::declval<T>()))>>
: std::true_type {
#ifndef NDEBUG
static_assert(!std::is_pointer<T>::value,
"Missing deduction for function pointer!");
#endif
};
template <typename T>
struct use_bool_op : has_bool_op<T> {};
#define FU2_DEFINE_USE_OP_TRAIT(CONST, VOLATILE, NOEXCEPT) \
template <typename Ret, typename... Args> \
struct use_bool_op<Ret (*CONST VOLATILE)(Args...) NOEXCEPT> \
: std::true_type {};
FU2_DETAIL_EXPAND_CV(FU2_DEFINE_USE_OP_TRAIT)
#undef FU2_DEFINE_USE_OP_TRAIT
template <typename Ret, typename... Args>
struct use_bool_op<Ret(Args...)> : std::false_type {};
#if defined(FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE)
template <typename Ret, typename... Args>
struct use_bool_op<Ret(Args...) noexcept> : std::false_type {};
#endif
#endif // FU2_HAS_NO_EMPTY_PROPAGATION
template <typename Config, typename T>
struct assert_wrong_copy_assign {
static_assert(!Config::is_copyable ||
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!");
@@ -1372,7 +1511,8 @@ public:
enable_if_can_accept_all_t<T>* = nullptr,
assert_wrong_copy_assign_t<T>* = nullptr,
assert_no_strong_except_guarantee_t<T>* = nullptr>
constexpr function(T&& callable) : erasure_(std::forward<T>(callable)) {
constexpr function(T&& callable)
: erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable)) {
}
template <typename T, typename Allocator, //
enable_if_not_convertible_to_this<T>* = nullptr,
@@ -1381,7 +1521,7 @@ public:
assert_wrong_copy_assign_t<T>* = nullptr,
assert_no_strong_except_guarantee_t<T>* = nullptr>
constexpr function(T&& callable, Allocator&& allocator)
: erasure_(std::forward<T>(callable),
: erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable),
std::forward<Allocator>(allocator)) {
}
@@ -1418,7 +1558,7 @@ public:
assert_wrong_copy_assign_t<T>* = nullptr,
assert_no_strong_except_guarantee_t<T>* = nullptr>
function& operator=(T&& callable) {
erasure_ = std::forward<T>(callable);
erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable));
return *this;
}
@@ -1445,7 +1585,7 @@ public:
assert_wrong_copy_assign_t<T>* = nullptr,
assert_no_strong_except_guarantee_t<T>* = nullptr>
void assign(T&& callable, Allocator&& allocator = Allocator{}) {
erasure_.assign(std::forward<T>(callable),
erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable),
std::forward<Allocator>(allocator));
}
@@ -1582,13 +1722,13 @@ using function_view = function_base<false, true, capacity_default, //
/// Exception type that is thrown when invoking empty function objects
/// and exception support isn't disabled.
///
/// Exception suport is enabled if
/// Exception support is enabled if
/// the template parameter 'Throwing' is set to true (default).
///
/// This type will default to std::bad_function_call if the
/// functional header is used, otherwise the library provides its own type.
///
/// You may disable the inclusion of the functionl header
/// You may disable the inclusion of the functional header
/// through defining `FU2_WITH_NO_FUNCTIONAL_HEADER`.
///
using detail::type_erasure::invocation_table::bad_function_call;
@@ -1612,7 +1752,13 @@ constexpr auto overload(T&&... callables) {
}
} // namespace fu2
#undef FU2_EXPAND_QUALIFIERS
#undef FU2_EXPAND_QUALIFIERS_NOEXCEPT
#undef FU2_DETAIL_EXPAND_QUALIFIERS
#undef FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT
#undef FU2_DETAIL_EXPAND_CV
#undef FU2_DETAIL_EXPAND_CV_NOEXCEPT
#undef FU2_DETAIL_UNREACHABLE_INTRINSIC
#undef FU2_DETAIL_UNREACHABLE_INTRINSIC
#undef FU2_DETAIL_TRAP
#endif // FU2_INCLUDED_FUNCTION2_HPP_