boost::bind的简单实现
前言
在上一篇blog中简单的实现了boost::function,支持带有2个参数的函数/函数指针,函数对象,函数适配器/bind类,以及带有1个参数的成员函数指针。
本文接着来介绍如何实现一个简单的boost::bind。
基本目标如下:
- 支持接收0个参数的函数/函数指针,函数对象。
- 支持接收1个参数的函数/函数指针,函数对象。
- 支持接收2个参数的函数/函数指针,函数对象。
实现
首先,解决占位符的问题:
namespace
{ struct Placeholders1
{
} _1; struct Placeholders2
{
} _2; }
使用匿名namespace的原因是防止不同编译单元中的命名冲突, 让占位符对象只在其所在的编译单元中可见。
在boost::bind源码中主要是通过2个list表维持各种相关信息。一个bindlist表维持了bind接收的绑定参数,包括占位符,用户传入的变量等。一个calllist维持了调用bind返回的对象时所传入的参数信息。它们的通过继承层次的方式来表现的。
下面这个继承层次的每一个类都要作为对应的bindlist和calllist层次中的基类,它们分别保存了bind接收的绑定参数信息(用户传入的变量,占位符),以及调用bind返回的对象时所传入的参数信息。
class Base0
{
}; template<typename T1>
class Base1 : public Base0
{
public:
Base1(T1 data1)
: data1_(data1)
{
} protected:
T1 data1_;
}; template<typename T1, typename T2>
class Base2 : public Base1<T1>
{
public:
Base2(T1 data1, T2 data2)
: Base1<T1>(data1), data2_(data2)
{
} protected:
T2 data2_;
};
接着,就是所谓的calllist的实现了。它们的基类将保存调用bind返回的对象时所传入的参数信息。
class CallList0 : public Base0
{
public:
template<typename _T>
_T operator[](_T arg)
{
return arg;
}
}; template<typename T1>
class CallList1 : public Base1<T1>
{
public:
CallList1(T1 data1)
: Base1<T1>(data1)
{
} T1 operator[](Placeholders1 arg1)
{
return Base1<T1>::data1_;
} template<typename _T>
_T operator[](_T arg)
{
return arg;
} }; template<typename T1, typename T2>
class CallList2: public Base2<T1, T2>
{
public:
CallList2(T1 data1, T2 data2)
: Base2<T1, T2>(data1, data2)
{
} T1 operator[](Placeholders1 arg1)
{
return Base2<T1, T2>::data1_;
} T2 operator[](Placeholders2 arg2)
{
return Base2<T1, T2>::data2_;
} template<typename _T>
_T operator[](_T arg)
{
return arg;
}
};
然后,我们来看看bindlist,它们的基类主要保存了bind接收的占位符、参数信息。
class BindLinst0 : public Base0
{
public:
template<typename Func>
void operator()(Func func)
{
func();
}
}; template<typename T1>
class BindList1 : public Base1<T1>
{
public:
BindList1(T1 data1)
: Base1<T1>(data1)
{
} template<typename Func, typename Call>
void operator()(Func func, Call call)
{
func(call[Base1<T1>::data1_]);
}
}; template<typename T1, typename T2>
class BindList2 : public Base2<T1, T2>
{
public:
BindList2(T1 data1, T2 data2)
: Base2<T1, T2>(data1, data2)
{
} template<typename Func, typename Call>
void operator()(Func func, Call call)
{
func(call[Base2<T1, T2>::data1_], call[Base2<T1, T2>::data2_]);
}
};
接下来就是bind函数所返回的对象了,相信童鞋们可以想象的到,这个对象中应该主要保存的是bind函数接收的参数信息咯,并且他还保存了所注册的函数。
template<typename Func, typename Bind>
class BindImpl
{
public:
BindImpl(Func func, Bind bindlist)
: func_(func), bindlist_(bindlist)
{
} void operator()()
{
bindlist_(func_);
} template<typename T1>
void operator()(T1 data1)
{
bindlist_(func_, CallList1<T1>(data1));
} template<typename T1, typename T2>
void operator()(T1 data1, T2 data2)
{
bindlist_(func_, CallList2<T1, T2>(data1, data2));
} protected:
Func func_;
Bind bindlist_;
};
如此,基本的轮廓就已经出来了。bind函数返回一个BindImpl对象,里面保存了注册的函数和bind接收的占位符、参数信息。当我们调用这个对象的时候,会生成一个calllist对象,它保存了调用BindImpl对象时所传入的参数,然后在bindlist中调用注册的函数。
需要的注意的是,在bindlist调用函数时我们转而调用了calllist的operator[]函数,通过它来判断传入的参数是占位符还是用户传入的参数,如果是占位符,那么就返回calllist中保存的之前注册的用户传入的信息。如果不是占位符,operator[]函数就单纯的返回他接收的参数,也就是之前用户调用BindImpl时传入的参数。
最后,我们通过一组重载的bind函数来实现对接收0个参数、1个参数、2个参数的支持,它们返回的是一个BindImpl对象。
template<typename Func>
BindImpl<Func, BindLinst0> bind(Func func)
{
return BindImpl<Func, BindLinst0>(func, BindLinst0());
} template<typename Func, typename T1>
BindImpl<Func, BindList1<T1> > bind(Func func, T1 data1)
{
return BindImpl<Func, BindList1<T1> >(func, BindList1<T1>(data1));
} template<typename Func, typename T1, typename T2>
BindImpl<Func, BindList2<T1, T2> > bind(Func func, T1 data1, T2 data2)
{
return BindImpl<Func, BindList2<T1, T2> >(func, BindList2<T1, T2>(data1, data2));
}
这样bind函数的基本功能就实现了,但是需要提到的是,目前的bind并不支持注册成员函数。如果要支持成员函数注册的话,需要萃取函数指针的返回类型以及每个参数类型,具体方法在上一篇blog《boost::function的简单实现》中介绍到了,有兴趣的童鞋可以去看看。
下面就来简单的测试一下:
int get(int a, int b)
{
std::cout << a + b << std::endl;
return 0;
} class Point
{
public:
int operator()(int a, int b)
{
std::cout << "Point::operator() called: a = " << a + b << std::endl;
return a+b;
}
}; int main(int argc, char const *argv[])
{
bind(get, _1, 10)(20, 1);
bind(Point(), _1, _2)(3, 4);
return 0;
}
结果为:
30
Point::operator() called: a = 7
得到的结果正如预期的一样。
参考文献
- boost中文手册. bind.hpp
(完)
boost::bind的简单实现的更多相关文章
- 使用BOOST BIND库提高C++程序性能
Boost.Bind为函数和函数对象,值语义和指针提供语义了一致的语法.我们首先通过一些简单的例子来看看它的基本用法,之后我们会延伸到嵌套绑定以实现功能组合.理解bind用法的一个关键是理解占位符(p ...
- boost::bind 和 boost::function 基本用法
这是一篇介绍bind和function用法的文章,起因是近来读陈硕的文章,提到用bind和function替代继承,于是就熟悉了下bind和function的用法,都是一些网上都有的知识,记录一下,期 ...
- (转)boost::bind介绍
转自:http://www.cnblogs.com/sld666666/archive/2010/12/14/1905980.html 这篇文章介绍boost::bind()的用法, 文章的主要内容是 ...
- [置顶] 编程模仿boost::function和boost::bind
boost::function和boost::bind结合使用是非常强大的,他可以将成员函数和非成员函数绑定对一个对象上,实现了类似C#的委托机制.委托在许多时候可以替代C++里面的继承,实现对象解耦 ...
- 手把手教你实现boost::bind
前言 boost::bind操作想必大家都使用过,它特别神奇,能够绑定函数与参数,绑定后能够改变参数数量,并且还可以使用占位符.它可以绑定普通函数也可以绑定类成员函数.好多小伙伴试图看过boost:: ...
- boost::bind 介绍
boost::bind 介绍 这篇文章介绍boost::bind()的用法, 文章的主要内容是参考boost的文档. 1. 目的 boost::bind 是std::bindlist 和 std: ...
- Boost::bind使用详解
1.Boost::bind 在STL中,我们经常需要使用bind1st,bind2st函数绑定器和fun_ptr,mem_fun等函数适配器,这些函数绑定器和函数适配器使用起来比较麻烦,需要根据是全局 ...
- boost::bind 学习
最近学习了太多与MacOS与Iphone相关的东西,因为不会有太多人有兴趣,学习的平台又是MacOS,不太喜欢MacOS下的输入法,所以写下来的东西少了很多. 等我学习的东西慢慢的与平台无关的时 ...
- boost::function的简单实现
前言 boost::function和boost:bind是一对强大的利器.相信用过的童鞋多少有些体会. 虽然平时在用boost::function,但是用的时候心中总会一些不安,因为不知道它是怎么实 ...
随机推荐
- Django源码分析之执行入口
魔法门 一般我们启动django,最简单的方法是进入project 目录,这时目录结构是这样的 然后我们执行python manage.py runserver,程序就开始执行了. 那django是如 ...
- 剑指offer-矩形覆盖10
题目描述 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形.请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? # -*- coding:utf-8 -*- class S ...
- 基于Vue、web3的以太坊项目开发及交易内幕初探 错误解决总结
基于Vue.web3的以太坊项目开发及交易内幕初探 本文通过宏观和微观两个层面窥探以太坊底层执行逻辑. 宏观层面描述创建并运行一个小型带钱包的发币APP的过程,微观层面是顺藤摸瓜从http api深入 ...
- matlab mex 小o -o 出错
https://github.com/kyamagu/mexopencv/issues/117 就是说2014a以后的版本mex -o 选项变成了 -output 蛋疼,这有什么好改的.找了好久才 ...
- JQuery JTable根据某行的某个值来设置行的背景颜色
目录 描述 处理方法 参考 描述 某个表的数据是用JQuery的JTable插件进行展示的.现在需求是:当表中的master字段为true时,就将对应的整行的背景颜色设置为浅蓝色. 处理方法 在fie ...
- 团队作业4——第一次项目冲刺(Alpha版本)-第一篇
第一次项目冲刺——第一阶段 今天我们在宿舍开了个会,每个人都斗志昂扬的.撸起袖子加油干! 分工讨论 团队成员 任务 郭达 完成博客随笔和leangoo 刘德培 设计好数据库 石浩洋 搭建好LAM ...
- 使用 Redis的SETNX命令实现分布式锁
使用Redis的 SETNX 命令可以实现分布式锁,下文介绍其实现方法. SETNX命令简介 命令格式 SETNX key value 将 key 的值设为 value,当且仅当 key 不存在. 若 ...
- NIO Q&A(持续补充。。。。)
Q:NIO是非阻塞的.但调用的selector.select()方法会阻塞.这和NIO非阻塞岂不是矛盾了? A:非阻塞指的是 IO 事件本身不阻塞,但是获取 IO 事件的 select 方法是需要阻塞 ...
- BZOJ4524 CQOI2016伪光滑数(堆)
对于每个质数求出其作为最大质因子时最多能有几个质因子,开始时将这些ak1~akmaxk扔进堆.考虑构造方案,使得每次取出最大值后,最大质因子.质因子数均与其相同且恰好比它小的数都在堆里.类似暴搜,对于 ...
- Java-Eclipse-Jabref一条龙
Java部分: 1. 到Oracle官网下载需要版本的JDK:http://www.oracle.com/technetwork/java/javase/archive-139210.html 2. ...