一步步手撸一个std::function

目录

前言

第一版

第二版

第三版

总结

他山之石


前言

在《std::function从实践到原理》中我们分析了std::function的实现原理,但这只是纸上谈兵。要想理解为什么这么实现,最好的办法还是想想要是自己手写一个要怎么实现。本文不想直接呈现最终版本,因为那样读者看不到某段代码是为了什么才那么写。我会搭建好几版,争取把所想所思都体现出来。

第一版

我们先不考虑复制构造函数,也不考虑移动构造函数,只考虑最普通的构造函数, 一共两种类型(functor type)可以赋值给Myfunction: lambda表达式、函数指针。仿函数与lambda异曲同工,略。这两种类型要么通过类模板参数要么通过构造函数的模板参数传进来,分别形如:

template<typename _Res, typename... _ArgTypes, typename _Functor>
class Myfunction 
//构造函数        
template<typename _Functor> 
Myfunction(_Functor __f){
     。。。
}

第一种要求用户必须这样使用:Myfunction<..., lambda0>, 但实际用户是给不出lambda表达式对应的类的(由编译器给出), 除非用decltype; 但即使给出来了,也不符合一贯的std::function<函数声明>的用法。所以只能是第二种。然后还有个需求:用户传过来的lambda对象或者函数指针得copy一份,这样以后才能调用到它们(重载函数operator ()()中调用)所以这个Myfunction大概得长这样:

template<typename _Res, typename... _ArgTypes>
class Myfunction 
{
public:
        template<typename _Functor> 
        Myfunction(_Functor __f){
                f = new _Functor(std::move(__f));
        }

        _Res operator()(_ArgTypes... __args) const{
                return (*f)(std::forward<_ArgTypes>(__args)...);
        }

private:
        _Functor* f;
};

但这样有个大问题:_Functor作用域只在构造函数内,_Functor* f是编译不过的。所以f要承接lambda对象,也要承接函数指针,那只能是万能指针void* f或char* f了。

如果f成了void*类型,operator()()中还是得知道f原本的类型,因为void* f不是callable的,你不能写 f(), 得把f转换成原来的类型如lambda或函数指针。_Functor只在构造函数中有,但operator()()却要使用,怎么办?

第二版

要解决这个问题,我们就得学一学std::function了。引入一个函数指针,它里面保留_Functor这个信息。

template<typename _Res, typename _Functor, typename... _ArgTypes>
class _MyFunction_handler
{
public:
    statc _Res _Function_handler_invoke(void* _Any_data,  _ArgTypes... __args){
        return (*(_Functor*)_Any_data)(std::forward<_ArgTypes>(__args)...);
    }
};

template<typename _Res, typename... _ArgTypes>
class MyFunction;

template<typename _Res, typename... _ArgTypes>
class MyFunction<_Res(_ArgTypes...)>
{
public:
    template<typename _Functor>
    MyFunction(_Functor __f){
        f = new _Functor(std::move(__f));
        _M_invoker = &_MyFunction_handler<_Res,_Functor,_ArgTypes...>::_Function_handler_invoke;
    }

    _Res operator()(_ArgTypes... __args) const{
        return (*_M_invoker)(f, std::forward<_ArgTypes>(__args)...);
    }

private:
    void* f;

    typedef _Res (*_Invoker_type)(void* _Any_data, _ArgTypes...);
    _Invoker_type _M_invoker;
};

 _Function_handler_invoke与_M_invoker的函数签名都一样:_Res (void*, _ArgTypes...), 只不过_Function_handler_invoke里面保留了_Functor信息,可以把void* f转换成原来的样子。当然_Function_handler_invoke也可以直接写在Myfunction内。

_M_invoker是一个桥梁,把构造和调用连接了起来(_Functor方面) 。写几个测试用例试一试:

int gAdd(int a, int b){
    return a+b;
}

int main(){
    MyFunction<int(int,int)> f1 = [](int a,int b)->int {return a+b;};
    int res = f1(1,2);
    std::cout<<res<<std::endl;

    MyFunction<int(int,int)> f2 = gAdd;
    int res2 = f2(3,4);
    std::cout<<res2<<std::endl;
}

能走通了!

显然有个问题:new的东西没释放。依照_M_invoker再搞一个_M_destroy。

第三版

#include <iostream>

template<typename _Res, typename _Functor, typename... _ArgTypes>
class _MyFunction_handler
{
public:
    static _Res _Function_handler_invoke(void* _Any_data,  _ArgTypes... __args){
        return (*(_Functor*)_Any_data)(std::forward<_ArgTypes>(__args)...);
    }
    static void _Function_handler_destroy(void* _Any_data){
        //((_Functor*)_Any_data)->~_Functor();
        delete ((_Functor*)_Any_data);
    }
};

template<typename _Res, typename... _ArgTypes>
class MyFunction;

template<typename _Res, typename... _ArgTypes>
class MyFunction<_Res(_ArgTypes...)>
{
public:
    template<typename _Functor>
    MyFunction(_Functor __f){
        f = new _Functor(std::move(__f));
        _M_invoker = &_MyFunction_handler<_Res,_Functor,_ArgTypes...>::_Function_handler_invoke;
        _M_destroy = &_MyFunction_handler<_Res,_Functor,_ArgTypes...>::_Function_handler_destroy;
    }

