/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ // // Docs: https://fburl.com/fbcref_function // /** * @class folly::Function * @refcode folly/docs/examples/folly/Function.cpp * * A polymorphic function wrapper that is not copyable and does not * require the wrapped function to be copy constructible. * * `folly::Function` is a polymorphic function wrapper, similar to * `std::function`. The template parameters of the `folly::Function` define * the parameter signature of the wrapped callable, but not the specific * type of the embedded callable. E.g. a `folly::Function<int(int)>` * can wrap callables that return an `int` when passed an `int`. This can be a * function pointer or any class object implementing one or both of * * int operator(int); * int operator(int) const; * * If both are defined, the non-const one takes precedence. * * Unlike `std::function`, a `folly::Function` can wrap objects that are not * copy constructible. As a consequence of this, `folly::Function` itself * is not copyable, either. * * Another difference is that, unlike `std::function`, `folly::Function` treats * const-ness of methods correctly. While a `std::function` allows to wrap * an object that only implements a non-const `operator()` and invoke * a const-reference of the `std::function`, `folly::Function` requires you to * declare a function type as const in order to be able to execute it on a * const-reference. * * For example: * * class Foo { * public: * void operator()() { * // mutates the Foo object * } * }; * * class Bar { * std::function<void(void)> foo_; // wraps a Foo object * public: * void mutateFoo() const * { * foo_(); * } * }; * * Even though `mutateFoo` is a const-method, so it can only reference `foo_` * as const, it is able to call the non-const `operator()` of the Foo * object that is embedded in the foo_ function. * * `folly::Function` will not allow you to do that. You will have to decide * whether you need to invoke your wrapped callable from a const reference * (like in the example above), in which case it will only wrap a * `operator() const`. If your functor does not implement that, * compilation will fail. If you do not require to be able to invoke the * wrapped function in a const context, you can wrap any functor that * implements either or both of const and non-const `operator()`. * * The template parameter of `folly::Function`, the `FunctionType`, can be * const-qualified. Be aware that the const is part of the function signature. * It does not mean that the function type is a const type. * * using FunctionType = R(Args...); * using ConstFunctionType = R(Args...) const; * * In this example, `FunctionType` and `ConstFunctionType` are different * types. `ConstFunctionType` is not the same as `const FunctionType`. * As a matter of fact, trying to use the latter should emit a compiler * warning or error, because it has no defined meaning. * * // This will not compile: * folly::Function<void(void) const> func = Foo(); * // because Foo does not have a member function of the form: * // void operator()() const; * * // This will compile just fine: * folly::Function<void(void)> func = Foo(); * // and it will wrap the existing member function: * // void operator()(); * * When should a const function type be used? As a matter of fact, you will * probably not need to use const function types very often. See the following * example: * * class Bar { * folly::Function<void()> func_; * folly::Function<void() const> constFunc_; * * void someMethod() { * // Can call func_. * func_(); * // Can call constFunc_. * constFunc_(); * } * * void someConstMethod() const { * // Can call constFunc_. * constFunc_(); * // However, cannot call func_ because a non-const method cannot * // be called from a const one. * } * }; * * As you can see, whether the `folly::Function`'s function type should * be declared const or not is identical to whether a corresponding method * would be declared const or not. * * You only require a `folly::Function` to hold a const function type, if you * intend to invoke it from within a const context. This is to ensure that * you cannot mutate its inner state when calling in a const context. * * This is how the const/non-const choice relates to lambda functions: * * // Non-mutable lambdas: can be stored in a non-const... * folly::Function<void(int)> print_number = * [] (int number) { std::cout << number << std::endl; }; * * // ...as well as in a const folly::Function * folly::Function<void(int) const> print_number_const = * [] (int number) { std::cout << number << std::endl; }; * * // Mutable lambda: can only be stored in a non-const folly::Function: * int number = 0; * folly::Function<void()> print_number = * [number] () mutable { std::cout << ++number << std::endl; }; * // Trying to store the above mutable lambda in a * // `folly::Function<void() const>` would lead to a compiler error: * // error: no viable conversion from '(lambda at ...)' to * // 'folly::Function<void () const>' * * Casting between const and non-const `folly::Function`s: * conversion from const to non-const signatures happens implicitly. Any * function that takes a `folly::Function<R(Args...)>` can be passed * a `folly::Function<R(Args...) const>` without explicit conversion. * This is safe, because casting from const to non-const only entails giving * up the ability to invoke the function from a const context. * Casting from a non-const to a const signature is potentially dangerous, * as it means that a function that may change its inner state when invoked * is made possible to call from a const context. Therefore this cast does * not happen implicitly. The function `folly::constCastFunction` can * be used to perform the cast. * * // Mutable lambda: can only be stored in a non-const folly::Function: * int number = 0; * folly::Function<void()> print_number = * [number] () mutable { std::cout << ++number << std::endl; }; * * // const-cast to a const folly::Function: * folly::Function<void() const> print_number_const = * constCastFunction(std::move(print_number)); * * When to use const function types? * Generally, only when you need them. When you use a `folly::Function` as a * member of a struct or class, only use a const function signature when you * need to invoke the function from const context. * When passing a `folly::Function` to a function, the function should accept * a non-const `folly::Function` whenever possible, i.e. when it does not * need to pass on or store a const `folly::Function`. This is the least * possible constraint: you can always pass a const `folly::Function` when * the function accepts a non-const one. * * How does the const behaviour compare to `std::function`? * `std::function` can wrap object with non-const invocation behaviour but * exposes them as const. The equivalent behaviour can be achieved with * `folly::Function` like so: * * std::function<void(void)> stdfunc = someCallable; * * folly::Function<void(void) const> uniqfunc = constCastFunction( * folly::Function<void(void)>(someCallable) * ); * * You need to wrap the callable first in a non-const `folly::Function` to * select a non-const invoke operator (or the const one if no non-const one is * present), and then move it into a const `folly::Function` using * `constCastFunction`. */ #pragma once #include <cstring> #include <functional> #include <memory> #include <new> #include <type_traits> #include <utility> #include <folly/CppAttributes.h> #include <folly/Portability.h> #include <folly/Traits.h> #include <folly/functional/Invoke.h> #include <folly/lang/Align.h> #include <folly/lang/Exception.h> #include <folly/lang/New.h> namespace folly { template <typename FunctionType> class Function; template <typename ReturnType, typename... Args> Function<ReturnType(Args...) const> constCastFunction( Function<ReturnType(Args...)>&&) noexcept; template <typename ReturnType, typename... Args> Function<ReturnType(Args...) const noexcept> constCastFunction( Function<ReturnType(Args...) noexcept>&&) noexcept; namespace detail { namespace function { enum class Op { … }; Data; struct CoerceTag { … }; FunctionNullptrTest; IsNullptrCompatible; template <typename T, std::enable_if_t<!IsNullptrCompatible<T>, int> = 0> constexpr bool isEmptyFunction(T const&) { … } template <typename T, std::enable_if_t<IsNullptrCompatible<T>, int> = 0> constexpr bool isEmptyFunction(T const& t) { … } CallableResult; CallableNoexcept; IfSafeResultImpl; #if defined(_MSC_VER) // Need a workaround for MSVC to avoid the inscrutable error: // // folly\function.h(...) : fatal error C1001: An internal error has // occurred in the compiler. // (compiler file 'f:\dd\vctools\compiler\utc\src\p2\main.c', line 258) // To work around this problem, try simplifying or changing the program // near the locations listed above. template <typename T> using CallArg = T&&; #else CallArg; #endif template <typename F, bool Nx, typename R, typename... A> class FunctionTraitsSharedProxy { … }; template < typename Fun, bool Small, bool Nx, typename ReturnType, typename... Args> ReturnType call_(Args... args, Data& p) noexcept(Nx) { … } template <typename FunctionType> struct FunctionTraits; FunctionTraits<ReturnType (Args...)>; FunctionTraits<ReturnType (Args...) const>; FunctionTraits<ReturnType (Args...) noexcept>; FunctionTraits<ReturnType (Args...) const noexcept>; // These are control functions. They type-erase the operations of move- // construction, destruction, and conversion to bool. // // The interface operations are noexcept, so the implementations are as well. // Having the implementations be noexcept in the type permits callers to omit // exception-handling machinery. // // This is intentionally instantiated per size rather than per function in order // to minimize the number of instantiations. It would be safe to minimize // instantiations even more by simply having a single non-template function that // copies sizeof(Data) bytes rather than only copying sizeof(Fun) bytes, but // then for small function types it would be likely to cross cache lines without // need. But it is only necessary to handle those sizes which are multiples of // the alignof(Data), and to round up other sizes. struct DispatchSmallTrivial { … }; struct DispatchBigTrivial { … }; struct DispatchSmall { … }; struct DispatchBig { … }; template <bool InSitu, bool IsTriv> struct Dispatch; template <> struct Dispatch<true, true> : DispatchSmallTrivial { … }; template <> struct Dispatch<true, false> : DispatchSmall { … }; template <> struct Dispatch<false, true> : DispatchBigTrivial { … }; template <> struct Dispatch<false, false> : DispatchBig { … }; DispatchOf; // This cannot be done inseide `Function` class, because the word // `Function` there refers to the instantion and not the template. is_instantiation_of_folly_function_v; } // namespace function } // namespace detail template <typename FunctionType> class Function final : private detail::function::FunctionTraits<FunctionType> { … }; template <typename FunctionType> void swap(Function<FunctionType>& lhs, Function<FunctionType>& rhs) noexcept { … } template <typename FunctionType> bool operator==(const Function<FunctionType>& fn, std::nullptr_t) { … } template <typename FunctionType> bool operator==(std::nullptr_t, const Function<FunctionType>& fn) { … } template <typename FunctionType> bool operator!=(const Function<FunctionType>& fn, std::nullptr_t) { … } template <typename FunctionType> bool operator!=(std::nullptr_t, const Function<FunctionType>& fn) { … } /** * Casts a `folly::Function` from non-const to a const signature. * * NOTE: The name of `constCastFunction` should warn you that something * potentially dangerous is happening. As a matter of fact, using * `std::function` always involves this potentially dangerous aspect, which * is why it is not considered fully const-safe or even const-correct. * However, in most of the cases you will not need the dangerous aspect at all. * Either you do not require invocation of the function from a const context, * in which case you do not need to use `constCastFunction` and just * use a non-const `folly::Function`. Or, you may need invocation from const, * but the callable you are wrapping does not mutate its state (e.g. it is a * class object and implements `operator() const`, or it is a normal, * non-mutable lambda), in which case you can wrap the callable in a const * `folly::Function` directly, without using `constCastFunction`. * Only if you require invocation from a const context of a callable that * may mutate itself when invoked you have to go through the above procedure. * However, in that case what you do is potentially dangerous and requires * the equivalent of a `const_cast`, hence you need to call * `constCastFunction`. * * @param that a non-const folly::Function. */ template <typename ReturnType, typename... Args> Function<ReturnType(Args...) const> constCastFunction( Function<ReturnType(Args...)>&& that) noexcept { … } template <typename ReturnType, typename... Args> Function<ReturnType(Args...) const> constCastFunction( Function<ReturnType(Args...) const>&& that) noexcept { … } template <typename ReturnType, typename... Args> Function<ReturnType(Args...) const noexcept> constCastFunction( Function<ReturnType(Args...) noexcept>&& that) noexcept { … } template <typename ReturnType, typename... Args> Function<ReturnType(Args...) const noexcept> constCastFunction( Function<ReturnType(Args...) const noexcept>&& that) noexcept { … } namespace detail { template <typename Void, typename> struct function_ctor_deduce_; function_ctor_deduce_<std::enable_if_t<std::is_function<std::remove_pointer_t<P>>::value>, P>; function_ctor_deduce_<void_t<decltype(&F::operator())>, F>; function_ctor_deduce_t; } // namespace detail template <typename F> Function(F) -> Function<detail::function_ctor_deduce_t<F>>; /** * @class folly::FunctionRef * * A reference wrapper for callable objects * * FunctionRef is similar to std::reference_wrapper, but the template parameter * is the function signature type rather than the type of the referenced object. * A folly::FunctionRef is cheap to construct as it contains only a pointer to * the referenced callable and a pointer to a function which invokes the * callable. * * The user of FunctionRef must be aware of the reference semantics: storing a * copy of a FunctionRef is potentially dangerous and should be avoided unless * the referenced object definitely outlives the FunctionRef object. Thus any * function that accepts a FunctionRef parameter should only use it to invoke * the referenced function and not store a copy of it. Knowing that FunctionRef * itself has reference semantics, it is generally okay to use it to reference * lambdas that capture by reference. */ template <typename FunctionType> class FunctionRef; FunctionRef<ReturnType (Args...)>; } // namespace folly