C++11 bind源码分析

以VS2019 bind源码为例分析。

再c++11之前有bind1st和bind2nd两个函数。但是有很大局限性。只能绑定一个继承自binary_function的函数。C++11的bind对此做了泛型拓展。可以绑定所有callable object

bind函数的两个重载定义

// FUNCTION TEMPLATE bind (implicit return type)
template <class _Fx, class... _Types>
_NODISCARD inline _Binder<_Unforced, _Fx, _Types...> bind(
    _Fx&& _Func, _Types&&... _Args) { // bind a callable object with an implicit return type
    return _Binder<_Unforced, _Fx, _Types...>(_STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...);
}

// FUNCTION TEMPLATE bind (explicit return type)
template <class _Ret, class _Fx, class... _Types>
_NODISCARD _Binder<_Ret, _Fx, _Types...> bind(
    _Fx&& _Func, _Types&&... _Args) { // bind a callable object with an explicit return type
    return _Binder<_Ret, _Fx, _Types...>(_STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...);
}

最终绑定函数的调用时通过结构体模板_Invoker_ret调用invoke实现的。这在function源码分析时列举过。
一个隐式绑定函数的返回值类型,_Unforced类型最终在结构体模板_Invoker_ret的重载中推到出绑定函数的返回值。

template <>
struct _Invoker_ret<_Unforced, false> { // selected for _Rx being _Unforced
    template <class... _Valtys>
    static auto _Call(_Valtys&&... _Vals)
        -> decltype(_STD invoke(_STD forward<_Valtys>(_Vals)...)) { // INVOKE, unchanged
        return _STD invoke(_STD forward<_Valtys>(_Vals)...);
    }
};

一个显式指定绑定函数的返回值类型。

template <class _Rx>
struct _Invoker_ret<_Rx, false> { // selected for all _Rx other than cv void and _Unforced
    template <class... _Valtys>
    static _Rx _Call(_Valtys&&... _Vals) { // INVOKE, implicitly converted to _Rx
        return _STD invoke(_STD forward<_Valtys>(_Vals)...);
    }
};

bind函数的返回值类型是_Binder类。bind函数的具体实现是将要绑定函数和绑定参数打包到_Binder类中,然后返回该_Binder类对象。
class _Binder:

template <class _Ret, class _Fx, class... _Types>
class _Binder : public _Binder_result_type<_Ret, _Fx>::type { // wrap bound callable object and arguments
private:
    using _Seq    = index_sequence_for<_Types...>;
    using _First  = decay_t<_Fx>;
    using _Second = tuple<decay_t<_Types>...>;

    _Compressed_pair<_First, _Second> _Mypair;

public:
    explicit _Binder(_Fx&& _Func, _Types&&... _Args)
        : _Mypair(_One_then_variadic_args_t(), _STD forward<_Fx>(_Func),
              _STD forward<_Types>(_Args)...) { // construct from forwarded callable object and arguments
    }

#define _BINDER_OPERATOR(CONST_OPT)                                                                           \
    template <class... _Unbound>                                                                              \
    auto operator()(_Unbound&&... _Unbargs)                                                                   \
        CONST_OPT->decltype(_Call_binder(_Invoker_ret<_Ret>(), _Seq(), _Mypair._Get_first(), _Mypair._Myval2, \
            _STD forward_as_tuple(_STD forward<_Unbound>(                                                     \
                _Unbargs)...))) { /* invoke bound callable object with bound/unbound arguments */             \
        return _Call_binder(_Invoker_ret<_Ret>(), _Seq(), _Mypair._Get_first(), _Mypair._Myval2,              \
            _STD forward_as_tuple(_STD forward<_Unbound>(_Unbargs)...));                                      \
    }

    _CLASS_DEFINE_CONST(_BINDER_OPERATOR)
#undef _BINDER_OPERATOR
};
// FUNCTION TEMPLATE _Call_binder
template <class _Ret, size_t... _Ix, class _Cv_FD, class _Cv_tuple_TiD, class _Untuple>
inline auto _Call_binder(_Invoker_ret<_Ret>, index_sequence<_Ix...>, _Cv_FD& _Obj, _Cv_tuple_TiD& _Tpl, _Untuple&& _Ut)
    -> decltype(_Invoker_ret<_Ret>::_Call(
        _Obj, _Fix_arg(_STD get<_Ix>(_Tpl), _STD move(_Ut))...)) { // bind() and bind<R>() invocation
    return _Invoker_ret<_Ret>::_Call(_Obj, _Fix_arg(_STD get<_Ix>(_Tpl), _STD move(_Ut))...);
}

从源码可以看出,将函数对象和函数参数列表保存在成员变量_Mypair中。
调用时通过类_Binder的operator()函数最终通过invoke调用,根据不同的函数类别(普通C函数,类成员函数,类或结构体对象等等),调用不同的invoke重载。

