From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- mfbt/FunctionRef.h | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 mfbt/FunctionRef.h (limited to 'mfbt/FunctionRef.h') diff --git a/mfbt/FunctionRef.h b/mfbt/FunctionRef.h new file mode 100644 index 0000000000..374173d884 --- /dev/null +++ b/mfbt/FunctionRef.h @@ -0,0 +1,219 @@ +/* -*- 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 +#include // std::forward + +// 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 MOZ_TEMPORARY_CLASS FunctionRef; + +template +class MOZ_TEMPORARY_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(std::forward(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; + * DoSomething([&x] { x++; }); + */ + template , + typename std::enable_if_t>, + FunctionRef>>* = nullptr> + MOZ_IMPLICIT FunctionRef(Callable&& aCallable MOZ_LIFETIME_BOUND) noexcept + : mAdaptor([](const Payload& aPayload, Params... aParams) { + auto& func = *static_cast*>(aPayload.mObject); + return static_cast(func(std::forward(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, std::forward(params)...); + } + + /** Return true iff this wasn't created from |nullptr|. */ + explicit operator bool() const noexcept { return mAdaptor != nullptr; } +}; + +} /* namespace mozilla */ + +#endif /* mozilla_FunctionRef_h */ -- cgit v1.2.3