[Chromium] 閉包任務的建立

leno米雷發表於2024-12-02

OnceCallback OnceClosure RepeatingCallback RepeatingClosure

Closure是給訊息迴圈使用的內部任務,特點是返回值和引數都是void,不需要額外的執行環境,是一個完整的可以直接執行的閉包任務。

Callback是繫結閉包,用於繫結函式,自由使用的型別。

BindState

函式指標儲存在BindState物件的functor_成員中,繫結的引數也存在這個物件中。

  // 真正的函式指標和呼叫bind時候傳入的繫結引數
  Functor functor_;
  BoundArgsTuple bound_args_;

也就是說真正到執行函式的時候一定是需要BindState物件參與。

BindStateBase

其中的polymorphic_invoke_的成員變數儲存了Invoker型別的靜態方法RunOnce或者Run

using InvokeFuncStorage = void (*)(); // 臨時轉換用的萬能儲存用函式指標型別
InvokeFuncStorage polymorphic_invoke_; // BindStateBase持有這個

polymorphic_invoke_可以說是把BindState物件中儲存的繫結的引數和執行時未繫結的參與合併然後呼叫函式指標的關鍵。

InvokeHelper

可以幫助探測方法的物件指標是否可用。是在Traits最終呼叫之前外包的最後一層

怎麼把void(*)()模板函式和呼叫Bind的時候傳入的函式指標關聯起來

// callback.h
// RepeatingCallback
// 下面是組裝好的閉包執行的程式碼,可以看出拿到了PolymorphicInvoke型別,繫結的引數儲存在bind_state中
R Run(Args... args) const& {
    CHECK(!holder_.is_null());

    // Keep `bind_state` alive at least until after the invocation to ensure all
    // bound `Unretained` arguments remain protected by MiraclePtr.
    scoped_refptr<internal::BindStateBase> bind_state = holder_.bind_state();

    PolymorphicInvoke f =
        reinterpret_cast<PolymorphicInvoke>(holder_.polymorphic_invoke());
    return f(bind_state.get(), std::forward<Args>(args)...);
  }

PolymorphicInvoke型別如下所示,其中R是繫結閉包的返回值的型別,在傳入的引數之前,還有一個BindStateBase型別的指標,下面的Args模板引數列表代表的是Run,函式執行的時候傳入的補充引數

// Args are binded args
using PolymorphicInvoke = R (*)(internal::BindStateBase*,
                                  internal::PassingType<Args>...);

來源如下所示,是從BindStateBase中拿到的

using InvokeFuncStorage = void (*)(); // 臨時轉換用的萬能儲存用函式指標型別
InvokeFuncStorage polymorphic_invoke_; // BindStateBase持有這個

// 透過函式返回
InvokeFuncStorage polymorphic_invoke() const {
    return bind_state_->polymorphic_invoke_;
  }

但是polymorphic_invoke_是哪來的?其實它並非原本繫結的函式的函式指標,而是一層包裝,這個包裝的訂一可以看BindImpl函式,這個包裝的函式如下所示

// struct Invoker
// Invoker<Traits, StorageType, R(UnboundArgs...)>
static R RunOnce(BindStateBase* base,
                   PassingType<UnboundArgs>... unbound_args) {
    auto* const storage = static_cast<StorageType*>(base);
    return RunImpl(std::move(storage->functor_),
                   std::move(storage->bound_args_), Indices(),
                   std::forward<UnboundArgs>(unbound_args)...);
  }
 // 和RunOnce的區別可以看一下繫結引數的傳遞方式,Once是移動操作,這裡是複製操作
  static R Run(BindStateBase* base, PassingType<UnboundArgs>... unbound_args) {
    auto* const storage = static_cast<const StorageType*>(base);
    return RunImpl(storage->functor_, storage->bound_args_, Indices(),
                   std::forward<UnboundArgs>(unbound_args)...);
  }

這裡的storage->functor_有點像真正的函式指標,但是真正的函式指標是存在BindState型別裡面的,這個storage的型別StorageType是什麼來頭,而且還用了static_cast型別轉換,如果轉換成BindState的話不應該使用這種轉換才對。這得看這個Invoker型別的物件是怎麼構造出來的

下面是繫結一個函式的時候的具體程式碼

