1、绑定到基本函数
auto bound_function = std::bind(original_function, arg1, arg2, ..., placeholder);
original_function
是绑定的原始函数或者成员函数指针,也可以是一个可调用对象(lamda表达式)
arg1, arg2, ...
是要绑定的参数,可以是实际的值,也可以是占位符
(std::placeholders::_1),表示调用新函数对象时需要提供的参数位置。
2、绑定到成员函数
#include <functional>
#include <iostream>
class MyClass {
public:
void display(int value) {
std::cout << "Value: " << value << std::endl;
}
};
int main() {
MyClass obj;
auto bound_display = std::bind(&MyClass::display, &obj, std::placeholders::_1);
bound_display(42); // 输出 Value: 42
return 0;
}
&MyClass::display
获取成员函数地址,&obj
指定调用该函数的实例。
看一个例子解释用法:
TcpServer类的构造函数中定义:
acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this,
std::placeholders::_1, std::placeholders::_2));
// 有一个新的客户端的连接,acceptor会执行这一个回调
void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)//有新连接来了
{
//轮询算法,选择一个subLoop,来管理channel
EventLoop *ioLoop = threadPool_->getNextLoop();
......
}
this 指针: 在类的成员函数中,this 是一个指向当前对象的指针。它用于访问该对象的成员变量和成员函数。
std::bind: 这个函数模板允许将一个成员函数与特定的对象进行绑定,从而可以创建一个可调用对象
。std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2)
创建了一个可调用对象
,它将调用当前 TcpServer 对象(由 this 指向)的 newConnection 函数,并将 std::placeholders::_1 和 std::placeholders::_2 作为参数传递给它。
调用的时候,会传参:
newConnectionCallback_(connfd, peerAddr);
3、绑定到std::function
std::function
是一个通用的函数封装器 ,它可以存储任何可调用对象,并允许我们以后以统一的方式调用它们。我们可以将std::bind
的结果存储到std::function
中,以提高代码灵活性和可读性。
#include <functional>
#include <iostream>
int multiply(int a, int b) {
return a * b;
}
int main() {
std::function<int(int)> double_it = std::bind(multiply, 2, std::placeholders::_1);
std::cout << double_it(5) << std::endl; // 输出10
return 0;
}
看一个例子解释用法:
1、EventLoop.cc
开启事件循环,处理发生事件的channel,调用handleEvent
函数:
for (Channel *channel : activeChannels_)
{
//Poller能监听哪些channel发生事件了,然后上报给EventLoop,EventLoop通知channel处理相应的事件
channel->handleEvent(pollReturnTime_);//事先已经绑定好
}
2、Channel.cc
传入的参数是Timestamp
,判断当前channel和Tcpconnection对象是否还处于绑定状态,然后执行handleEventWithGuard
函数,再执行readCallback_
回调函数。
//fd得到poller通知后,处理事件的函数
void Channel::handleEvent(Timestamp receiveTime)
{
if (tied_) // 如果绑定
{
std::shared_ptr<void> guard = tie_.lock(); // .lock() 智能指针的提升操作 弱->强
if (guard) // 提升成功才执行,否则说明与tie_绑定的对象已经销毁,就忽略事件处理
{
handleEventWithGuard(receiveTime);
}
}else { // 没绑定
handleEventWithGuard(receiveTime);
}
}
// 根据poller通知的channel发生的具体事件,由channel负责具体调用的回调操作
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
LOG_INFO("channel handleEvent revents:%d", revents_);
// read
if (revents_ & (EPOLLIN | EPOLLPRI))
{
if (readCallback_)
{
readCallback_(receiveTime);
}
}
...
}
3、TcpConnection.cc
在TcpConnection对象创建时(构造函数中),就已经将handleRead
函数绑定到对应channel中的readCallback_
上了,使用的是move移动转发+bind绑定器+function函数封装器,相当于如下操作:
std::function<void(Timestamp)> readCallback_ = std::move(std::bind(&TcpConnection::handleRead, this, std::placeholders::_1)
调用位置为 readCallback_(receiveTime);
一个占位符,传入receiveTime,执行handleRead函数,完成读取数据的操作。
// 下面给channel设置相应的回调函数,poller给channel通知感兴趣的事件发生了,channel会回调相应的操作函数
channel_->setReadCallback(
std::bind(&TcpConnection::handleRead, this, std::placeholders::_1)
);
...
//表示fd有数据可读
void TcpConnection::handleRead(Timestamp receiveTime)
{
int savedErrno = 0;
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
if (n > 0)
{
// 已建立连接的用户,有可读事件发生了,调用用户传入的回调操作 onMessage
// shared_from_this: 获取当前TcpConnection对象的一个智能指针 TcpConnectionPtr
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
}
else if (n == 0)
{
handleClose();
}
else
{
errno = savedErrno;
LOG_ERROR("TcpConnection::handleRead error! ");
handleError();
}
}
4、Channel.h
// 不用typedef,而用using定义类型
using ReadEventCallback = std::function<void(Timestamp)>; // 只读事件回调
ReadEventCallback readCallback_;
// set是为了 设置(相当于赋值操作) 回调函数对象 提供 <对外的接口> (EventLoop、TcpConnection中会进行设置)
// cb是一个函数对象,存在值和地址,直接使用会导致资源复制,这里直使用std::move移动语义避免不必要的复制和提高性能
// 将cb标记为右值引用,在调用对象的移动构造函数或移动赋值运算符完成实际移动操作。
void setReadCallback(ReadEventCallback cb) { readCallback_ = std::move(cb); }