简介
在前面C++集群的项目里面大量应用到了绑定器来做解耦操作,那么,绑定器到底是什么呢?有什么玄妙的地方嘞?
其实也不是很玄乎,以前写Qt的时候就经常用到绑定,昨天又发现,其实我们一直在用绑定器却不自知,比如说创建线程,将函数指针与它的参数一并传入。
std::function
在这一篇博客里(C++搭建集群聊天室(八):网络层代码与业务层代码(登录注册)解耦),我写过这样的代码:
#include <functional>
···
using MsgHandler = std::function<void(const TcpConnectionPtr &conn,json &js,Timestamp time)>;
···
MsgHandler getHandle(int msgid);
···
unordered_map<int,MsgHandler> _msgHanderMap;
···
//注册消息以及对应的回调操作
ChatService::ChatService(){
_msgHanderMap.insert({LOGIN_TYPE,std::bind(&ChatService::login,this,_1,_2,_3)});
_msgHanderMap.insert({REG_TYPE,std::bind(&ChatService::reg,this,_1,_2,_3)});
}
要怎么去界定这个MsgHandler?或者换句话说,要怎么理解它?看成一个自定义数据类型?一个typename?那它到底是type了谁的name???
可调用对象
在C++中,有“可调用对象”这么个概念:
函数指针;
具有operator()成员函数的类对象(仿函数);
可以被转换为函数指针的类对象;
类成员(函数)指针。
std::function是一个可调用对象的包装器,一个类模板,可以容纳除了类成员(函数)指针之外的所用可调用对象,通过指它的模板参数,可以以统一的方式处理函数、函数对象、函数指针,并允许保存或者延迟执行。
使用方法:
#include <iostream>
#include <functional>
void func1(void)
{
std::cout << __FUNCTION__ << std::endl;
}
class Test
{
public:
static int func2(int a)
{
std::cout << __FUNCTION__ << "(" << a << ")->: ";
return a;
}
};
class Test2
{
public:
int operator()(int a)
{
std::cout << __FUNCTION__ << "(" << a << ")->: ";
return a;
}
};
int main(void)
{
//绑定一个普通函数
std::function<void(void)> fb1 = func1;
fb1();
//绑定一个静态成员函数
std::function<int(int)> fb2 = Test::func2;
std::cout << fb2(123) << std::endl;
//绑定一个仿函数
Test2 bar;
fb2 = bar;
std::cout << fb2(456) << std::endl;
return 0;
}
func1
Test::func2(123)->: 123
Test2::operator ()(456)->: 456
接下来来看看熟悉的场景,使用 function 做回调。
#include <iostream>
#include <functional>
class A
{
std::function<void()> callback;
public:
A(const std::function<void()>& f) : callback(f) {}
void notify(void)
{
callback();
}
};
class B
{
public:
void operator()(void)
{
std::cout << __FUNCTION__ << std::endl;
}
};
int main(void)
{
B b;
A a(b);
a.notify();
return 0;
}
稍微有点抽象,但也不是说很抽象啊。
B::operator ()
接下来,再看利用function做函数指针:
#include <iostream>
#include <functional>
using namespace std;
void run(int x, const std::function<void(int)>& f)
{
if (x % 2 != 0)
{
f(x);
}
}
void func(int x)
{
cout << x << " ";
}
int main(void)
{
for (int i = 0; i < 10; i++)
{
run(i, func);
}
cout << std::endl;
return 0;
}
function的部分且先讲到这里,单看一个function,其实没什么特别突出的地方,甚至写的还麻烦了。
std::bind
std::bind用来将可调用对象与起参数一起进行绑定,绑定的结果使用std::function进行保存,并在我们需要调用的时候调用。
它主要有两大作用:
将可调用对象和参数绑定成为一个仿函数;
将多元(参数个数为n,n-1)可调用对象转换成一元或者(n-1)元可调用对象,即只绑定部分对象。
来看一下用法示例:
在前面原有代码的基础上做一下函数参数的扩充:
#include <iostream>
#include <functional>
void run(int x, const std::function<void(int)>& f)
{
if (!(x & 1)) //x % 2 == 0
{
f(x);
}
}
void func1(int x)
{
std::cout << x << " ";
}
void func2(int x)
{
std::cout << x + 2 << " ";
}
int main(void)
{
auto fr = std::bind(func1, std::placeholders::_1);
for (int i = 0; i < 10; i++)
{
run(i, fr);
}
std::cout << std::endl;
return 0;
}
联想一下 pthread_create 函数,有没有种熟悉的感觉、
std::placeholders
这个呢,之前在项目博客里说过,是占位符。
通过std::placeholders占位符绑定函数参数,使得std::bind的使用非常灵活。std::placeholders决定函数占用位置取用输入参数的第几个参数。
using namespace placeholders;
···
_server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));
···
_server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));