    _Res operator()(_ArgTypes... __args) const{
        return (*_M_invoker)(f, std::forward<_ArgTypes>(__args)...);
    }
    ~MyFunction(){
        (*_M_destroy)(f);
    }
private:
    void* f;

    typedef _Res (*_Invoker_type)(void* _Any_data, _ArgTypes...);
    _Invoker_type _M_invoker;
    typedef void (*_Destroy_type)(void* _Any_data);
    _Destroy_type _M_destroy;

};

int gAdd(int a, int b){
    return a+b;
}

int main(){
    MyFunction<int(int,int)> f1 = [](int a,int b)->int {return a+b;};
    int res = f1(1,2);
    std::cout<<res<<std::endl;

    MyFunction<int(int,int)> f2 = gAdd;
    int res2 = f2(3,4);
    std::cout<<res2<<std::endl;

}

[mzhai@~]$ g++ myfunction.cpp -std=c++11 -g
[mzhai@~]$ ./a.out
3
7
[mzhai@c++11]$ valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt1 ./a.out
3
7

valgrind测试也没有内存泄漏。读者如想看看模板是怎么展开的,只需把上面的代码拷贝到c++ insights  即可。

总结

完美?NO,还没有处理给Myfunction赋值nullptr的情况。 还没有写各种各样的构造函数, 其中copy构造函数也需要知道原来的类型(lambda类型还是函数指针?),这样就又得起一个类似_M_invoker/_M_destroy的成员,std::function中的_M_manager就是为copy function和析构而引入的,只不过它最后有一个参数std::_Manager_operation用来区分是copy构造还是析构。 

他山之石

后来我在Stack Overflow上看到一个别人写的std::function. 大体思想一样,贴在下面:

#include <iostream>
#include <memory>

template <typename T>
class function;

template <typename R, typename... Args>
class function<R(Args...)>
{
    // function pointer types for the type-erasure behaviors
    // all these char* parameters are actually casted from some functor type
    typedef R (*invoke_fn_t)(char*, Args&&...);
    typedef void (*construct_fn_t)(char*, char*);
    typedef void (*destroy_fn_t)(char*);

    // type-aware generic functions for invoking
    // the specialization of these functions won't be capable with
    //   the above function pointer types, so we need some cast
    template <typename Functor>
    static R invoke_fn(Functor* fn, Args&&... args)
    {
        return (*fn)(std::forward<Args>(args)...);
    }

    template <typename Functor>
    static void construct_fn(Functor* construct_dst, Functor* construct_src)
    {
        // the functor type must be copy-constructible
        new (construct_dst) Functor(*construct_src);
    }

    template <typename Functor>
    static void destroy_fn(Functor* f)
    {
        f->~Functor();
    }

    // these pointers are storing behaviors
    invoke_fn_t invoke_f;
    construct_fn_t construct_f;
    destroy_fn_t destroy_f;

    // erase the type of any functor and store it into a char*
    // so the storage size should be obtained as well
    std::unique_ptr<char[]> data_ptr;
    size_t data_size;
public:
    function()
        : invoke_f(nullptr)
        , construct_f(nullptr)
        , destroy_f(nullptr)
        , data_ptr(nullptr)
        , data_size(0)
    {}

    // construct from any functor type
    template <typename Functor>
    function(Functor f)
        // specialize functions and erase their type info by casting
        : invoke_f(reinterpret_cast<invoke_fn_t>(invoke_fn<Functor>))
        , construct_f(reinterpret_cast<construct_fn_t>(construct_fn<Functor>))
        , destroy_f(reinterpret_cast<destroy_fn_t>(destroy_fn<Functor>))
        , data_ptr(new char[sizeof(Functor)])
        , data_size(sizeof(Functor))
    {
        // copy the functor to internal storage
        this->construct_f(this->data_ptr.get(), reinterpret_cast<char*>(&f));
    }

    // copy constructor
    function(function const& rhs)
        : invoke_f(rhs.invoke_f)
        , construct_f(rhs.construct_f)
        , destroy_f(rhs.destroy_f)
        , data_size(rhs.data_size)
    {
        if (this->invoke_f) {
            // when the source is not a null function, copy its internal functor
            this->data_ptr.reset(new char[this->data_size]);
            this->construct_f(this->data_ptr.get(), rhs.data_ptr.get());
        }
    }

    ~function()
    {
        if (data_ptr != nullptr) {
            this->destroy_f(this->data_ptr.get());
        }
    }

    // other constructors, from nullptr, from function pointers

    R operator()(Args&&... args)
    {
        return this->invoke_f(this->data_ptr.get(), std::forward<Args>(args)...);
    }
};

// examples
int main()
{
    int i = 0;
    auto fn = [i](std::string const& s) mutable
    {
        std::cout << ++i << ". " << s << std::endl;
    };
    fn("first");                                   // 1. first
    fn("second");                                  // 2. second

    // construct from lambda
    ::function<void(std::string const&)> f(fn);
    f("third");                                    // 3. third

    // copy from another function
    ::function<void(std::string const&)> g(f);
    f("forth - f");                                // 4. forth - f
    g("forth - g");                                // 4. forth - g

    // capture and copy non-trivial types like std::string
    std::string x("xxxx");
    ::function<void()> h([x]() { std::cout << x << std::endl; });
    h();

    ::function<void()> k(h);
    k();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深山老宅

鸡蛋不错的话,要不要激励下母鸡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值