Boost教程(五)bind,function和signal

bind

使用boost实现回调函数常常用的是bind,bind第一个参数可以接受函数对象、函数指针(包括成员函数),bind最多可以接受9个参数,返回一个函数对象,具有operator(),而且类型可以自动推导。对于类的成员函数,需要传入一个类的对象(或引用、指针),这样就剩8个参数可用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Demo
{
public:
int add(int a, int b) {return a+b; }
static int add_2(int a, int b) {return a+b;}
};

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

int main()
{
Demo d;
int a = 1, b=4;
// 类成员函数,d做对象,加&是函数指针
boost::function<int(int,int)> func = boost::bind(&Demo::add, d, a, b);
// 静态成员函数,不用&
boost::function<int(int,int)> func_2 = boost::bind(Demo::add_2, _1, _2);
// 普通函数
boost::function<int(int,int)> func_3 = boost::bind(&add_3, _1, _2);
// 返回函数对象,有operator ()
cout<<"add: "<<func(a,b) <<endl;
cout<<"static add: "<<func_2(a,b) <<endl;
cout<<"function add: "<<func_3(a,b) <<endl;
return 0;
}

这里的func_2,func_3中的bind用了占位符的形式,在ROS中有一个很重要的应用就是在main函数中给变量赋值,在话题订阅的回调函数中,可以获得此变量的值,用的就是bind,而且用到占位符,在回调执行时才传入值。参考:如何让回调函数与main函数互相传值

function

function可以理解为函数对象的容器,也像一种智能的函数指针,可以配合bind使用,作为bind表达式的结果,可以用于回调函数机制,而且具有很大的弹性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int add(int a, int b)
{
return a+b;
}

class Demo
{
public:
int add_2(int a, int b)
{
return a+b;
}
};

int main()
{
boost::function<int(int, int)> func, func_2;
func = add; // 函数对象
cout << sizeof(func) <<endl; // 32
if(func) // 像智能指针
cout<< func(4, 2) <<endl;
func.clear(); // 清空函数对象,这次像容器,也可以用func=0,这像指针
assert(func.empty()); // 是否为空,也是像容器的特性

// 对于类成员函数,只能经过bind
int a=3, b=1;
Demo d;
func_2 = boost::bind(&Demo::add_2, d, a, b);
cout<< func_2( a, b)<<endl;

return 0;
}

调用空的function对象会抛出bad_function_call异常,所以在使用function前最好检测有效性,就像指针一样。从function的一些成员函数来看,它确实既像容器又像指针。

两个function对象不能使用==和!=比较,这是特意的。function对象体积比一般函数指针大,速度慢一点。

signal

boost的signal和Qt的signal道理是一样的,都是观察者模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <boost/signals2.hpp>
void slot1(int n)
{
cout<<"slot1: "<<n<<endl;
}

void slot2(int n)
{
cout<<"slot2: "<<n*2<<endl;
}

int main()
{
signals2::signal<void(int)> sig; // 模板与槽函数的声明对应
sig.connect(&slot1);
sig.connect(&slot2, signals2::at_front); // slot2函数放到前面,默认是插到
// sig.disconnect_all_slots(); // 解绑所有槽函数
sig(7); // 触发信号,调用槽函数
return 0;
}

运行结果:

1
2
slot2: 14
slot1: 7