VC中function函数解析
C++标准库是日常应用中非常重要的库,我们会用到C++标准库的很多组件,C++标准库的作用,不单单是一种可以很方便使用的组件,也是我们学习很多实现技巧的重要宝库。我一直对C++很多组件的实现拥有比较强的兴趣。最近花了一些时间,查看了C++中function类的实现,将其中的要点,写在这里(这里只介绍其中的一部分):
1.首先VC实现了将<Ret(T1, T2, ...)>这种类型的类型参数,改变为<Ret, T1, T2, ...>这种类型的类型参数。使用的方法如下:
template <class _Fty>
class function : public _Get_function_impl<_Fty>::type
{
public:
using _Mybase = _Get_function_impl<_Fty>::type;
public:
function() noexcept
{ // construct empty function wrapper
} template <class _Fx,
class = typename _Mybase::_Enable_if_callable_t<_Fx&, function>>
function(_Fx _Func)
{ // construct wrapper holding copy of _Func
this->_Reset(std::move(_Func));
} private:
// 没有其他的数据成员
};
不过,对于_Get_function_impl<_Fty>::type的实现,应该是编译器额外处理(我不记得C++中有类似的语法),大致处理如下(如下代码不能编译通过):
template <class _Ret,
class... _Types>
struct _Get_function_impl<_Ret CALL_OPT (_Types...)>
{
using type = _Func_class<_Ret, _Types...>;
};
参考以上代码,也可以使用boost中的boost::typeindex,可以知道,function的实现,继承于_Func_class,function的实现,使用了类似于Adapter的方式,具体的实现细节在_Func_class中。这点,可以参考上面的function构造函数。function中没有直接定义operator()函数,operator()函数在_Func_class中定义,我们查看一下_Func_class函数:
using max_align_t = double; // most aligned type
// size in pointers of std::function and std::any (roughly 3 pointers larger than std::string when building debug
constexpr int _Small_object_num_ptrs = + / sizeof(void *);
constexpr size_t _Space_size = (_Small_object_num_ptrs - ) * sizeof(void*); template <class _Ret,
class... _Types>
class _Func_class
{ // implement function template
public:
using result_type = _Ret; using _Ptrt = _Func_base<_Ret, _Types...>; _Func_class() noexcept
{ // construct without stored object
_Set();
} _Ret operator()(_Types... _Args) const
{
if (_Empty())
{
_Xbad_function_call();
}
const auto _Impl = _Getimpl();
return (_Impl->_Do_call(_STD forward<_Types>(Args)...));
} protected:
// 用于判断传入的函数对象可以调用_Types...表示的参数,并且返回_Ret类型的参数,
// 而且不为function类型(至少上面的调用时是这个意思)
template<class _Fx,
class _Function>
using _Enable_if_callable_t = enable_if_t<conjunction_v<negation<is_same<decay_t<_Fx>, _Function>>,
_Is_invocable_r<_Ret, _Fx, _Types...>>>; bool _Empty() const _NOEXCEPT
{ // return true if no stored object
return (_Getimpl() == );
} template <class _Fx>
void _Reset(_Fx&& _Val)
{ // store copy of _Val
if (!_Test_callable(_Val))
{ // null member pointer/function pointer/std::function
return; // already empty
} using _Impl = _Func_impl_no_alloc<decay_t<_Fx>, _Ret, _Types...>;
_Reset_impl<_Impl>(std::forward<_Fx>(_Val), _Is_large<_Impl>());
} template <class _Myimpl, class _Fx>
void _Reset_impl(_Fx&& _Val, true_type)
{ // store copy of _Val, large (dynamically allocated)
_Set(_Global_new<_Myimpl>(std::foward<_Fx>(_Val)));
} template <class _Myimpl, class _Fx>
void _Reset_impl(_Fx&& _Val, false_type)
{ // store copy of _Val, small (locally stored)
// placement operator new,将对象创建在_Mystorage所在的地址
_Set(::new (_Getspace()) _Myimpl(std::forward<_Fx>(_Val)));
} void _Tidy() noexcept
{ // clean up
if (!_Empty())
{ // destroy callable object and maybe delete it
_Getimpl()->_Delete_this(!_Local());
_Set();
}
} private:
union _Storage
{ // storage for small objects (basic_string is small)
max_align_t _Dummy1; // for maximum alignment
char _Dummy2[_Space_size]; // to permit aliasing
_Ptrt *_Ptrs[_Small_object_num_ptrs]; // _Ptrs[_Small_object_num_ptrs - 1] is reserved
}; _Storage _Mystorage; // 数据成员 bool _Local() const noexcept
{ // test for locally stored copy of object
return (_Getimpl() == _Getspace());
} _Ptrt* _Getimpl() const noexcept
{ // get pointer to object
return (_Mystorage._Ptrs[_Small_object_num_ptrs - ]);
} void _Set(_Ptrt* _Ptr) noexcept
{ // store pointer to object
_Mystorage._Ptrs[_Small_object_num_ptrs - ] = _Ptr;
} void *_Getspace() noexcept
{ // get pointer to storage space
return (&_Mystorage);
} const void* _Getspace() const noexcept
{
return (&_Mystorage);
}
};
这个类只有一个数据成员,就是_Storage _Mystorage;其中_Storage是个union,union中两个成员用了Dummy开头,dummy的意思,就是只是为了实现某些目的,实际中并不会应用,_Dummy1的目的是为了让这个对象最大对齐,_Dummy2说是用于别名,我从实现来看,更像用来表示多大的函数对象可以本地存储,_Space_size表示的就是可以将函数对象直接存储到_Func_class中最大的值,而_Ptrs[_Small_object_num_ptrs]的大小,刚好比_Dummy2对一个指针的长度大小,因为最后一个指针需要用了存储实际函数的起始地址,这点,可以参考_Getimpl函数和_Set函数。这样做的目的,应该是减少new的次数,因为new的次数过多,容易导致比较严重的碎片化,而且new本来速度也比不了在堆栈中分配内存的速度,不过,另一方面,从实现来看,一个function占用的大小,远大于一个函数指针的大小,更远大于一个没有数据成员的函数对象的大小。如果对于内存占用有很大的要求,而且需要的函数对象又特别多,例如附带函数的事件处理队列等,需要慎重考虑一下。
说了这么多,那么C++中是如何做到在函数对象比较小的情况下,将函数对象存储到本地,而比较大的时候,将函数对象在堆上分配呢?我们需要查看一下:
_Ptrt *_Ptrs[_Small_object_num_ptrs];中的_Ptrt,也就是_Func_base:
template <class _Rx,
class... _Types>
class _Func_base
{ // abstract base for implementation types
public:
virtual _Func_base* _Copy(void*) const = ;
virtual _Func_base* _Move(void*) const = ;
virtual _Fx _Do_call(Types&&...) = ;
virtual void _Delete_this(bool) _NOEXCEPT = ; _Func_base() = default;
_Func_base(const _Func_base&) = delete;
_Func_base& operator=(const _Func_base&) = delete;
// destructor non-virtual due to _Delete_this()
}; template <class _Callable,
class _Rx,
class... _Types>
class _Func_impl_no_alloc final : public _Func_base<_Rx, _Types...>
{ // derived class for specific implementation types that don't use allocators
public:
using _Mybase = _Func_base<_Rx, _Types...>;
using _Nothrow_move = is_nothrow_move_constructible<_Callable>; template <class _Other,
class = enable_if_t<!is_same_v<_Func_impl_no_alloc, decay_t<_Other>>>>
explicit _Func_impl_no_alloc(_Other&& _Val)
: _Callee(std::forward<_Other>(_Val))
{ // construct
} private:
virtual _Mybase *_Copy(void *_Where) const override
{ // return clone of *this
return (_Clone(_Where, _Is_large<_Func_impl_no_alloc>()));
} _Mybase *_Clone(void *, true_type) const
{ // return clone of *this, large (dynamically allocated)
return (_Global_new<_Func_impl_no_alloc>(_Callee));
} _Mybase *_Clone(void *_Where, false_type) const
{ // return clone of *this, small (locally stored)
return (::new (_Where) _Func_impl_no_alloc(_Callee));
} virtual _Mybase *_Move(void *_Where) override
{ // return clone of *this
return (::new (_Where) _Func_impl_no_alloc(std::move(_Callee)));
} virtual _Rx _Do_call(_Types&&... _Args) override
{
return (_Invoker_ret<_Rx>::_Call(_Callee, std::forward<_Types>(_Args)...));
} virtual const void *_Get() const noexcept override
{ // return address of stored object
return (std::addressof(_Callee));
} virtual void _Delete_this(bool _Dealloc) noexcept override
{
this->~_Func_impl_no_alloc();
if (_Dealloc)
{
_Deallocate<alignof(_Func_impl_no_alloc)>(this, sizeof(_Func_impl_no_alloc));
}
} _Callable _Callee;
};
我们认真查看上述代码,不难发现,区分在_Copy函数中的_Is_large<_Func_impl_no_alloc>(),实际调用是:_Mybase *_Clone(void *, true_type),采用的是在堆中分配一段空间,而_Mybase *_Clone(void *_Where, false_type)采用的是placement operator new,将对象创建在本地。我们查看_Func_class中的Reset系列函数,也可以发现_Is_large的使用,以及堆栈分配和本地分配的区别。下面,给出_Is_large的实现:
template <class _Impl>
struct _Is_large
: bool_constant<_Space_size < sizeof(_Impl)
|| !_Impl::_Nothrow_move::value>
{ // determine whether _Impl must be dynamically allocated
};
其中true_type是bool_constant<true>,而false_type是bool_constant<false>,为不同的类型。最后,还要要简单提及一下的是:_Func_class是继承于public _Arg_types<_Types...>,而
// 这个类的目的是提供argument_type, first_argument_type, second_argument_type,
// 因为C++17中已经为deprecated,所以,没有查看的必要
template <class... _Types>
struct _Arg_types
{ // provide argument_type, etc. (sometimes)
};
所以,我从简单考虑,之前没有写出这种继承关系,对实际理解应该没有什么影响。上述,就是我的简单介绍。
VC中function函数解析的更多相关文章
- 转:VC中UpdateData()函数的使用
VC中UpdateData()函数的使用 UpdateData(FALSE)与UpdateData(TRUE)是相反的过程 UpdateData(FALSE)是把程序中改变的值更新到控件中去 ...
- 2019-2-14SQLserver中function函数和存储过程、触发器、CURSOR
Sqlserver 自定义函数 Function使用介绍 前言: 在SQL server中不仅可以可以使用系统自带的函数(时间函数.聚合函数.字符串函数等等),还可以根据需要自定义函数 ...
- hive中function函数查询
1. desc function [函数名] desc function xpath; 查询用法: 2. desc function extended [函数名] desc function exte ...
- JavaScript中Function函数与Object对象的关系
函数对象和其他内部对象的关系 除了函数对象,还有很多内部对象,比如:Object.Array.Date.RegExp.Math.Error.这些名称实际上表示一个 类型,可以通过new操作符返回一个对 ...
- js中function函数
function:是具备某个功能的方法,方法本身没有意义,只有执行方法才有价值. function: 1 创建一个函数: 2 执行这个方法: 例: 创建 function 方法名(){ 存放某个功能的 ...
- java中main函数解析(转载)
从写java至今,写的最多的可能就是主函数 public static void main(String[] args) {} 但是以前一直都没有问自己,为什么要这么写,因为在c语言中就没有这样子的要 ...
- Python中function(函数)和methon(方法)的区别
在Python中,对这两个东西有明确的规定: 函数function —— A series of statements which returns some value to a caller. It ...
- php中parse_url函数解析
1.在php开发过程中我们经常要用到用户上传文件这个功能,那么用户上传文件我们肯定要知道用户上传文件的合法性,那么我们就要从url中获取文件的扩展名.那么就会用到parse_url()这个函数. pa ...
- java中main函数解析
从写java至今,写的最多的可能就是主函数 public static void main(String[] args) {} 但是以前一直都没有问自己,为什么要这么写,因为在c语言中就没有这样子的要 ...
随机推荐
- 构建工具 buildtool
一.什么是build tool? 构建工具是从源代码自动创建可执行应用程序的程序.构建包括将代码编译,链接和打包成可用或可执行的形式.在小项目中,开发人员通常会手动调用构建过程.这对于较大的项目来说是 ...
- [Educational Codeforces Round 55 (Rated for Div. 2)][C. Multi-Subject Competition]
https://codeforc.es/contest/1082/problem/C 题目大意:有m个类型,n个人,每个人有一个所属类型k和一个能力v,要求所选的类型的人个数相等并且使v总和最大(n, ...
- HTML的属性
data-的好处:对dom的一些自定义标识,通过这些标识符可以传递一些信息到相应的事件里:dom.dataset['属性名称'] 将节点变成数组 渐进式渲染:就是加载到哪里就显示到哪里 css和js的 ...
- LG5056 【模板】插头dp
题意 题目背景 ural 1519 陈丹琦<基于连通性状态压缩的动态规划问题>中的例题 题目描述 给出n*m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路.问有多少种铺法? 输 ...
- LOJ3048 「十二省联考 2019」异或粽子
题意 题目描述 小粽是一个喜欢吃粽子的好孩子.今天她在家里自己做起了粽子. 小粽面前有 $n$ 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 $1$ 到 $n$.第 $i$ 种馅儿具 ...
- datetime学习
四.datetime类 (一).datetime类的数据构成 datetime类其实是可以看做是date类和time类的合体,其大部分的方法和属性都继承于这二个类,相关的操作方法请参阅,本文上面关于二 ...
- FastAdmin 新年福袋进行中
FastAdmin 新年福袋进行中 2019新年福袋活动正在进行中 https://www.fastadmin.net/act/20190101/springfestival.html
- TypeScript 之 泛型
https://m.runoob.com/manual/gitbook/TypeScript/_book/doc/handbook/Generics.html 泛型:可以支持多种类型的数据 泛型函数的 ...
- es query_string 和 match 的区别
默认使用 空格拆分成 多个 子项,并且 每个子项 都会去分词 查询.可以通过 default_operator 指定 子项之间的关系.默认是 或 . 然后 每个 子项前面可以使用 -+ 指定必须有 ...
- Linux 下Tomcat单机多应用
修改/etc/profile 下,增加如下两个tomcat的配置.apache-tomcat-8.0.50为第一个tomcat, apache-tomcat-8.0.50_2为第二个tomcat ex ...