template <typename Functor, typename... Args>
  static auto BindImpl(Functor&& functor, Args&&... args) {
    // There are a lot of variables and type aliases here. An example will be
    // illustrative. Assume we call:
    // ```
    //   struct S {
    //     double f(int, const std::string&);
    //   } s;
    //   int16_t i;
    //   BindOnce(&S::f, Unretained(&s), i);
    // ```
    // This means our template params are:
    // ```
    //   template <typename> class CallbackT = OnceCallback
    //   typename Functor = double (S::*)(int, const std::string&)
    //   typename... Args =
    //       UnretainedWrapper<S, unretained_traits::MayNotDangle>, int16_t
    // ```
    // And the implementation below is effectively:
    // ```
    //   using Traits = struct {
    //     using RunType = double(S*, int, const std::string&);
    //     static constexpr bool is_method = true;
    //     static constexpr bool is_nullable = true;
    //     static constexpr bool is_callback = false;
    //     static constexpr bool is_stateless = true;
    //     ...
    //   };
    //   using ValidatedUnwrappedTypes = struct {
    //     using Type = TypeList<S*, int16_t>;
    //     static constexpr bool value = true;
    //   };
    //   using BoundArgsList = TypeList<S*, int16_t>;
    //   using RunParamsList = TypeList<S*, int, const std::string&>;
    //   using BoundParamsList = TypeList<S*, int>;
    //   using ValidatedBindState = struct {
    //     using Type =
    //         BindState<double (S::*)(int, const std::string&),
    //                   UnretainedWrapper<S, unretained_traits::MayNotDangle>,
    //                   int16_t>;
    //     static constexpr bool value = true;
    //   };
    //   if constexpr (true) {
    //     using UnboundRunType = double(const std::string&);
    //     using CallbackType = OnceCallback<double(const std::string&)>;
    //     ...
    // ```
    using Traits = FunctorTraits<TransformToUnwrappedType<kIsOnce, Functor&&>,
                                 TransformToUnwrappedType<kIsOnce, Args&&>...>;
    if constexpr (TraitsAreInstantiable<Traits>::value) {
      using ValidatedUnwrappedTypes =
          ValidateUnwrappedTypeList<kIsOnce, Traits::is_method, Args&&...>;
      using BoundArgsList = TypeList<Args...>;
      using RunParamsList = ExtractArgs<typename Traits::RunType>;
      using BoundParamsList = TakeTypeListItem<sizeof...(Args), RunParamsList>;
      using ValidatedBindState =
          ValidateBindStateType<Traits::is_method, Traits::is_nullable,
                                Traits::is_callback, Functor, Args...>;
      // This conditional checks if each of the `args` matches to the
      // corresponding param of the target function. This check does not affect
      // the behavior of `Bind()`, but its error message should be more
      // readable.
      if constexpr (std::conjunction_v<
                        NotFunctionRef<Functor>, IsStateless<Traits>,
                        ValidatedUnwrappedTypes,
                        ParamsCanBeBound<
                            Traits::is_method,
                            std::make_index_sequence<sizeof...(Args)>,
                            BoundArgsList,
                            typename ValidatedUnwrappedTypes::Type,
                            BoundParamsList>,
                        ValidatedBindState>) {
        using UnboundRunType =
            MakeFunctionType<ExtractReturnType<typename Traits::RunType>,
                             DropTypeListItem<sizeof...(Args), RunParamsList>>;
        using CallbackType = CallbackT<UnboundRunType>;

        // Store the invoke func into `PolymorphicInvoke` before casting it to
        // `InvokeFuncStorage`, so that we can ensure its type matches to
        // `PolymorphicInvoke`, to which `CallbackType` will cast back.
        typename CallbackType::PolymorphicInvoke invoke_func;
        using Invoker =
            Invoker<Traits, typename ValidatedBindState::Type, UnboundRunType>;
        if constexpr (kIsOnce) {
          invoke_func = Invoker::RunOnce;
        } else {
          invoke_func = Invoker::Run;
        }
       // 可以看到這裡傳入了包裝用的函式
        return CallbackType(ValidatedBindState::Type::Create(
            reinterpret_cast<BindStateBase::InvokeFuncStorage>(invoke_func),
            std::forward<Functor>(functor), std::forward<Args>(args)...));
      }
    }
  }

ValidatedBindState::Type這個型別萃取的結果都是BindState型別(根據計算結果,傳入的模板引數不同)所以說。上面的storage->functor_就確實是真正的函式指標了。

模板超程式設計相關記錄

如何萃取出函式的返回值和引數等內容

函式返回值和引數列表

