/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * A generic callable type that can be initialized from any compatible callable, * suitable for use as a function argument for the duration of the function * call (and no longer). */ #ifndef mozilla_FunctionRef_h #define mozilla_FunctionRef_h #include "mozilla/OperatorNewExtensions.h" // mozilla::NotNull, ::operator new #include // std::nullptr_t #include // std::{declval,integral_constant}, std::is_{convertible,same,void}_v, std::{enable_if,remove_reference,remove_cv}_t // This concept and its implementation are substantially inspired by foonathan's // prior art: // // https://foonathan.net/2017/01/function-ref-implementation/ // https://github.com/foonathan/type_safe/blob/2017851053f8dd268372f1612865792c5c621570/include/type_safe/reference.hpp namespace mozilla { namespace detail { // Template helper to determine if |Returned| is a return type compatible with // |Required|: if the former converts to the latter, or if |Required| is |void| // and nothing is returned. template using CompatibleReturnType = std::integral_constant || std::is_convertible_v>; // Template helper to check if |Func| called with |Params| arguments returns // a type compatible with |Ret|. template using EnableMatchingFunction = std::enable_if_t< CompatibleReturnType< decltype(std::declval()(std::declval()...)), Ret>::value, int>; struct MatchingFunctionPointerTag {}; struct MatchingFunctorTag {}; struct InvalidFunctorTag {}; // Template helper to determine the proper way to store |Callable|: as function // pointer, as pointer to object, or unstorable. template struct GetCallableTag { // Match the case where |Callable| is a compatible function pointer or // converts to one. (|+obj| invokes such a conversion.) template static MatchingFunctionPointerTag test( int, T& obj, EnableMatchingFunction = 0); // Match the case where |Callable| is callable but can't be converted to a // function pointer. (|short| is a worse match for 0 than |int|, causing the // function pointer match to be preferred if both apply.) template static MatchingFunctorTag test(short, T& obj, EnableMatchingFunction = 0); // Match all remaining cases. (Any other match is preferred to an ellipsis // match.) static InvalidFunctorTag test(...); using Type = decltype(test(0, std::declval())); }; // If the callable is |nullptr|, |std::declval()| will be an // error. Provide a specialization for |nullptr| that will fail substitution. template struct GetCallableTag {}; template using EnableFunctionTag = std::enable_if_t< std::is_same_v::Type, Result>, int>; } // namespace detail /** * An efficient, type-erasing, non-owning reference to a callable. It is * intended for use as the type of a function parameter that is not used after * the function in question returns. * * This class does not own the callable, so in general it is unsafe to store a * FunctionRef. */ template class FunctionRef; template class FunctionRef { union Payload; // |FunctionRef| stores an adaptor function pointer, determined by the // arguments passed to the constructor. That adaptor will perform the steps // needed to invoke the callable passed at construction time. using Adaptor = Ret (*)(const Payload& aPayload, Params... aParams); // If |FunctionRef|'s callable can be stored as a function pointer, that // function pointer is stored after being cast to this *different* function // pointer type. |mAdaptor| then casts back to the original type to call it. // ([expr.reinterpret.cast]p6 guarantees that A->B->A function pointer casts // produce the original function pointer value.) An outlandish signature is // used to emphasize that the exact function pointer type doesn't matter. using FuncPtr = Payload***** (*)(Payload*****); /** * An adaptor function (used by this class's function call operator) that * invokes the callable in |mPayload|, forwarding arguments and converting * return type as needed. */ const Adaptor mAdaptor; /** Storage for the wrapped callable value. */ union Payload { // This arm is used if |FunctionRef| is passed a compatible function pointer // or a lambda/callable that converts to a compatible function pointer. FuncPtr mFuncPtr; // This arm is used if |FunctionRef| is passed some other callable or // |nullptr|. void* mObject; } mPayload; template static Ret CallFunctionPointer(const Payload& aPayload, Params... aParams) noexcept { auto func = reinterpret_cast(aPayload.mFuncPtr); return static_cast(func(static_cast(aParams)...)); } template FunctionRef(detail::MatchingFunctionPointerTag, Ret2 (*aFuncPtr)(Params2...)) : mAdaptor(&CallFunctionPointer) { ::new (KnownNotNull, &mPayload.mFuncPtr) FuncPtr(reinterpret_cast(aFuncPtr)); } public: /** * Construct a |FunctionRef| that's like a null function pointer that can't be * called. */ MOZ_IMPLICIT FunctionRef(std::nullptr_t) noexcept : mAdaptor(nullptr) { // This is technically unnecessary, but it seems best to always initialize // a union arm. ::new (KnownNotNull, &mPayload.mObject) void*(nullptr); } FunctionRef() : FunctionRef(nullptr) {} /** * Constructs a |FunctionRef| from an object callable with |Params| arguments, * that returns a type convertible to |Ret|, where the callable isn't * convertible to function pointer (often because it contains some internal * state). For example: * * int x = 5; * auto doSideEffect = [&x]{ x++; }; // state is captured reference to |x| * FunctionRef f(doSideEffect); */ template < typename Callable, typename = detail::EnableFunctionTag, typename std::enable_if_t>, FunctionRef>>* = nullptr> MOZ_IMPLICIT FunctionRef(Callable& aCallable) noexcept : mAdaptor([](const Payload& aPayload, Params... aParams) { auto& func = *static_cast(aPayload.mObject); return static_cast(func(static_cast(aParams)...)); }) { ::new (KnownNotNull, &mPayload.mObject) void*(&aCallable); } /** * Constructs a |FunctionRef| from an value callable with |Params| arguments, * that returns a type convertible to |Ret|, where the callable is stateless * and is (or is convertible to) a function pointer. For example: * * // Exact match * double twice(double d) { return d * 2; } * FunctionRef func1(&twice); * * // Compatible match * float thrice(long double d) { return static_cast(d) * 3; } * FunctionRef func2(&thrice); * * // Non-generic lambdas that don't capture anything have a conversion * // function to the appropriate function pointer type. * FunctionRef f([](long double){ return 'c'; }); */ template > MOZ_IMPLICIT FunctionRef(const Callable& aCallable) noexcept : FunctionRef(detail::MatchingFunctionPointerTag{}, +aCallable) {} /** Call the callable stored in this with the given arguments. */ Ret operator()(Params... params) const { return mAdaptor(mPayload, static_cast(params)...); } /** Return true iff this wasn't created from |nullptr|. */ explicit operator bool() const noexcept { return mAdaptor != nullptr; } }; } /* namespace mozilla */ #endif /* mozilla_FunctionRef_h */