humorly / wstd-bind Goto Github PK
View Code? Open in Web Editor NEW实作std::bind之 wstd::bind
License: MIT License
实作std::bind之 wstd::bind
License: MIT License
剖析std::bind源码,根据其中的设计**,实作相对简洁易懂的wstd::bind,源码实现如下:
一:构建binder对象
1.构建std::binder,用以存储函数指针与相关绑定参数/占位符;
2.提供call接口,用以function执行函数调用时回调该接口;
二:构建函数对象
1.构建std::function,重载调用操作符,用以实现函数调用;
2.编写std::function以binder对象的构造函数,用以在function函数调用时转入binder之中的调用过程,并将function中传入的参数一并传入其中;
三:构建bind接口
1.提供用以创建binder对象的bind接口,用以绑定成员/非成员函数;
实现过程
1.binder
// 构建非成员函数的binder模板
template <typename Fx, typename ... args>
class binder
{
public:
// f-> 绑定的函数指针, params...->参数列表
// tuple_ -> 用以接收bind构造过程的参数
binder(Fx f, args ... params) : tuple_(params ...)
{
function_ = f;
}
virtual ~binder() {}
// 提供给function对象调用的回调
// function调用时将传入附带参数列表
template <typename ... args>
auto call(args ... params)
{
return func(args_seq_, params ...);
};
// call的实际处理过程,该过程实际将原先bind绑定的参数包展开
// 判定bind时有占位符时,将替换以function调用call时的传入参数
template <std::size_t ... I, typename ... args>
auto func(std::index_sequence<I ...>, args ... params)
{
return function_(params_wrapper< decltype(std::get<I>(tuple_)) >::template get_value(std::get<I>(tuple_), params ...) ...);
}
protected:
// 用以存储函数指针
Fx function_;
// 获取绑定的参数列表
std::index_sequence_for<args...> args_seq_{};
// 存储参数列表的元组
std::tuple<args...> tuple_;
};
2.funciton
①.funciton
// funciton在构建之时本需要对参数模板Fx进行拆分
// 用以剥离返回类型ret_type与参数列表args ...
// 此处通过继承func_impl以实现剥离参数
// 继承于此,因此也将相关接口封装至基类中
template <typename Fx>
class function : public func_impl<Fx>
{
public:
// 上述创建的binder对象将会被传入至此
template<typename __binder_type>
function(__binder_type t)
{
// 调用基类初始函数
// 将binder实质传入其中
this->reset(t);
}
virtual ~function() {}
// 重载调用操作符,经过function调用过程传入的参数
// 将会经过此处投递至binder对象的call中
template<typename ... args>
auto operator ()(args ... params)
{
this->call(params ...);
}
};
②.func_impl
// func_impl常规版本
template <typename ret, typename ... args>
class func_impl {};
// 对函数类型ret (args ...)偏特化以萃取返回值ret与参数列表args ...
template <typename ret, typename ... args>
class func_impl<ret (args ...)>
{
public:
typedef typename ret ret_type;
func_impl() {}
virtual ~func_impl()
{
if (nullptr != impl_)
delete impl_;
}
protected:
// function调用此处时将binder对象传入
// binder对象将会被用以构造binder_impl对象
// 利用此方法即可在function构造中存储binder类型
template <typename binder>
void reset(binder call_impl)
{
// 存储binder_impl类型
impl_ = new binder_impl<binder, ret, args...>(call_impl);
}
// function对象调用此处以跳转到binder_impl_warpper中的virtual call
// 即会跳转至binder_impl中的call
ret call(args ... params)
{
return impl_->call(params ...);
}
// 存储binder_impl对象指针,其中存储了binder对象
// 在binder对象构造之前并不知道binder
// 会指向何种类型的函数指针
// 因此只能在构造时确定该binder_impl指针
binder_impl_wrapper<ret, args ...> * impl_ = nullptr;
};
④.binder_impl_wrapper
// 用来进行跳转的基类
template <typename ret, typename ... args>
class binder_impl_wrapper
{
public:
binder_impl_wrapper() {}
virtual ~binder_impl_wrapper() {}
// 用以跳转的接口
virtual ret call(args ... parmas) { return ret(); };
};
⑤.binder_impl
// call_type即为binder类型,ret -> 返回值类型
// args ... 参数包类型
template <typename call_type, typename ret, typename ... args>
class binder_impl : public binder_impl_wrapper<ret, args ...>
{
public:
// 构造过程存储实际的binder类型
binder_impl(call_type cal) : caller_(cal)
{
}
virtual ~binder_impl() {}
// 经过func_impl调用而跳转至此
virtual ret call(args ... parmas)
{
// 转到真实的binder中的call接口
// 至此从function函数调用跳转到binder中的call
return caller_.call(parmas ...);
};
protected:
// 实际的binder对象存储
call_type caller_;
};
⑥.核心调用过程 -> binder.call()
template <typename ... args>
auto call(args ... params)
{
// args_seq_ -> 传递bind参数序列,用以展开binder的参数
// params ... -> function传递的参数,用以替换binder对应占位符参数
return func(args_seq_, params ...);
};
template <std::size_t ... I, typename ... args>
auto func(std::index_sequence<I ...>, args ... params)
{
// tuple_中存储的是bind时的实际参数内容
// 需要一个模板对参数(std::get<I>(tuple_)参数类型进行特化,
// #1.如果是占位符则获取占位符标记N ,比如std::placeholder::_1则捕获1,
// 而后此处用args ...中的第1个参数替换
// #2.如果非占位符则直接获取(std::get<I>(tuple_)的值以传入函数
// 指针function_中作为参数
// 这里使用模板params_wrapper实现
return function_(params_wrapper< decltype(std::get<I>(tuple_)) >::template get_value(std::get<I>(tuple_), params ...) ...);
}
⑦.params_wrapper
// 常规版本 -> 非占位符
template <typename __place_type>
struct params_wrapper
{
// 如果调用到此处直接返回参数val,即上述中的std::get<I>(tuple_)
// 否则调用占位符特化版本中的参数包中的对应参数
template <typename ... args>
static auto get_value(__place_type val, args ... params)
{
return val;
}
};
// 占位符类型的偏特化
// placeholders special
#define place_holder_wrapper(__index) template <> \
struct params_wrapper<std::_Ph<__index> &> \
{ \
template <typename ... args> \
static auto get_value(std::_Ph<__index> &, args ... params) \
{ \
// 构造参数列表
static std::tuple<args...> tu_(params ...); \
// 返回占位符std::_Ph<__index>对应的function参数包中的参数
return std::get<__index - 1>(tu_); \
} \
};
// 使用上述定义偏特化宏偏特化占位符 std::_Ph<1> ~ std::_Ph<20>
// placeholder 1 ~ 20
place_holder_wrapper(1);
place_holder_wrapper(2);
place_holder_wrapper(3);
place_holder_wrapper(4);
place_holder_wrapper(5);
place_holder_wrapper(6);
place_holder_wrapper(7);
place_holder_wrapper(8);
place_holder_wrapper(9);
place_holder_wrapper(10);
place_holder_wrapper(11);
place_holder_wrapper(12);
place_holder_wrapper(13);
place_holder_wrapper(14);
place_holder_wrapper(15);
place_holder_wrapper(16);
place_holder_wrapper(17);
place_holder_wrapper(18);
place_holder_wrapper(19);
place_holder_wrapper(20);
3.wstd::bind接口提供
问题:wstd::bind需要解决一个问题,区分成员函数/非成员函数,对于非成员函数提供的binder模板是binder,对于成员提供一个新模板binder_t,实现如下:
①.binder_t
// function wrapper with member funciton
template <typename Fx, typename T, typename ... args>
class binder_t
{
public:
binder_t(Fx f, T ob, args ... params) : tuple_(params ...)
{
function_ = f;
ob_ = ob;
}
virtual ~binder_t() {}
// call
template <typename ... args>
auto call(args ... params)
{
return func(args_seq_, params ...);
};
template <std::size_t... I, typename ... args>
auto func(std::index_sequence<I...>, args ... params)
{
// 调用时以成员指针形式调用
return (ob_->*function_)(params_wrapper< decltype(std::get<I>(tuple_)) >::template get_value(std::get<I>(tuple_), params ...)...);
}
protected:
// 存储成员函指针
Fx function_;
// 存储class类型
T ob_;
std::index_sequence_for<args ...> args_seq_{};
std::tuple<args ...> tuple_;
};
基于上述,wstd::bind接口需要能判定是否成员/非成员函数,以返回不同的类型,因此需要在构建一个区分模板make_binder
②.make_binder
// 常规版本
// binder selector
template <bool __type>
struct make_binder
{
// 非成员函数返回binder类型
template<typename Fx, typename ... args>
static binder<Fx, args...> bind(Fx f, args... a)
{
binder<Fx, args...> binder_(f, a...);
return binder_;
}
};
// 偏特化成员函数
template <>
struct make_binder<true>
{
// 成员函数返回binder_t
template<typename Fx, typename T, typename ... args>
static binder_t<Fx, T, args...> bind(Fx f, T ob, args... a)
{
binder_t<Fx, T, args...> binder_(f, ob, a...);
return binder_;
}
};
因此,完整的wstd::bind接口如下:
template<typename Fx, typename T, typename ... args>
auto bind(Fx f, T ob, args ... params)
{
// 利用std::is_member_function_pointer<decltype(f)即可判定函数指针f是否成员函数
// value = true即为成员函数,反之即为非成员
return make_binder<std::is_member_function_pointer<decltype(f)>::value>::template bind(f, ob, params ...);
}
#测试代码
#include "bind.h"
#include <iostream>
#include <functional>
class print
{
public:
print() {}
virtual ~print() {}
int draw(int a, int b, int c)
{
std::cout << "a = " << a << "," << "b = " << b << "," << "c = " << c << "\n";
return 0;
}
};
int test(int a, int b, int c)
{
std::cout << "a = " << a << "," << "b = " << b << "," << "c = " << c << "\n";
return 0;
}
int main()
{
print pt_;
std::function<int(int)> invoke_ = std::bind(test, 0, 0, std::placeholders::_1);
invoke_(12345);
std::function<int(int)> mem_invoke_ = std::bind(&print::draw, &pt_, 0, 0, std::placeholders::_1);
mem_invoke_(12345);
wstd::function<int (int)> inv_ = wstd::bind(test, 0, 0, std::placeholders::_1);
inv_(12345);
wstd::function<int (int)> mem_inv_ = wstd::bind(&print::draw, &pt_, 0, 0, std::placeholders::_1);
mem_inv_(123);
return 0;
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.