“vector”: 不是“std”的成员_libcxx 的 std::function 源码分析
鏈接:functional。其中 std::function 的主體內容在 2100 多行。
先來看 function 的頭部。
template<class _Rp, class ..._ArgTypes> class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>其中的 libcpp template vis 宏是用來 controlling symbol visibility 的,我們先不用管。我們先看兩個繼承,這兩個 maybe 類是 traits 類。模板元編程常見的技巧。如果 Rp 和 ArgTypes 滿足一元或者二元函數的模板模式(偏特化)的話,那么就繼承相應的 unary function 或者 binary function。如果不滿足的話就繼承空類。其中 unary function 定義是這樣。
template <class Arg, class Result> struct unary_function {typedef Arg argument_type;typedef Result result_type; };binary 類似。其實就是 typedef 了函數相關類型的空類。
接下來是 std::function 中的數據成員。我們可以看到條件編譯。
#ifndef _LIBCPP_ABI_OPTIMIZED_FUNCTIONtypedef __function::__value_func<_Rp(_ArgTypes...)> __func; #elsetypedef __function::__policy_func<_Rp(_ArgTypes...)> __func; #endif__func __f_;其中這個 libcpp abi optimized function 是后加的一個優化選項。可以看到原來用的是 value func 而優化之后用 policy func。具體的 patch 可以見PATCH D55045。兩者究竟有什么區別呢?我們瞧瞧。
我們去跟蹤 value func。可以看到 value func 的數據成員是這樣的。
template <class _Fp> class __value_func;template <class _Rp, class... _ArgTypes> class __value_func<_Rp(_ArgTypes...)> {typename aligned_storage<3 * sizeof(void*)>::type __buf_;typedef __base<_Rp(_ArgTypes...)> __func;__func* __f_;我們看到 value func 里面有一個 buf。這個 buf 是 3 個指針長度大小。我們這里假定在 64 位機器吧。那么這個 buf 就是 24 字節。然后還有一個 func 指針指向了一個 base 對象。所以我們得到了一個重要的結論:一個 value func 就是 4 個指針長度,32字節。base 對象是干嘛的?我們跟蹤一下。我們可以在代碼里看到這樣一句話。
// __base provides an abstract interface for copyable functors.然后是 base 類的定義。就是一個抽象的接口,里面一大堆 = 0 的函數,說是指向一個可拷貝的函子。那這個 buf 又是干嘛的呢?我們繼續挖 value func。
這是在 value func 的構造函數代碼段節選。
if (__function::__not_null(__f)) {_FunAlloc __af(__a);if (sizeof(_Fun) <= sizeof(__buf_) &&is_nothrow_copy_constructible<_Fp>::value &&is_nothrow_copy_constructible<_FunAlloc>::value){__f_ =::new ((void*)&__buf_) _Fun(_VSTD::move(__f), _Alloc(__af));}else{typedef __allocator_destructor<_FunAlloc> _Dp;unique_ptr<__func, _Dp> __hold(__af.allocate(1), _Dp(__af, 1));::new ((void*)__hold.get()) _Fun(_VSTD::move(__f), _Alloc(__a));__f_ = __hold.release();} }可以看到,我們的構造函數根據 sizeof Fun 有了差別。如果 buf 能裝得下 Fun(并且拷貝不會拋異常)那么我們的 func 指針直接去指向 buf。并且在 buf 上構造(replacement new)我們的 Fun。如果裝不下(或者拷貝拋異常)那么就只能 allocate 出來內存來存放這個 Fun 了。至于這里為什么用 unique ptr 這么迂回的方式,我也不是很懂,但是我猜測和異常有關。所以,我們又得到了一個重要的結論,如果 buf 上可以分配得下 Fun(并且拷貝不拋異常),那么直接在 Buf 上分配(棧)。否則會去 allocate 內存。
關于 value func 就說到這,value func 里面其他的東西都比較正常。其中 value func 的 swap 函數實現的也比較崎嶇,感興趣可以看看。
接下來是 policy func 了。首先我們先看 policy storage。
union __policy_storage {mutable char __small[sizeof(void*) * 2];void* __large; };可以看到這時候用一個 union 節省了內存。并且 small 的 size 變成了 2 個指針長度即 16 個字節。一般的函數指針是不會超過這個數的,但是其他的函子可以很膨脹。這里留一個思考題:為什么一般的函數指針不會超過 16 個字節,而不是 8 個字節?提示:成員函數指針。
這是一個配套 policy storage 使用的 traits 類。
template <typename _Fun> struct __use_small_storage: public _VSTD::integral_constant<bool, sizeof(_Fun) <= sizeof(__policy_storage) &&_LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&_VSTD::is_trivially_copy_constructible<_Fun>::value &&_VSTD::is_trivially_destructible<_Fun>::value> {};很顯然,如果 Fun 滿足條件那么 use small storage 里的 value 是 true,否則是 false。很簡單的模板元編程。注意這里的條件沒有 allocator 了。因為在 C++17 之后,std::function 就不用 allocator 了。
在看 policy func 之前,我們還得看一個類,invoker。
// __policy_invoker calls an instance of __alloc_func held in __policy_storage.template <class _Fp> struct __policy_invoker;template <class _Rp, class... _ArgTypes> struct __policy_invoker<_Rp(_ArgTypes...)> {typedef _Rp (*__Call)(const __policy_storage*,__fast_forward<_ArgTypes>...);__Call __call_;其中 fast forward 先不用管,是一個傳參策略的 traits。我們看到 policy invoker 里面有一個 Call 函數指針。這個函數指針指向的函數接受 policy storage 和函子的參數,然后返回函子的返回值。為什么需要這么一個類呢?我們往下看。
// Creates an invoker that calls the given instance of __func. template <typename _Fun> _LIBCPP_INLINE_VISIBILITY static __policy_invoker __create() {return __policy_invoker(&__call_impl<_Fun>); }這里返回了一個 policy invoker 對象,并且用 call impl 初始化了 Call 函數指針。再瞧瞧 call impl。
template <typename _Fun> static _Rp __call_impl(const __policy_storage* __buf,__fast_forward<_ArgTypes>... __args) {_Fun* __f = reinterpret_cast<_Fun*>(__use_small_storage<_Fun>::value? &__buf->__small: __buf->__large);return (*__f)(_VSTD::forward<_ArgTypes>(__args)...); }可以看到這個 call impl 函數先判定 Fun 存儲在哪里,然后就去調用它。
所以,為什么需要 invoker 呢?因為我們這里用的是(內存和指向其他內存的指針)的一個 union。所以取內存方式會不同,中間會差一層取地址的抽象(見源碼的 small 和 large 的取法,small 要多一層取地址)。
而 value func 為什么可以統一取內存的方式呢?因為 value func 的指向內存的指針 func 和 buf 是分開的,即使分配到了 buf 上,value func 的 func 指針依然會指向這個 buf。所以無論如何,只要通過這個 func 指針來獲取內存,一定就是沒錯的。沒有疑問。
好了,我們可以看 policy func 了。
// __policy_func uses a __policy and __policy_invoker to create a type-erased, // copyable functor.template <class _Fp> class __policy_func;template <class _Rp, class... _ArgTypes> class __policy_func<_Rp(_ArgTypes...)> {// Inline storage for small objects.__policy_storage __buf_;// Calls the value stored in __buf_. This could technically be part of// policy, but storing it here eliminates a level of indirection inside// operator().typedef __function::__policy_invoker<_Rp(_ArgTypes...)> __invoker;__invoker __invoker_;// The policy that describes how to move / copy / destroy __buf_. Never// null, even if the function is empty.const __policy* __policy_;可以看到首先 policy func 里面有 buf,16 個字節。然后是 invoker,8 個字節。最后是 policy 指針,8 個字節。所以加起來還是 32 個字節。其中 policy 類似于 base,也是提供如何進行基本操作的類的指針。
我們再來看看 policy func 的構造函數。
template <class _Fp, class _Alloc> _LIBCPP_INLINE_VISIBILITY __policy_func(_Fp&& __f, const _Alloc& __a): __policy_(__policy::__create_empty()) {typedef __alloc_func<_Fp, _Alloc, _Rp(_ArgTypes...)> _Fun;typedef allocator_traits<_Alloc> __alloc_traits;typedef typename __rebind_alloc_helper<__alloc_traits, _Fun>::type_FunAlloc;if (__function::__not_null(__f)){__invoker_ = __invoker::template __create<_Fun>();__policy_ = __policy::__create<_Fun>();_FunAlloc __af(__a);if (__use_small_storage<_Fun>()){::new ((void*)&__buf_.__small)_Fun(_VSTD::move(__f), _Alloc(__af));}else{typedef __allocator_destructor<_FunAlloc> _Dp;unique_ptr<_Fun, _Dp> __hold(__af.allocate(1), _Dp(__af, 1));::new ((void*)__hold.get())_Fun(_VSTD::move(__f), _Alloc(__af));__buf_.__large = __hold.release();}} }和 value func 差不多。不再贅述了。注意這里的分配內存之后并沒有賦值給一個指針,而僅僅是在 buf 上分配內存就完了。獲取函子并調用的操作是由 invoker 做的。
我覺得 std::function 的重點就是數據成員這部分。至于相關的成員函數,我覺得都比較正常,這里就不在多說什么了。
那,就這樣。
總結
以上是生活随笔為你收集整理的“vector”: 不是“std”的成员_libcxx 的 std::function 源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DellEMC UnityVSA 部署指
- 下一篇: 语言print如何实现连续输出_【每日一