tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FunctionRef.h (8983B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * A generic callable type that can be initialized from any compatible callable,
      9 * suitable for use as a function argument for the duration of the function
     10 * call (and no longer).
     11 */
     12 
     13 #ifndef mozilla_FunctionRef_h
     14 #define mozilla_FunctionRef_h
     15 
     16 #include "mozilla/OperatorNewExtensions.h"  // mozilla::NotNull, ::operator new
     17 
     18 #include <cstddef>      // std::nullptr_t
     19 #include <type_traits>  // std::{declval,integral_constant}, std::is_{convertible,same,void}_v, std::{enable_if,remove_reference,remove_cv}_t
     20 #include <utility>      // std::forward
     21 
     22 // This concept and its implementation are substantially inspired by foonathan's
     23 // prior art:
     24 //
     25 // https://foonathan.net/2017/01/function-ref-implementation/
     26 // https://github.com/foonathan/type_safe/blob/2017851053f8dd268372f1612865792c5c621570/include/type_safe/reference.hpp
     27 
     28 namespace mozilla {
     29 
     30 namespace detail {
     31 
     32 // Template helper to determine if |Returned| is a return type compatible with
     33 // |Required|: if the former converts to the latter, or if |Required| is |void|
     34 // and nothing is returned.
     35 template <typename Returned, typename Required>
     36 using CompatibleReturnType =
     37    std::integral_constant<bool, std::is_void_v<Required> ||
     38                                     std::is_convertible_v<Returned, Required>>;
     39 
     40 // Template helper to check if |Func| called with |Params| arguments returns
     41 // a type compatible with |Ret|.
     42 template <typename Func, typename Ret, typename... Params>
     43 using EnableMatchingFunction = std::enable_if_t<
     44    CompatibleReturnType<
     45        decltype(std::declval<Func&>()(std::declval<Params>()...)), Ret>::value,
     46    int>;
     47 
     48 struct MatchingFunctionPointerTag {};
     49 struct MatchingFunctorTag {};
     50 struct InvalidFunctorTag {};
     51 
     52 // Template helper to determine the proper way to store |Callable|: as function
     53 // pointer, as pointer to object, or unstorable.
     54 template <typename Callable, typename Ret, typename... Params>
     55 struct GetCallableTag {
     56  // Match the case where |Callable| is a compatible function pointer or
     57  // converts to one.  (|+obj| invokes such a conversion.)
     58  template <typename T>
     59  static MatchingFunctionPointerTag test(
     60      int, T& obj, EnableMatchingFunction<decltype(+obj), Ret, Params...> = 0);
     61 
     62  // Match the case where |Callable| is callable but can't be converted to a
     63  // function pointer.  (|short| is a worse match for 0 than |int|, causing the
     64  // function pointer match to be preferred if both apply.)
     65  template <typename T>
     66  static MatchingFunctorTag test(short, T& obj,
     67                                 EnableMatchingFunction<T, Ret, Params...> = 0);
     68 
     69  // Match all remaining cases.  (Any other match is preferred to an ellipsis
     70  // match.)
     71  static InvalidFunctorTag test(...);
     72 
     73  using Type = decltype(test(0, std::declval<Callable&>()));
     74 };
     75 
     76 // If the callable is |nullptr|, |std::declval<std::nullptr_t&>()| will be an
     77 // error.  Provide a specialization for |nullptr| that will fail substitution.
     78 template <typename Ret, typename... Params>
     79 struct GetCallableTag<std::nullptr_t, Ret, Params...> {};
     80 
     81 template <typename Result, typename Callable, typename Ret, typename... Params>
     82 using EnableFunctionTag = std::enable_if_t<
     83    std::is_same_v<typename GetCallableTag<Callable, Ret, Params...>::Type,
     84                   Result>,
     85    int>;
     86 
     87 }  // namespace detail
     88 
     89 /**
     90 * An efficient, type-erasing, non-owning reference to a callable. It is
     91 * intended for use as the type of a function parameter that is not used after
     92 * the function in question returns.
     93 *
     94 * This class does not own the callable, so in general it is unsafe to store a
     95 * FunctionRef.
     96 */
     97 template <typename Fn>
     98 class MOZ_TEMPORARY_CLASS FunctionRef;
     99 
    100 template <typename Ret, typename... Params>
    101 class MOZ_TEMPORARY_CLASS FunctionRef<Ret(Params...)> {
    102  union Payload;
    103 
    104  // |FunctionRef| stores an adaptor function pointer, determined by the
    105  // arguments passed to the constructor.  That adaptor will perform the steps
    106  // needed to invoke the callable passed at construction time.
    107  using Adaptor = Ret (*)(const Payload& aPayload, Params... aParams);
    108 
    109  // If |FunctionRef|'s callable can be stored as a function pointer, that
    110  // function pointer is stored after being cast to this *different* function
    111  // pointer type.  |mAdaptor| then casts back to the original type to call it.
    112  // ([expr.reinterpret.cast]p6 guarantees that A->B->A function pointer casts
    113  // produce the original function pointer value.)  An outlandish signature is
    114  // used to emphasize that the exact function pointer type doesn't matter.
    115  using FuncPtr = Payload***** (*)(Payload*****);
    116 
    117  /**
    118   * An adaptor function (used by this class's function call operator) that
    119   * invokes the callable in |mPayload|, forwarding arguments and converting
    120   * return type as needed.
    121   */
    122  const Adaptor mAdaptor;
    123 
    124  /** Storage for the wrapped callable value. */
    125  union Payload {
    126    // This arm is used if |FunctionRef| is passed a compatible function pointer
    127    // or a lambda/callable that converts to a compatible function pointer.
    128    FuncPtr mFuncPtr;
    129 
    130    // This arm is used if |FunctionRef| is passed some other callable or
    131    // |nullptr|.
    132    void* mObject;
    133  } mPayload;
    134 
    135  template <typename RealFuncPtr>
    136  static Ret CallFunctionPointer(const Payload& aPayload,
    137                                 Params... aParams) noexcept {
    138    auto func = reinterpret_cast<RealFuncPtr>(aPayload.mFuncPtr);
    139    return static_cast<Ret>(func(std::forward<Params>(aParams)...));
    140  }
    141 
    142  template <typename Ret2, typename... Params2>
    143  FunctionRef(detail::MatchingFunctionPointerTag, Ret2 (*aFuncPtr)(Params2...))
    144      : mAdaptor(&CallFunctionPointer<Ret2 (*)(Params2...)>) {
    145    ::new (KnownNotNull, &mPayload.mFuncPtr)
    146        FuncPtr(reinterpret_cast<FuncPtr>(aFuncPtr));
    147  }
    148 
    149 public:
    150  /**
    151   * Construct a |FunctionRef| that's like a null function pointer that can't be
    152   * called.
    153   */
    154  MOZ_IMPLICIT FunctionRef(std::nullptr_t) noexcept : mAdaptor(nullptr) {
    155    // This is technically unnecessary, but it seems best to always initialize
    156    // a union arm.
    157    ::new (KnownNotNull, &mPayload.mObject) void*(nullptr);
    158  }
    159 
    160  FunctionRef() : FunctionRef(nullptr) {}
    161 
    162  /**
    163   * Constructs a |FunctionRef| from an object callable with |Params| arguments,
    164   * that returns a type convertible to |Ret|, where the callable isn't
    165   * convertible to function pointer (often because it contains some internal
    166   * state).  For example:
    167   *
    168   *   int x = 5;
    169   *   DoSomething([&x] { x++; });
    170   */
    171  template <typename Callable,
    172            typename = detail::EnableFunctionTag<detail::MatchingFunctorTag,
    173                                                 Callable, Ret, Params...>,
    174            typename std::enable_if_t<!std::is_same_v<
    175                std::remove_cv_t<std::remove_reference_t<Callable>>,
    176                FunctionRef>>* = nullptr>
    177  MOZ_IMPLICIT FunctionRef(Callable&& aCallable MOZ_LIFETIME_BOUND) noexcept
    178      : mAdaptor([](const Payload& aPayload, Params... aParams) {
    179          auto& func = *static_cast<std::remove_reference_t<Callable>*>(
    180              aPayload.mObject);
    181          return static_cast<Ret>(func(std::forward<Params>(aParams)...));
    182        }) {
    183    ::new (KnownNotNull, &mPayload.mObject) void*(&aCallable);
    184  }
    185 
    186  /**
    187   * Constructs a |FunctionRef| from an value callable with |Params| arguments,
    188   * that returns a type convertible to |Ret|, where the callable is stateless
    189   * and is (or is convertible to) a function pointer.  For example:
    190   *
    191   *   // Exact match
    192   *   double twice(double d) { return d * 2; }
    193   *   FunctionRef<double(double)> func1(&twice);
    194   *
    195   *   // Compatible match
    196   *   float thrice(long double d) { return static_cast<float>(d) * 3; }
    197   *   FunctionRef<double(double)> func2(&thrice);
    198   *
    199   *   // Non-generic lambdas that don't capture anything have a conversion
    200   *   // function to the appropriate function pointer type.
    201   *   FunctionRef<int(double)> f([](long double){ return 'c'; });
    202   */
    203  template <typename Callable,
    204            typename = detail::EnableFunctionTag<
    205                detail::MatchingFunctionPointerTag, Callable, Ret, Params...>>
    206  MOZ_IMPLICIT FunctionRef(const Callable& aCallable) noexcept
    207      : FunctionRef(detail::MatchingFunctionPointerTag{}, +aCallable) {}
    208 
    209  /** Call the callable stored in this with the given arguments. */
    210  Ret operator()(Params... params) const {
    211    return mAdaptor(mPayload, std::forward<Params>(params)...);
    212  }
    213 
    214  /** Return true iff this wasn't created from |nullptr|. */
    215  explicit operator bool() const noexcept { return mAdaptor != nullptr; }
    216 };
    217 
    218 } /* namespace mozilla */
    219 
    220 #endif /* mozilla_FunctionRef_h */