这里面有个比较重要也是比较难理解的是参数的绑定时怎么实现的

template <class _Ty, _Ty... _Vals>
struct integer_sequence { // sequence of integer parameters
    static_assert(is_integral_v<_Ty>, "integer_sequence<T, I...> requires T to be an integral type.");

    using value_type = _Ty;

    _NODISCARD static constexpr size_t size() noexcept {
        return sizeof...(_Vals);
    }
};

// ALIAS TEMPLATE make_integer_sequence
template <class _Ty, _Ty _Size>
using make_integer_sequence = __make_integer_seq<integer_sequence, _Ty, _Size>;

template <size_t... _Vals>
using index_sequence = integer_sequence<size_t, _Vals...>;

template <size_t _Size>
using make_index_sequence = make_integer_sequence<size_t, _Size>;

template <class... _Types>
using index_sequence_for = make_index_sequence<sizeof...(_Types)>;

这里用一个例子比较好理解。

void func(int a, int b, std::string c, double c) {}
std::function<void (int, double)> functor = std::bind(func, 5, std::placeholders::_1, "abc", std::placeholders::_2);

在_Binder 类中using _Seq = index_sequence_for<_Types...>;,这里套用上面这个实际的例子,那么_Seq的类型展开就是:index_sequence_for<int, std::placeholders::_1, std::string, std::placeholders::_2>
sizeof…(_Types)在这个例子中等于4,所以再展开:
make_index_sequence <4>
再展开
make_integer_sequence<size_t, 4>
由于没找到__make_integer_seq的源码(make_index_sequence的实现之后有时间会再做讨论),但是从看得到的源码可以知道make_integer_sequence<size_t, 4>最终展开是
index_sequence<0,1, 2, 3>

将这个模板结构体对象传入_Call_binder:

_Call_binder(_Invoker_ret<_Ret>(), _Seq(), _Mypair._Get_first(), _Mypair._Myval2,              \
            _STD forward_as_tuple(_STD forward<_Unbound>(_Unbargs)...))

再结合_Call_binder的实现可以看出

_Fix_arg(_STD get<_Ix>(_Tpl), _STD move(_Ut))...

根据模板可变参数展开规则,可以展开为:

_Fix_arg(_STD get<0>(_Tpl), _STD move(_Ut)), \
_Fix_arg(_STD get<1>(_Tpl), _STD move(_Ut)), \
_Fix_arg(_STD get<2>(_Tpl), _STD move(_Ut)), \
_Fix_arg(_STD get<3>(_Tpl), _STD move(_Ut))

所以这里是用make_index_sequence在编译阶段展开作为tuple的参数来实现展开tuple。
这里有一点需要注意,真正执行的顺序是这样的:

_Fix_arg(_STD get<3>(_Tpl), _STD move(_Ut))
_Fix_arg(_STD get<2>(_Tpl), _STD move(_Ut))
_Fix_arg(_STD get<1>(_Tpl), _STD move(_Ut))
_Fix_arg(_STD get<0>(_Tpl), _STD move(_Ut))

我的理解是可变参数展开作为参数,这样先执行的先入栈,后执行的后入栈。
我们知道_cdecl函数参数是从右向左依次入栈。这样正好作为参数展开之后的顺序就是正序了。

接下来我们再看_Fix_arg的实现。其实不用分析代码,到这里我们根据bind的使用经验也可以猜到这个_Fix_arg函数的作用:将调用时传入的参数绑定到定义时非绑定的参数位置。

template <class _Cv_TiD, class _Untuple>
auto _Fix_arg(_Cv_TiD& _Tid, _Untuple&& _Ut)
    -> decltype(_Select_fixer<_Cv_TiD>::_Fix(_Tid, _STD move(_Ut))) { // translate an argument for bind
    return _Select_fixer<_Cv_TiD>::_Fix(_Tid, _STD move(_Ut));
}

具体的实现在_Select_fixer:

// FUNCTION TEMPLATE _Fix_arg
template <class _Cv_TiD, bool = _Is_specialization_v<remove_cv_t<_Cv_TiD>, reference_wrapper>,
    bool = is_bind_expression_v<_Cv_TiD>, int = is_placeholder_v<_Cv_TiD>>
struct _Select_fixer;

template <class _Cv_TiD>
struct _Select_fixer<_Cv_TiD, true, false, 0> { // reference_wrapper fixer
    template <class _Untuple>
    static auto _Fix(_Cv_TiD& _Tid, _Untuple &&) -> typename _Cv_TiD::type& { // unwrap a reference_wrapper
        return _Tid.get();
    }
};