// For most functors, the traits should not depend on how the functor is passed,
// so decay the functor.
template <typename Functor, typename... BoundArgs>
// This requirement avoids "implicit instantiation of undefined template" errors
// when the underlying `DecayedFunctorTraits<>` cannot be instantiated. Instead,
// this template will also not be instantiated, and the caller can detect and
// handle that.
  requires IsComplete<DecayedFunctorTraits<std::decay_t<Functor>, BoundArgs...>>
struct FunctorTraits<Functor, BoundArgs...>
    : DecayedFunctorTraits<std::decay_t<Functor>, BoundArgs...> {};
    
// Functions.
template <typename R, typename... Args, typename... BoundArgs>
struct DecayedFunctorTraits<R (*)(Args...), BoundArgs...> {
  using RunType = R(Args...);
  static constexpr bool is_method = false;
  static constexpr bool is_nullable = true;
  static constexpr bool is_callback = false;
  static constexpr bool is_stateless = true;

  template <typename Function, typename... RunArgs>
  static R Invoke(Function&& function, RunArgs&&... args) {
    return std::forward<Function>(function)(std::forward<RunArgs>(args)...);
  }
};

// Methods.
template <typename R,
          typename Receiver,
          typename... Args,
          typename... BoundArgs>
struct DecayedFunctorTraits<R (Receiver::*)(Args...), BoundArgs...> {
  using RunType = R(Receiver*, Args...);
  static constexpr bool is_method = true;
  static constexpr bool is_nullable = true;
  static constexpr bool is_callback = false;
  static constexpr bool is_stateless = true;

  template <typename Method, typename ReceiverPtr, typename... RunArgs>
  static R Invoke(Method method,
                  ReceiverPtr&& receiver_ptr,
                  RunArgs&&... args) {
    return ((*receiver_ptr).*method)(std::forward<RunArgs>(args)...);
  }
};

使用這個函式萃取,得到函式簽名,重點關注RunType,針對不同的函式指標型別有不同的特化模板萃取出函式簽名,例如上述程式碼中的普通函式和型別方法,對應兩種特化。

這個程式碼把RunType定義為一個函式簽名。這樣就隨時都可以取出返回值和引數列表。下述程式碼就可以完成這個操作。

// Implements `ExtractArgs` and `ExtractReturnType`.
template <typename Signature>
struct ExtractArgsImpl;

template <typename R, typename... Args>
struct ExtractArgsImpl<R(Args...)> {
  using ReturnType = R;
  using ArgsList = TypeList<Args...>;
};

根據傳入的函式指標和繫結的引數,計算出剩餘引數的模板引數列表

程式碼如下所示,RunType不用多說,上面解釋過了,是函式簽名,ExtractReturnType是萃取出函式簽名的返回值型別,主要看一下DropTypeListItem這個是計算出未繫結的引數列表

using RunParamsList = ExtractArgs<typename Traits::RunType>;
using UnboundRunType =
            MakeFunctionType<ExtractReturnType<typename Traits::RunType>,
                             DropTypeListItem<sizeof...(Args), RunParamsList>>;

Args是繫結引數的模板引數列表,RunParamsList是整個函式簽名的所有引數列表,DropTypeListItem是怎麼計算出來的呢

// Implements `DropTypeListItem`.
template <size_t n, typename List>
  requires is_instantiation<TypeList, List>
struct DropTypeListItemImpl {
  using Type = List;
};

template <size_t n, typename T, typename... List>
  requires(n > 0)
struct DropTypeListItemImpl<n, TypeList<T, List...>>
    : DropTypeListItemImpl<n - 1, TypeList<List...>> {};

// A type-level function that drops `n` list items from a given `TypeList`.
template <size_t n, typename List>
using DropTypeListItem = typename DropTypeListItemImpl<n, List>::Type;

這裡是經典的模板引數列表展開,但是用到了size限制,計算出已經繫結的引數的個數,拆引數包的時候拆掉執行個數,留下的List就是我們需要的未繫結的引數列表了,其中requires是C++20的特性,可以用引數展開和引數個數為0的特化來做替換

template <size_t n, typename List>
struct DropTypeListItemImpl;

template <size_t n, typename T, typename... List>
struct DropTypeListItemImpl<n, TypeList<T, List...>>
    : DropTypeListItemImpl<n - 1, TypeList<List...>> {};

// specialization
template <typename T, typename... List>
struct DropTypeListItemImpl<0, TypeList<T, List...>> {
  using Type = TypeList<T, List...>;
};

template <>
struct DropTypeListItemImpl<0, TypeList<>> {
  using Type = TypeList<>;
};

template <size_t n, typename List>
using DropTypeListItem = typename DropTypeListItemImpl<n, List>::Type;

相關文章