“vector”: 不是“std”的成员_libcxx 的 std::function 源码分析
鏈接:functional。其中 std::function 的主體內(nèi)容在 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 的,我們先不用管。我們先看兩個(gè)繼承,這兩個(gè) maybe 類是 traits 類。模板元編程常見的技巧。如果 Rp 和 ArgTypes 滿足一元或者二元函數(shù)的模板模式(偏特化)的話,那么就繼承相應(yīng)的 unary function 或者 binary function。如果不滿足的話就繼承空類。其中 unary function 定義是這樣。
template <class Arg, class Result> struct unary_function {typedef Arg argument_type;typedef Result result_type; };binary 類似。其實(shí)就是 typedef 了函數(shù)相關(guān)類型的空類。
接下來是 std::function 中的數(shù)據(jù)成員。我們可以看到條件編譯。
#ifndef _LIBCPP_ABI_OPTIMIZED_FUNCTIONtypedef __function::__value_func<_Rp(_ArgTypes...)> __func; #elsetypedef __function::__policy_func<_Rp(_ArgTypes...)> __func; #endif__func __f_;其中這個(gè) libcpp abi optimized function 是后加的一個(gè)優(yōu)化選項(xiàng)。可以看到原來用的是 value func 而優(yōu)化之后用 policy func。具體的 patch 可以見PATCH D55045。兩者究竟有什么區(qū)別呢?我們瞧瞧。
我們?nèi)ジ?value func。可以看到 value func 的數(shù)據(jù)成員是這樣的。
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 里面有一個(gè) buf。這個(gè) buf 是 3 個(gè)指針長(zhǎng)度大小。我們這里假定在 64 位機(jī)器吧。那么這個(gè) buf 就是 24 字節(jié)。然后還有一個(gè) func 指針指向了一個(gè) base 對(duì)象。所以我們得到了一個(gè)重要的結(jié)論:一個(gè) value func 就是 4 個(gè)指針長(zhǎng)度,32字節(jié)。base 對(duì)象是干嘛的?我們跟蹤一下。我們可以在代碼里看到這樣一句話。
// __base provides an abstract interface for copyable functors.然后是 base 類的定義。就是一個(gè)抽象的接口,里面一大堆 = 0 的函數(shù),說是指向一個(gè)可拷貝的函子。那這個(gè) buf 又是干嘛的呢?我們繼續(xù)挖 value func。
這是在 value func 的構(gòu)造函數(shù)代碼段節(jié)選。
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();} }可以看到,我們的構(gòu)造函數(shù)根據(jù) sizeof Fun 有了差別。如果 buf 能裝得下 Fun(并且拷貝不會(huì)拋異常)那么我們的 func 指針直接去指向 buf。并且在 buf 上構(gòu)造(replacement new)我們的 Fun。如果裝不下(或者拷貝拋異常)那么就只能 allocate 出來內(nèi)存來存放這個(gè) Fun 了。至于這里為什么用 unique ptr 這么迂回的方式,我也不是很懂,但是我猜測(cè)和異常有關(guān)。所以,我們又得到了一個(gè)重要的結(jié)論,如果 buf 上可以分配得下 Fun(并且拷貝不拋異常),那么直接在 Buf 上分配(棧)。否則會(huì)去 allocate 內(nèi)存。
關(guān)于 value func 就說到這,value func 里面其他的東西都比較正常。其中 value func 的 swap 函數(shù)實(shí)現(xiàn)的也比較崎嶇,感興趣可以看看。
接下來是 policy func 了。首先我們先看 policy storage。
union __policy_storage {mutable char __small[sizeof(void*) * 2];void* __large; };可以看到這時(shí)候用一個(gè) union 節(jié)省了內(nèi)存。并且 small 的 size 變成了 2 個(gè)指針長(zhǎng)度即 16 個(gè)字節(jié)。一般的函數(shù)指針是不會(huì)超過這個(gè)數(shù)的,但是其他的函子可以很膨脹。這里留一個(gè)思考題:為什么一般的函數(shù)指針不會(huì)超過 16 個(gè)字節(jié),而不是 8 個(gè)字節(jié)?提示:成員函數(shù)指針。
這是一個(gè)配套 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。很簡(jiǎn)單的模板元編程。注意這里的條件沒有 allocator 了。因?yàn)樵?C++17 之后,std::function 就不用 allocator 了。
在看 policy func 之前,我們還得看一個(gè)類,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 先不用管,是一個(gè)傳參策略的 traits。我們看到 policy invoker 里面有一個(gè) Call 函數(shù)指針。這個(gè)函數(shù)指針指向的函數(shù)接受 policy storage 和函子的參數(shù),然后返回函子的返回值。為什么需要這么一個(gè)類呢?我們往下看。
// 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>); }這里返回了一個(gè) policy invoker 對(duì)象,并且用 call impl 初始化了 Call 函數(shù)指針。再瞧瞧 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)...); }可以看到這個(gè) call impl 函數(shù)先判定 Fun 存儲(chǔ)在哪里,然后就去調(diào)用它。
所以,為什么需要 invoker 呢?因?yàn)槲覀冞@里用的是(內(nèi)存和指向其他內(nèi)存的指針)的一個(gè) union。所以取內(nèi)存方式會(huì)不同,中間會(huì)差一層取地址的抽象(見源碼的 small 和 large 的取法,small 要多一層取地址)。
而 value func 為什么可以統(tǒng)一取內(nèi)存的方式呢?因?yàn)?value func 的指向內(nèi)存的指針 func 和 buf 是分開的,即使分配到了 buf 上,value func 的 func 指針依然會(huì)指向這個(gè) buf。所以無(wú)論如何,只要通過這個(gè) func 指針來獲取內(nèi)存,一定就是沒錯(cuò)的。沒有疑問。
好了,我們可以看 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 個(gè)字節(jié)。然后是 invoker,8 個(gè)字節(jié)。最后是 policy 指針,8 個(gè)字節(jié)。所以加起來還是 32 個(gè)字節(jié)。其中 policy 類似于 base,也是提供如何進(jìn)行基本操作的類的指針。
我們?cè)賮砜纯?policy func 的構(gòu)造函數(shù)。
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 差不多。不再贅述了。注意這里的分配內(nèi)存之后并沒有賦值給一個(gè)指針,而僅僅是在 buf 上分配內(nèi)存就完了。獲取函子并調(diào)用的操作是由 invoker 做的。
我覺得 std::function 的重點(diǎn)就是數(shù)據(jù)成員這部分。至于相關(guān)的成員函數(shù),我覺得都比較正常,這里就不在多說什么了。
那,就這樣。
總結(jié)
以上是生活随笔為你收集整理的“vector”: 不是“std”的成员_libcxx 的 std::function 源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DellEMC UnityVSA 部署指
- 下一篇: php获取代理服务器真实内网IP方法