前言
这几天简单的看了下list,deque发现基本实现和stl源码刨析中基本类似,由于stl是个大块头,就打算选择性的看把,一些像queue,stack这样的适配器也粗略瞄一眼,就不细看了。刚好最近使用funtion比较频繁,所以这次来看下function的实现。
STL版本
本文所使用的stl版本为libc++ 13.0,属于LLVM项目。
如果遇到不熟悉的宏定义可以参考文档Symbol Visibility Macros
function
先看看function的定义,由于function使用可变参数模板,但是在声明中不允许出现可变参数模板,所以就直接跳过声明了。
template<class _Fp> class _LIBCPP_DEPRECATED_CXX03_FUNCTION _LIBCPP_TEMPLATE_VIS function; //声明
template<class _Rp, class ..._ArgTypes> //定义
class _LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
#if _LIBCPP_STD_VER <= 17 || !defined(_LIBCPP_ABI_NO_BINDER_BASES)
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
#endif
首先解释一下模板参数,_Rp为函数返回类型,_ArgTypes是参数类型,是一个可变参。
至于继承的两个对象是历史原因,需要兼容旧的代码,大致是当函数参数为一个的时候就会继承__maybe_derive_from_unary_function的特化,两个会继承__maybe_derive_from_binary_function,c++17之后删除,就不讨论了。
接下来是function的核心
#ifndef _LIBCPP_ABI_OPTIMIZED_FUNCTION
typedef __function::__value_func<_Rp(_ArgTypes...)> __func;
#else
typedef __function::__policy_func<_Rp(_ArgTypes...)> __func;
#endif
__func __f_;
__f_是function实际存储的地方,正常是__function::__value_func,但是后面有一位大佬进行了优化,做出了__function::__policy_func,对小于2 * sizeof(void*)的非平凡小对象进行了优化,不过里面具体是什么,待看完function后揭晓。
//__value_func和__policy_func性能对比
Benchmark Time CPU Time Old =
Time New CPU Old CPU New
---------------------------------------------------------------------------=
----------------------------------------
BM_MixedFunctorTypes -0.3794 -0.3794 19661 =
12202 19660 12202
如果刚兴趣也可以看看提交,[PATCH] D55045: Add a version of std::function that includes a few optimizations.
接着看function的构造函数吧
template <class _Fp, bool = _And<
_IsNotSame<__uncvref_t<_Fp>, function>,
__invokable<_Fp, _ArgTypes...>
>::value>
struct __callable;
template <class _Fp>
struct __callable<_Fp, true>
{
static const bool value = is_void<_Rp>::value ||
__is_core_convertible<typename __invoke_of<_Fp, _ArgTypes...>::type,
_Rp>::value;
};
template <class _Fp>
struct __callable<_Fp, false>
{
static const bool value = false;
};
template <class _Fp>
using _EnableIfLValueCallable = typename enable_if<__callable<_Fp&>::value>::type;
public:
template<class _Fp, class = _EnableIfLValueCallable<_Fp>>
function(_Fp);
首先调了一个最典型的构造函数的声明,可以发现function的构造是一个模板函数,第一模板参数为函数,比如function<void()>,_Fp就是void(),第二个模板参数是一个检查,检查第一个参数是否为一个可调用对象。
利用__callable的第二个模板参数进行特化,_IsNotSame利用模板特化判断两个模板参数是否一样。可以看下原代码。
template <class _Tp, class _Up> struct _LIBCPP_TEMPLATE_VIS is_same : public false_type {};
template <class _Tp> struct _LIBCPP_TEMPLATE_VIS is_same<_Tp, _Tp> : public true_type {};
template <class _Tp>
struct __uncvref {
typedef _LIBCPP_NODEBUG_TYPE typename remove_cv<typename remove_reference<_Tp>::type>::type type;
};
两个模板参数一样会特化为第二个,不一样会特化为第一个,、__uncvref_t则是利用类型萃取移除引用。所以这里is_same是判断_Fp是否是一个function类型,并且利用__invokable<_Fp, _ArgTypes…>判断是否是可调用对象。
当_Fp不是function类型,并且是可调用对象,__callable则特化为struct __callable<_Fp, true>。
接着我们看构造函数的定义。
template <class _Rp, class... _ArgTypes>
template <class _Fp, class>
function<_Rp(_ArgTypes...)>::function(_Fp __f) : __f_(_VSTD::move(__f)) {}
仅仅是委托给__f_处理。顺带一提()调用与析构都差不多如此。
template<class _Rp, class ..._ArgTypes>
_Rp
function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const
{
return __f_(_VSTD::forward<_ArgTypes>(__arg)...);
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>::~function() {}
__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成员有两个,一个为pod类型的_buf,另一个为_base指针的_f。首先来看一下__buf_是什么把。
__buf_的类型是aligned_storage<3 * sizeof(void*)>::type,aligned_storage这个类的作用是提供一个模板参数大小的POD类型,详细的看这里aligned_storage
__base<_Rp(_ArgTypes…)>则为一个虚基类,实际存储的的是__func<_Fp, _Alloc, _Rp(_ArgTypes…)>,这个我们等等在说,先看下构造函数和析构函数吧。
template <class _Fp, class _Alloc>
_LIBCPP_INLINE_VISIBILITY __value_func(_Fp&& __f, const _Alloc& __a)
: __f_(nullptr)
{
typedef allocator_traits<_Alloc> __alloc_traits;
typedef __function::__func<_Fp, _Alloc, _Rp(_ArgTypes...)> _Fun;
typedef typename __rebind_alloc_helper<__alloc_traits, _Fun>::type
_FunAlloc;
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();
}
}
}
_LIBCPP_INLINE_VISIBILITY
~__value_func()
{
if ((void*)__f_ == &__buf_)
__f_->destroy();
else if (__f_)
__f_->destroy_deallocate();
}
可以发现当传入的仿函数小于3sizeof(void),也就是24字节时,会在自身__buf_存储,否则用分配器进行分配空间。并且使用分配器时候还利用unique_ptr进行构造,用unique_ptr的好处在于方便,比如在构造过程中抛出异常了,则unique_ptr会自动调用析构函数。
接下来是重载()调用,其实也是调用内部__f_的()重载.
_LIBCPP_INLINE_VISIBILITY
_Rp operator()(_ArgTypes&&... __args) const
{
if (__f_ == nullptr)
__throw_bad_function_call();
return (*__f_)(_VSTD::forward<_ArgTypes>(__args)...);
}
__func和__base
__func是__base的基类,而__base是一个虚基类,规定了子类必需要实现的虚函数。
template<class _Rp, class ..._ArgTypes>
class __base<_Rp(_ArgTypes...)>
{
__base(const __base&);
__base& operator=(const __base&);
public:
_LIBCPP_INLINE_VISIBILITY __base() {}
_LIBCPP_INLINE_VISIBILITY virtual ~__base() {}
virtual __base* __clone() const = 0;
virtual void __clone(__base*) const = 0;
virtual void destroy() _NOEXCEPT = 0;
virtual void destroy_deallocate() _NOEXCEPT = 0;
virtual _Rp operator()(_ArgTypes&& ...) = 0;
#ifndef _LIBCPP_NO_RTTI
virtual const void* target(const type_info&) const _NOEXCEPT = 0;
virtual const std::type_info& target_type() const _NOEXCEPT = 0;
#endif // _LIBCPP_NO_RTTI
};
而__func_内部又存储着一个__alloc_func
template<class _FD, class _Alloc, class _FB> class __func;
template<class _Fp, class _Alloc, class _Rp, class ..._ArgTypes>
class __func<_Fp, _Alloc, _Rp(_ArgTypes...)>
: public __base<_Rp(_ArgTypes...)>
{
__alloc_func<_Fp, _Alloc, _Rp(_ArgTypes...)> __f_;
///函数略,基本都是对__f_的操作
}
__alloc_func
到这着一步就差不多要将function扒到底了,我们看下着__alloc_func里到底是啥。
template <class _Fp, class _Ap, class _Rp, class... _ArgTypes>
class __alloc_func<_Fp, _Ap, _Rp(_ArgTypes...)>
{
__compressed_pair<_Fp, _Ap> __f_;
public:
typedef _LIBCPP_NODEBUG_TYPE _Fp _Target;
typedef _LIBCPP_NODEBUG_TYPE _Ap _Alloc;
//函数略
}
可以发现__alloc_func有__compressed_pair类,这个算一个挺好的优化,__compressed_pair可以理解为一个pair,但是在某一个或都为空的时候更节约空间,因为大家可以sizeof一个空类试一下,可以发现一个空类的大小为1。这里就是利用继承省略空类的空间。我们来看下__compressed_pair。
template <class _T1, class _T2>
class __compressed_pair : private __compressed_pair_elem<_T1, 0>,
private __compressed_pair_elem<_T2, 1> {/*略*/}
template <class _Tp, int _Idx,
bool _CanBeEmptyBase =
is_empty<_Tp>::value && !__libcpp_is_final<_Tp>::value>
struct __compressed_pair_elem{
typedef _Tp _ParamT;
typedef _Tp& reference;
typedef const _Tp& const_reference;
//函数略
private:
_Tp __value_;
}
template <class _Tp, int _Idx>
struct __compressed_pair_elem<_Tp, _Idx, true>{
typedef _Tp _ParamT;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp __value_type;
}
可以发现如果_CanBeEmptyBase特化为true时候,__compressed_pair_elem会特化为__compressed_pair_elem<_Tp, _Idx, true>,两者区别就是类中是否实际存储_Tp _value。
接着看下函数调用,也就是重载(),这里就是真正执行function的方法了。
_LIBCPP_INLINE_VISIBILITY
_Rp operator()(_ArgTypes&&... __arg)
{
typedef __invoke_void_return_wrapper<_Rp> _Invoker;
return _Invoker::__call(__f_.first(),
_VSTD::forward<_ArgTypes>(__arg)...);
}
template <class _Ret, bool = is_void<_Ret>::value>
struct __invoke_void_return_wrapper{}
template <class _Ret>
struct __invoke_void_return_wrapper<_Ret, true>{}
__invoke_void_return_wrapper<_Rp>是一个封装了std::__invoke的类,并且有两种特化,一种是有返回值,一种是无返回值。std::__invoke是一个调用可调用对象的函数,比如函数指针,function,lambda对象都可以通过invok进行调用。
__policy_func
不知道大家看到这里还记不得的__value_func,而__policy_func是一个对非平凡类型的小对象优化的__value_func。具体大小为小于 2 * sizeof(void*)。我们来看下代码。
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_;
//函数略
}
可以发现思路与之前__value_func差不多,首先存储对象从由两个变成一个__policy_storage,节省了空间,__policy_storage类型其实是一个union。
union __policy_storage
{
mutable char __small[sizeof(void*) * 2];
void* __large;
};
接着看构造。
_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();
}
}
}
其中利用了__use_small_storage<_Fun>()判断是在自身buf上构造还是调用分配器,我们看下这个具体是个什么
template <typename _Fun>
struct __use_small_storage
: public integral_constant<
bool, sizeof(_Fun) <= sizeof(__policy_storage) &&
_LIBCPP_ALIGNOF(_Fun) <= _LIBCPP_ALIGNOF(__policy_storage) &&
is_trivially_copy_constructible<_Fun>::value &&
is_trivially_destructible<_Fun>::value> {};
可以看出来是做了类型萃取,()调用实际上是调用integral_constant的()重载。条件为小于__policy_storage的大小。对其要求小于等于,并且定义拷贝构造和析构。
满足以上条件才在自身构造。而()重载和__value_func基本一样,利用std::invoke就不多说了。