/*
- * Copyright 2017-present Facebook, Inc.
+ * Copyright 2016-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Function<ReturnType(Args...) const> constCastFunction(
Function<ReturnType(Args...)>&&) noexcept;
+#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...) const noexcept> constCastFunction(
+ Function<ReturnType(Args...) noexcept>&&) noexcept;
+#endif
+
namespace detail {
namespace function {
return false;
}
+template <typename F, typename... Args>
+using CallableResult = decltype(std::declval<F>()(std::declval<Args>()...));
+
+template <
+ typename From,
+ typename To,
+ typename = typename std::enable_if<
+ !std::is_reference<To>::value || std::is_reference<From>::value>::type>
+using SafeResultOf = decltype(static_cast<To>(std::declval<From>()));
+
template <typename FunctionType>
struct FunctionTraits;
using NonConstSignature = ReturnType(Args...);
using OtherSignature = ConstSignature;
- template <typename F, typename G = typename std::decay<F>::type>
- using ResultOf = decltype(
- static_cast<ReturnType>(std::declval<G&>()(std::declval<Args>()...)));
+ template <typename F>
+ using ResultOf =
+ SafeResultOf<CallableResult<_t<std::decay<F>>&, Args...>, ReturnType>;
template <typename Fun>
static ReturnType callSmall(Data& p, Args&&... args) {
using NonConstSignature = ReturnType(Args...);
using OtherSignature = NonConstSignature;
- template <typename F, typename G = typename std::decay<F>::type>
- using ResultOf = decltype(static_cast<ReturnType>(
- std::declval<const G&>()(std::declval<Args>()...)));
+ template <typename F>
+ using ResultOf = SafeResultOf<
+ CallableResult<const _t<std::decay<F>>&, Args...>,
+ ReturnType>;
template <typename Fun>
static ReturnType callSmall(Data& p, Args&&... args) {
};
};
+#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE
+template <typename ReturnType, typename... Args>
+struct FunctionTraits<ReturnType(Args...) noexcept> {
+ using Call = ReturnType (*)(Data&, Args&&...) noexcept;
+ using IsConst = std::false_type;
+ using ConstSignature = ReturnType(Args...) const noexcept;
+ using NonConstSignature = ReturnType(Args...) noexcept;
+ using OtherSignature = ConstSignature;
+
+ template <typename F>
+ using ResultOf =
+ SafeResultOf<CallableResult<_t<std::decay<F>>&, Args...>, ReturnType>;
+
+ template <typename Fun>
+ static ReturnType callSmall(Data& p, Args&&... args) noexcept {
+ return static_cast<ReturnType>((*static_cast<Fun*>(
+ static_cast<void*>(&p.tiny)))(static_cast<Args&&>(args)...));
+ }
+
+ template <typename Fun>
+ static ReturnType callBig(Data& p, Args&&... args) noexcept {
+ return static_cast<ReturnType>(
+ (*static_cast<Fun*>(p.big))(static_cast<Args&&>(args)...));
+ }
+
+ static ReturnType uninitCall(Data&, Args&&...) noexcept {
+ throw std::bad_function_call();
+ }
+
+ ReturnType operator()(Args... args) noexcept {
+ auto& fn = *static_cast<Function<NonConstSignature>*>(this);
+ return fn.call_(fn.data_, static_cast<Args&&>(args)...);
+ }
+
+ class SharedProxy {
+ std::shared_ptr<Function<NonConstSignature>> sp_;
+
+ public:
+ explicit SharedProxy(Function<NonConstSignature>&& func)
+ : sp_(std::make_shared<Function<NonConstSignature>>(std::move(func))) {}
+ ReturnType operator()(Args&&... args) const {
+ return (*sp_)(static_cast<Args&&>(args)...);
+ }
+ };
+};
+
+template <typename ReturnType, typename... Args>
+struct FunctionTraits<ReturnType(Args...) const noexcept> {
+ using Call = ReturnType (*)(Data&, Args&&...) noexcept;
+ using IsConst = std::true_type;
+ using ConstSignature = ReturnType(Args...) const noexcept;
+ using NonConstSignature = ReturnType(Args...) noexcept;
+ using OtherSignature = NonConstSignature;
+
+ template <typename F>
+ using ResultOf = SafeResultOf<
+ CallableResult<const _t<std::decay<F>>&, Args...>,
+ ReturnType>;
+
+ template <typename Fun>
+ static ReturnType callSmall(Data& p, Args&&... args) noexcept {
+ return static_cast<ReturnType>((*static_cast<const Fun*>(
+ static_cast<void*>(&p.tiny)))(static_cast<Args&&>(args)...));
+ }
+
+ template <typename Fun>
+ static ReturnType callBig(Data& p, Args&&... args) noexcept {
+ return static_cast<ReturnType>(
+ (*static_cast<const Fun*>(p.big))(static_cast<Args&&>(args)...));
+ }
+
+ static ReturnType uninitCall(Data&, Args&&...) noexcept {
+ throw std::bad_function_call();
+ }
+
+ ReturnType operator()(Args... args) const noexcept {
+ auto& fn = *static_cast<const Function<ConstSignature>*>(this);
+ return fn.call_(fn.data_, static_cast<Args&&>(args)...);
+ }
+
+ class SharedProxy {
+ std::shared_ptr<Function<ConstSignature>> sp_;
+
+ public:
+ explicit SharedProxy(Function<ConstSignature>&& func)
+ : sp_(std::make_shared<Function<ConstSignature>>(std::move(func))) {}
+ ReturnType operator()(Args&&... args) const {
+ return (*sp_)(static_cast<Args&&>(args)...);
+ }
+ };
+};
+#endif
+
template <typename Fun>
bool execSmall(Op o, Data* src, Data* dst) {
switch (o) {
// invoking undefined behavior. Const-correctness is only violated when
// `FunctionType` is a const function type (e.g., `int() const`) and `*this`
// is the result of calling `constCastFunction`.
- mutable Data data_;
+ mutable Data data_{};
Call call_{&Traits::uninitCall};
Exec exec_{&detail::function::uninitNoop};
* signature matches (i.e. it returns an object convertible to `R` when called
* with `Args...`).
*
- * \note `typename = ResultOf<Fun>` prevents this overload from being
- * selected by overload resolution when `fun` is not a compatible function.
+ * \note `typename Traits::template ResultOf<Fun>` prevents this overload
+ * from being selected by overload resolution when `fun` is not a compatible
+ * function.
*
- * \note The noexcept requires some explanation. IsSmall is true when the
+ * \note The noexcept requires some explanation. `IsSmall` is true when the
* decayed type fits within the internal buffer and is noexcept-movable. But
* this ctor might copy, not move. What we need here, if this ctor does a
* copy, is that this ctor be noexcept when the copy is noexcept. That is not
- * checked in IsSmall, and shouldn't be, because once the Function is
+ * checked in `IsSmall`, and shouldn't be, because once the `Function` is
* constructed, the contained object is never copied. This check is for this
* ctor only, in the case that this ctor does a copy.
*/
typename = typename Traits::template ResultOf<Fun>>
/* implicit */ Function(Fun fun) noexcept(
IsSmall<Fun>::value && noexcept(Fun(std::declval<Fun>())))
- : Function(static_cast<Fun&&>(fun), IsSmall<Fun>{}) {}
+ : Function(std::move(fun), IsSmall<Fun>{}) {}
/**
* For move-constructing from a `folly::Function<X(Ys...) [const?]>`.
* Assigns a callable object to this `Function`. If the operation fails,
* `*this` is left unmodified.
*
- * \note `typename = ResultOf<Fun>` prevents this overload from being
- * selected by overload resolution when `fun` is not a compatible function.
+ * \note `typename = decltype(Function(std::declval<Fun>()))` prevents this
+ * overload from being selected by overload resolution when `fun` is not a
+ * compatible function.
*/
template <typename Fun, typename = decltype(Function(std::declval<Fun>()))>
Function& operator=(Fun fun) noexcept(
// Q: Why is is safe to destroy and reconstruct this object in place?
// A: See the explanation in the move assignment operator.
this->~Function();
- ::new (this) Function(static_cast<Fun&&>(fun));
+ ::new (this) Function(std::move(fun));
} else {
// Construct a temporary and (nothrow) swap.
- Function(static_cast<Fun&&>(fun)).swap(*this);
+ Function(std::move(fun)).swap(*this);
}
return *this;
}
return std::move(that);
}
+#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...) const noexcept> constCastFunction(
+ Function<ReturnType(Args...) noexcept>&& that) noexcept {
+ return Function<ReturnType(Args...) const noexcept>{
+ std::move(that), detail::function::CoerceTag{}};
+}
+
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...) const noexcept> constCastFunction(
+ Function<ReturnType(Args...) const noexcept>&& that) noexcept {
+ return std::move(that);
+}
+#endif
+
namespace detail {
namespace function {
template <typename Fun, typename FunctionType, typename = void>