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 = 6 + 16 / sizeof(void *);
constexpr size_t _Space_size = (_Small_object_num_ptrs - 1) * 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(0);
    }

    _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() == 0);
    }

    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(0);
    }
    }

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 - 1]);
    }

    void _Set(_Ptrt* _Ptr) noexcept
    {    // store pointer to object
    _Mystorage._Ptrs[_Small_object_num_ptrs - 1] = _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 = 0;
    virtual _Func_base* _Move(void*) const = 0;
    virtual _Fx _Do_call(Types&&...) = 0;
    virtual void _Delete_this(bool) _NOEXCEPT = 0;

    _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)
};

所以,我从简单考虑,之前没有写出这种继承关系,对实际理解应该没有什么影响。上述,就是我的简单介绍。

原文地址:https://www.cnblogs.com/albizzia/p/9048898.html

时间: 2024-11-08 04:12:49

VC中function函数解析的相关文章

VC 中 UpdateData() 函数的使用

UpdateData(FALSE)与UpdateData(TRUE)是相反的过程 UpdateData(FALSE)是把程序中改变的值更新到控件中去 UpdateData(TRUE)是把在控件中输入的值更新到你的变量中去 TRUE    ===> 控件    -〉数据成员 FALSE ===>   数据成员 -〉 控件 VC 中 UpdateData() 函数的使用,布布扣,bubuko.com

JavaScript中Function函数与Object对象的关系

函数对象和其他内部对象的关系 除了函数对象,还有很多内部对象,比如:Object.Array.Date.RegExp.Math.Error.这些名称实际上表示一个 类型,可以通过new操作符返回一个对象.然而函数对象和其他对象不同,当用typeof得到一个函数对象的类型时,它仍然会返回字符串 "function",而typeof一个数组对象或其他的对象时,它会返回字符串"object".下面的代码示例了typeof不同类型的情况: 以下是引用片段: alert(ty

javascript中function 函数递归的陷阱问题

//看下这个递归方法,最后输出的值function fn(i){ i++; if(i<10){ fn(i); } else{ return i; } } var result = fn(0); console.log(result); 大部分人都可能一下就会说出结果为10,但是真实的结果是undefined.为什么呢?因为对于每一个函数,没有写return的返回值的时候,其实对于var a = function(); 就是没有给a赋值,那么这个值就是默认的undefined值了,可以typeof

转;VC++中Format函数详解

Format是一个很常用,却又似乎很烦的方法,以下是它的完整概貌,以供大家查询之用: 一.字符串 首先看它的声明: function Format(const Format: string; const Args: array of const): string; overload; 事实上Format方法有两个种形式,另外一种是三个参数的,主要区别在于它是线程安全的, 但并不多用,所以这里只对第一个介绍: function Format(const Format: string; const

VC中UpdateData()函数的使用

UpdateData(FALSE)与UpdateData(TRUE)是相反的过程     UpdateData(FALSE)是把程序中改变的值更新到控件中去 UpdateData(TRUE)是把在控件中输入的值更新到你的变量中去 TRUE    ===> 控件    -〉数据成员 FALSE ===>   数据成员 -〉 控件     比如你的一个编辑框控件c_Edit与变量s_Edit相关联 在程序中你用了     s_Edit="CSDN"但是你在编辑框中输入"

Matlab中sortrows函数解析

一.问题来源 返回检索到的数据(按相关度排序)在原始数据中的索引. 二.问题解析 x = [1 4 3 5; 1 3 2 6]:sortrows(x)其结果是按照row来排列,默认首先排第一列,1和1一样大,那么排第二列,3比4小,所以1 3 2 6应该在第一行.假如使用sortrows(x, 4)那么结果中首先看第4列的大小,5比6小,那么原来的顺序就对了,如果是x = 1 2 3 61 2 3 5那么,sortrows(x, 4)之后,由于第4列的5比6小,那么,1 2 3 5应该排到前面.

keras中to_categorical()函数解析

from keras.utils.np_utils import * # 类别向量定义 b = [0, 1, 2, 3, 4, 5, 6, 7, 8] # 调用to_categorical将b按照9个类别来进行转换 b = to_categorical(b, 9) print(b) 来源:https://blog.csdn.net/moyu123456789/article/details/83444140 原文地址:https://www.cnblogs.com/yibeimingyue/p/

在VC中读写ini配置文件

配置文件中经常用到ini文件,在VC中其函数分别为: 写入.ini文件:bool WritePrivateProfileString(LPCTSTR lpAppName,LPCTSTR lpKeyName,LPCTSTR lpString,LPCTSTR lpFileName); 读取.ini文件:DWORD GetPrivateProfileString(LPCTSTR lpAppName,LPCTSTR lpKeyName,LPCTSTR lpDefaut,LPSTR lpReturnedS

深度解析VC中的消息(转发)

http://blog.csdn.net/chenlycly/article/details/7586067 这篇转发的文章总结的比较好,但是没有告诉我为什么ON_MESSAGE的返回值必须是LRESULT 摘要: Windows编程和Dos编程,一个很大的区别就是,windows编程是事件驱动,消息传递的.所以,要做好windows编程,必须对消息机制有一个清楚的认识,本文希望能够对消息的传递做一个全面的论述,由于小生初学VC,里面可能有一些错误的地方,还往各位大虾批评.指正. 注意:有些消息