template <class _Cv_TiD>
struct _Select_fixer<_Cv_TiD, false, true, 0> { // nested bind fixer
#pragma warning(push)
#pragma warning(disable : 4100) // TRANSITION, VSO#181496, unreferenced formal parameter
    template <class _Untuple, size_t... _Jx>
    static auto _Apply(_Cv_TiD& _Tid, _Untuple&& _Ut,
        index_sequence<_Jx...>) -> decltype(_Tid(_STD get<_Jx>(_STD move(_Ut))...)) { // call a nested bind expression
        return _Tid(_STD get<_Jx>(_STD move(_Ut))...);
    }
#pragma warning(pop)

    template <class _Untuple>
    static auto _Fix(_Cv_TiD& _Tid, _Untuple&& _Ut) -> decltype(_Apply(_Tid, _STD move(_Ut),
        make_index_sequence<tuple_size_v<_Untuple>>())) { // call a nested bind expression
        return _Apply(_Tid, _STD move(_Ut), make_index_sequence<tuple_size_v<_Untuple>>());
    }
};

template <class _Cv_TiD>
struct _Select_fixer<_Cv_TiD, false, false, 0> { // identity fixer
    template <class _Untuple>
    static _Cv_TiD& _Fix(_Cv_TiD& _Tid, _Untuple&&) { // pass a bound argument as an lvalue (important!)
        return _Tid;
    }
};

template <class _Cv_TiD, int _Jx>
struct _Select_fixer<_Cv_TiD, false, false, _Jx> { // placeholder fixer
    static_assert(_Jx > 0, "invalid is_placeholder value");

    template <class _Untuple>
    static auto _Fix(_Cv_TiD&, _Untuple&& _Ut)
        -> decltype(_STD get<_Jx - 1>(_STD move(_Ut))) { // choose the Jth unbound argument (1-based indexing)
        return _STD get<_Jx - 1>(_STD move(_Ut));
    }
};

从上面的实现可知,其实是遍历_Binder对象构造时保存的参数列表,如果是指定的参数,返回指定的参数,如果是placeholder占位的,取函数调用时传入的非绑定参数列表。

如果_Fix_arg函数传入的第一个参数是placeholders类型,会调用上面_Select_fixer的最后一个特化版本。_Jx其实是萃取的_Ph的模板参数值(如下列举1~20)。

placeholders 的定义如下:

namespace placeholders {
    _INLINE_VAR constexpr _Ph<1> _1{};
    _INLINE_VAR constexpr _Ph<2> _2{};
    _INLINE_VAR constexpr _Ph<3> _3{};
    _INLINE_VAR constexpr _Ph<4> _4{};
    _INLINE_VAR constexpr _Ph<5> _5{};
    _INLINE_VAR constexpr _Ph<6> _6{};
    _INLINE_VAR constexpr _Ph<7> _7{};
    _INLINE_VAR constexpr _Ph<8> _8{};
    _INLINE_VAR constexpr _Ph<9> _9{};
    _INLINE_VAR constexpr _Ph<10> _10{};
    _INLINE_VAR constexpr _Ph<11> _11{};
    _INLINE_VAR constexpr _Ph<12> _12{};
    _INLINE_VAR constexpr _Ph<13> _13{};
    _INLINE_VAR constexpr _Ph<14> _14{};
    _INLINE_VAR constexpr _Ph<15> _15{};
    _INLINE_VAR constexpr _Ph<16> _16{};
    _INLINE_VAR constexpr _Ph<17> _17{};
    _INLINE_VAR constexpr _Ph<18> _18{};
    _INLINE_VAR constexpr _Ph<19> _19{};
    _INLINE_VAR constexpr _Ph<20> _20{};
} // namespace placeholders

// PLACEHOLDERS
template <int _Nx>
struct _Ph { // placeholder
};

template <class _Tx>
struct is_placeholder : integral_constant<int, 0> { // template to indicate that _Tx is not a placeholder
};

template <int _Nx>
struct is_placeholder<_Ph<_Nx>>
    : integral_constant<int, _Nx> { // template specialization to indicate that _Ph<_Nx> is a placeholder
};

template <class _Tx>
struct is_placeholder<const _Tx> : is_placeholder<_Tx>::type { // ignore cv-qualifiers
};

template <class _Tx>
struct is_placeholder<volatile _Tx> : is_placeholder<_Tx>::type { // ignore cv-qualifiers
};

template <class _Tx>
struct is_placeholder<const volatile _Tx> : is_placeholder<_Tx>::type { // ignore cv-qualifiers
};

template <class _Ty>
_INLINE_VAR constexpr int is_placeholder_v = is_placeholder<_Ty>::value;

通过以上对bind源码的分析,我们也知道了bind的内部实现技巧。
不过还有一个问题:bind函数返回的是一个_Binder对象,是怎么能赋值给一个function对象呢?
答案是:_Binder对象的调用就是通过operator()。
其实我们回忆一下function的源码,_Binder对象其实就是一个callable object。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值