简析 std::function 实现原理
本篇基于 libstdc++v3,在线代码可以到 libstdc++ 上面看,或者直接在自己机器上看(/usr/include/c++/4…..)。不做特殊说明的话,代码都是源自 bits/stl_functional.h,如果是老版本的有的代码在 functional 。当然老版本和新版本的行号是不一样的。
一元和二元的函数实现非常简单,在 bits/stl_function.h 中
template<typename _Arg, typename _Result>
struct unary_function
{
/// @c argument_type is the type of the argument
typedef _Arg argument_type;
/// @c result_type is the return type
typedef _Result result_type;
};
/**
* This is one of the @link functors functor base classes@endlink.
*/
template<typename _Arg1, typename _Arg2, typename _Result>
struct binary_function
{
/// @c first_argument_type is the type of the first argument
typedef _Arg1 first_argument_type;
/// @c second_argument_type is the type of the second argument
typedef _Arg2 second_argument_type;
/// @c result_type is the return type
typedef _Result result_type;
};
最简单的例子: algorithm 里的 plus, minus 等 functor
template<typename _Tp>
struct plus : public binary_function<_Tp, _Tp, _Tp>
{
_Tp
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x + __y; }
};
/// One of the @link logical_functors Boolean operations functors@endlink.
template<typename _Tp>
struct logical_not : public unary_function<_Tp, bool>
{
bool
operator()(const _Tp& __x) const
{ return !__x; }
};
/** @} */
其实就是一个有 operator() 的 struct 在目前的 libstdc++v3 中,还有这样的内容:
#if __cplusplus > 201103L
template<typename _Tp = void>
struct plus;
......
#endif
也就是允许 plus<void> 的存在。(我没读过标准我也布吉岛为什么) 而如果没有这个的话,ref void 会在模板实例化时报错。
_Maybe_unary_or_binary_function 算是对 function 实现的特化。 分别在二元和一元函数时特化成 unary_function 和 binary_function
line 495
template<typename _Res, typename... _ArgTypes>
struct _Maybe_unary_or_binary_function { };
template<typename _Res, typename _T1>
struct _Maybe_unary_or_binary_function<_Res, _T1>
: std::unary_function<_T1, _Res> { };
template<typename _Res, typename _T1, typename _T2>
struct _Maybe_unary_or_binary_function<_Res, _T1, _T2>
: std::binary_function<_T1, _T2, _Res> { };
然后由 function 做 private 继承
function:
template<typename _Res, typename... _ArgTypes>
class function<_Res(_ArgTypes...)>
: public _Maybe_unary_or_binary_function<_Res, _ArgTypes...>,
private _Function_base
当然一路上我们可以学习一下库里怎么写 c++ 的比如:
function&
operator=(function&& __x)
{
function(std::move(__x)).swap(*this);
return *this;
}
看到 function::swap , 可以发现他有三个关键的成员:
void swap(function& __x)
{
std::swap(_M_functor, __x._M_functor);
std::swap(_M_manager, __x._M_manager);
std::swap(_M_invoker, __x._M_invoker);
}
其中 _M_invoker 也就是 function 本身的成员,就是那个函数指针~~~
typedef _Res (*_Invoker_type)(const _Any_data&, _ArgTypes...);
_Invoker_type _M_invoker;
而 manager 和 functor 则是基类 _Fucntion_base 的成员
关键步骤在这里: operator()
template<typename _Res, typename... _ArgTypes>
_Res
function<_Res(_ArgTypes...)>::
operator()(_ArgTypes... __args) const
{
if (_M_empty())
__throw_bad_function_call();
return _M_invoker(_M_functor, std::forward<_ArgTypes>(__args)...);
}
大概猜一下,_M_functor 是要绑在 class 成员函数上的 this 指针,但是如果这只是一个全局函数而不是某个 class 的成员呢? 可能猜的并不对。 我们有必要看一下 _M_functor _M_invoker 是怎么进行初始化的
copy 的时候, _M_manager 竟然还要做一些奇奇怪怪的事情~
template<typename _Res, typename... _ArgTypes>
function<_Res(_ArgTypes...)>::
function(const function& __x)
: _Function_base()
{
if (static_cast<bool>(__x))
{
_M_invoker = __x._M_invoker;
_M_manager = __x._M_manager;
__x._M_manager(_M_functor, __x._M_functor, __clone_functor);
}
}
看来 _M_manger 里面还有很多黑魔法,留着慢慢看。 我们先来关注一下 function 是怎么造出来的,然后一路跟下去好了~
template<typename _Res, typename... _ArgTypes>
template<typename _Functor, typename>
function<_Res(_ArgTypes...)>::
function(_Functor __f)
: _Function_base()
{
typedef _Function_handler<_Signature_type, _Functor> _My_handler;
if (_My_handler::_M_not_empty_function(__f))
{
_My_handler::_M_init_functor(_M_functor, std::move(__f));
_M_invoker = &_My_handler::_M_invoke;
_M_manager = &_My_handler::_M_manager;
}
}
关键来了,这就是从一个正常的“函数”(或者说函数指针) 构造出 std::function 的过程。当然,我们可以往里面加 puts 再编译验证一下,答案是肯定的。
function(_functor __f) 的函数声明如下:
template<typename _Functor,
typename = _Requires<_Callable<_Functor>, void>>
function(_Functor);
问题又变得有些复杂,我们来看 _Requires 和 _Callable 的定义,就在 function 类的开头 模板看起来很头疼,不过感觉是对 _Functor 类型做检查,然后 enable_if 相应的 Functor 类型的构造函数
using _Invoke = decltype(__callable_functor(std::declval<_Functor&>())
(std::declval<_ArgTypes>()...) );
template<typename _Functor>
using _Callable = __check_func_return_type<_Invoke<_Functor>, _Res>;
template<typename _Cond, typename _Tp>
using _Requires = typename enable_if<_Cond::value, _Tp>::type;
从外向内开始,_Requires 的作用是如果 _Callable<_Functor>::value 是 true,则 _Require<..> 为 void 类型,模板正常匹配,提供对应 _Functor 类型的构造函数;否则 SFINAE (奇妙的 SFINAE),那么这个 _Functor 类型的构造函数不存在。
而 _Callable 则会做具体类型检查,其中 __check_func_return_type 的定义也在这个文件中:
template<typename _From, typename _To>
using __check_func_return_type
= __or_<is_void<_To>, is_convertible<_From, _To>>;
作用是是检查函数本身的返回值时候可以被转换成我们定义的function的返回值。 is_void is_convertible __or_ 都是 type_traits 中的内容(除了__or_ 其他两个都是 std 中的)
那么检查是否可以被调用的任务就落在 _Invoke 身上了。可以看到 decltype 里面“发生了”函数调用,想在编译时期获得这个函数返回类型(当然,如果_Functor不是函数,这时候就挂掉了) 这里出现了一个 std::declval 会给类型加上ref,是为了避免参数类型 _ArgTypes 中存在没有默认构造函数的类型,导致这一步函数调用在编译期出错。
template<typename _Functor>
inline _Functor&
__callable_functor(_Functor& __f)
{ return __f; }
template<typename _Member, typename _Class>
inline _Mem_fn<_Member _Class::*>
__callable_functor(_Member _Class::* &__p)
{ return std::mem_fn(__p); }
。。 还有一些重载略
其实 __callable_functor 只是起到了一个转换的作用,将不能直接call的(class member function)转换成可以直接上的~ 看来前面是猜错了,_Any_data 并不是用来处理 class member func 的。
都看到这里了,顺便看看 std::mem_fn 是怎么做的吧~
template<typename _Tp, typename _Class>
inline _Mem_fn<_Tp _Class::*>
mem_fn(_Tp _Class::* __pm) noexcept
{
return _Mem_fn<_Tp _Class::*>(__pm);
}
好吧,原来他什么都没有做,只是对 _Mem_fn 的 wrapper,那我们看一下 _Mem_fn 。
template<typename _MemberPointer>
class _Mem_fn;
template<typename _Tp, typename _Class>
_Mem_fn<_Tp _Class::*>
mem_fn(_Tp _Class::*) noexcept;
是不是有些失望,我们继续往下看。 来看最简单的一个 _Mem_fn (mem_fn对应有 const, volatile 等等各种重载,所以 _Mem_fn 也相应的有各种奇奇怪怪的类型)
template<typename _Res, typename _Class, typename... _ArgTypes>
class _Mem_fn<_Res (_Class::*)(_ArgTypes...)>
: public _Maybe_unary_or_binary_function<_Res, _Class*, _ArgTypes...>
他再一次继承了 _Maybe_unary_or_binary_function 这货,到底有什么用呢? = = 其实看了半天我也没想通为什么要做这个继承。。。先不管他
其中有一个成员
typedef _Res (_Class::*_Functor)(_ArgTypes...);
private:
_Functor __pmf;
也就是函数的指针。
那是怎样做 call 的呢,还是 operator()
// Handle objects
template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
_Res
operator()(_Class& __object, _Args&&... __args) const
{ return (__object.*__pmf)(std::forward<_Args>(__args)...); }
其实就是做了一层代理而已,拿到对象之后,利用函数指针调用函数。 而且对于 &&, pointer, smart pointer, ref wrapper 都有做对应处理
// Handle pointers
template<typename... _Args, typename _Req = _RequireValidArgs<_Args...>>
_Res
operator()(_Class* __object, _Args&&... __args) const
{ return (__object->*__pmf)(std::forward<_Args>(__args)...); }
// Handle smart pointers, references and pointers to derived
template<typename _Tp, typename... _Args,
typename _Req = _RequireValidArgs2<_Tp, _Args...>>
_Res
operator()(_Tp&& __object, _Args&&... __args) const
{
return _M_call(std::forward<_Tp>(__object), &__object,
std::forward<_Args>(__args)...);
}
template<typename _Tp, typename... _Args,
typename _Req = _RequireValidArgs3<_Tp, _Args...>>
_Res
operator()(reference_wrapper<_Tp> __ref, _Args&&... __args) const
{ return operator()(__ref.get(), std::forward<_Args>(__args)...); }
关于 mem_fn 就说这么多。中间的 _RequireValidArg 的作用是进行一系列类型检查(奇妙的模板)。 对应定义都在 _Mem_fn 类内。
好的! 绕了大半天,我们终于可以回到 function(_Functor) 这个构造函数了。
typedef _Function_handler<_Signature_type, _Functor> _My_handler;
if (_My_handler::_M_not_empty_function(__f))
{
_My_handler::_M_init_functor(_M_functor, std::move(__f));
_M_invoker = &_My_handler::_M_invoke;
_M_manager = &_My_handler::_M_manager;
}
我们遇到了 _Function_handler, _M_invoker 和 _M_manager 都是从 _My_handler 里面拿到的。
typedef _Res _Signature_type(_ArgTypes...);
sginature type 也就是函数签名类型。
来看 _Function_handler
template<typename _Res, typename _Functor, typename... _ArgTypes>
class _Function_handler<_Res(_ArgTypes...), _Functor>
: public _Function_base::_Base_manager<_Functor>
真是变态啊,它又继承了 _Function_base::_Base_manager。 既然刚才都是在调用静态方法,那我们就直接看函数
_M_not_empty_function 实际上是 _Function_base::_Base_manager 中的静态成员,有几个重载,就是判断函数是不是 null。
template<typename _Signature>
static bool
_M_not_empty_function(const function<_Signature>& __f)
{ return static_cast<bool>(__f); }
继续看 _My_handler::_M_init_functor,public 的方法转发给了 private 做处理。
static void
_M_init_functor(_Any_data& __functor, _Functor&& __f)
{ _M_init_functor(__functor, std::move(__f), _Local_storage()); }
_Any_data 我们一直没有看,不过根据之前的猜想他应该是 class 的一个实例什么的,然后可以在他身上掉 _M_invoker。
union _Any_data
{
void* _M_access() { return &_M_pod_data[0]; }
const void* _M_access() const { return &_M_pod_data[0]; }
template<typename _Tp>
_Tp&
_M_access()
{ return *static_cast<_Tp*>(_M_access()); }
template<typename _Tp>
const _Tp&
_M_access() const
{ return *static_cast<const _Tp*>(_M_access()); }
_Nocopy_types _M_unused;
char _M_pod_data[sizeof(_Nocopy_types)];
};
好吧 _Any_data 真的只是 _Any_data。 里面可以扔任何东西,那么他到底有什么用呢? _M_pod_data 是 sizeof(_Nocopy_types)
union _Nocopy_types
{
void* _M_object;
const void* _M_const_object;
void (*_M_function_pointer)();
void (_Undefined_class::*_M_member_pointer)();
};
也就是一个 pointer 的大小。不过具体用途我们还不清楚,接着看。
刚才还有一个 _Local_storage。
typedef integral_constant<bool, __stored_locally> _Local_storage;
定义了一个 bool 常量,那这个__stored_locally 是做什么的呢。
static const bool __stored_locally =
(__is_location_invariant<_Functor>::value
&& sizeof(_Functor) <= _M_max_size
&& __alignof__(_Functor) <= _M_max_align
&& (_M_max_align % __alignof__(_Functor) == 0));
似乎和 mem layout 开始扯上关系了。慢慢来看~
首先判断 _Functor 是不是一个指针。
template<typename _Tp>
struct __is_location_invariant
: integral_constant<bool, (is_pointer<_Tp>::value
|| is_member_pointer<_Tp>::value)>
{ };
然后进一步验证,这货的 size 肯定 <= pointer。
&& sizeof(_Functor) <= _M_max_size
static const std::size_t _M_max_size = sizeof(_Nocopy_types);
__alignof__ 是 gcc 的扩展。
&& __alignof__(_Functor) <= _M_max_align
&& (_M_max_align % __alignof__(_Functor) == 0));
static const std::size_t _M_max_align = __alignof__(_Nocopy_types);
一时半会看的有点摸不着头脑,这些判断是在做什么? 暂时扔下继续往下看。
private:
static void
_M_init_functor(_Any_data& __functor, _Functor&& __f, true_type)
{ new (__functor._M_access()) _Functor(std::move(__f)); }
static void
_M_init_functor(_Any_data& __functor, _Functor&& __f, false_type)
{ __functor._M_access<_Functor*>() = new _Functor(std::move(__f)); }
};
原来是对应 _Local_storage 的 true 或者 false 进行不同的 new 操作。也就是说,看 _Functor 在 是不是能再 _Any_data 中存下,如果太大则 new 出来一个,_Any_data 只存指针。
不过真的会太大么?函数不都是指针么?no,还有 lambda 表达式~ lambda 表达式似乎是通过 struct 来实现 capture 傻傻的,这里我们先不管他。
既然有 new 了,看来 manager, swap 什么的确实是必要的~ 继续往下看。
_M_invoker = &_My_handler::_M_invoke;
_M_manager = &_My_handler::_M_manager;
这实际上是两个函数指针。具体是哪个函数呢?注意到 _Function_handler 实际上是有多个模板重载的
template<typename _Res, typename _Functor, typename... _ArgTypes>
class _Function_handler<_Res(_ArgTypes...), _Functor>
: public _Function_base::_Base_manager<_Functor>
template<typename _Res, typename _Functor, typename... _ArgTypes>
class _Function_handler<_Res(_ArgTypes...), reference_wrapper<_Functor> >
: public _Function_base::_Ref_manager<_Functor>
template<typename _Class, typename _Member, typename _Res,
typename... _ArgTypes>
class _Function_handler<_Res(_ArgTypes...), _Member _Class::*>
: public _Function_handler<void(_ArgTypes...), _Member _Class::*>
分别处理 基本情况, reference wrapper,和 class member 等情况。 正因为存在多种情况,所以在 copy, swap 的时候 _M_invoke, _M_manager 等等都要有相应的变化。
我们先考虑最简单的基本情况。
template<typename _Res, typename _Functor, typename... _ArgTypes>
class _Function_handler<_Res(_ArgTypes...), _Functor>
: public _Function_base::_Base_manager<_Functor>
{
typedef _Function_base::_Base_manager<_Functor> _Base;
public:
static _Res
_M_invoke(const _Any_data& __functor, _ArgTypes... __args)
{
return (*_Base::_M_get_pointer(__functor))(
std::forward<_ArgTypes>(__args)...);
}
};
_M_invoke 又是一层代理,好吧,让我们来看 _Base_manager 里面到底在做什么吧。不过可以猜的到, 他应该是根据之前 new 的情况来取这个 pointer 。
// Retrieve a pointer to the function object
static _Functor*
_M_get_pointer(const _Any_data& __source)
{
const _Functor* __ptr =
__stored_locally? std::__addressof(__source._M_access<_Functor>())
/* have stored a pointer */ : __source._M_access<_Functor*>();
return const_cast<_Functor*>(__ptr);
}
果然不出所料。
接下来看 _M_manager
typedef bool (*_Manager_type)(_Any_data&, const _Any_data&, _Manager_operation);
这是 _Function_base 里面对 _M_manger 的类型定义。
static bool
_M_manager(_Any_data& __dest, const _Any_data& __source,
_Manager_operation __op)
{
switch (__op)
{
#ifdef __GXX_RTTI
case __get_type_info:
__dest._M_access<const type_info*>() = &typeid(_Functor);
break;
#endif
case __get_functor_ptr:
__dest._M_access<_Functor*>() = _M_get_pointer(__source);
break;
case __clone_functor:
_M_clone(__dest, __source, _Local_storage());
break;
case __destroy_functor:
_M_destroy(__dest, _Local_storage());
break;
}
return false;
}
可以看出,_M_manager 是根据不同的 operation 对 _Any_Data 进行不同操作。还是前面怎么 new 的问题。具体就不仔细分析了,道理和之前 _M_init_functor 都是一样的~
至此,最基本的 std::function 分析已经结束了。略去了 reference wrapper, class member, 还有 cv-qualifier 的一些情况。
总结一下:
- function 继承自 _Function_base。
- _M_invoke 是 function 的成员,一个函数指针,指向具体的 _Function_base::_Base_manager::_M_invoke 函数(存在 reference wrapper, class member func 等情况)。外界调用 operator() 会转发给 _M_invoke,_M_invoke 会转发给 _Function_base::_M_functor。
- _M_functor 是 _Function_base 的成员,_Any_data 类型,存放函数指针(或者其他承载函数的东西)。
- _M_Manager 是 _Function_base 成员,函数指针,针对 _M_functor 的具体内存的分配情况,_M_manager 指向相应的管理函数 _Function_base::_Base_manager::_M_Manager )
- _Maybe_unary_or_binary_function 是在为 function operator=(_Maybe_unary_or_binary_function) 做准备的啊~ (当 unary 和 binary 赋值给 function)
题外话:
- 静态用模板匹配确定类型,然后用函数指针做多态 (_M_manager, _M_invoke)。也算是学到了新方法~
- 顺便提一下 reference_wrapper,其实就是一个 pointer wrapper,包着一个指针,实现比较简单。
- 如果是 virtual function 怎么破呢? 想一下 mem_fn 怎么做的就明白了。