背景
C++中的function和bind是为了更方便地进行函数对象的封装和调用而设计的,在SurfaceFlinger源码中也是有很多使用部分。
比如分析Vsync相关源码时候有相关回调时候
可以看到这里的mRegistration就有个参数是 std::bind,怎么这里就可以进行回调呢?
所以基于以上背景就需要学习今天的function和bind的知识才可以很轻松的分析对。
function
文档描述:
总结概况:
function是一个通用的函数对象容器,可以存储任意可调用对象(函数、函数指针、成员函数、成员变量、lambda表达式,任何function对象,比如一个类,定义了operator()),并提供了一致的接口来调用这些对象。通过function,可以将一个函数或函数对象作为参数传递给其他函数或存储在容器中,大大提高了灵活性。
template <class Ret, class... Args> class function<Ret(Args...)>;
参数解释:
Ret:代表调用function的返回结果的类型
Args:代表是参数的类型。
demo展示:
1、function存储函数指针方式
#include <iostream>
#include <functional>
int fl(int a)
{
std::cout << "a = " << a<< std::endl;
return a*2;
}
int main()
{
//直接可以接受函数指针的赋值
std::function<int(int)> f = fl;
int functionResult = f(1);
std::cout<<"function result = "<<functionResult<<std::endl;
int directResult = fl(1);
std::cout<<" fl result = "<<directResult<<std::endl; // 调用fl函数
return 0;
}
结果如下:
明显可以看到这里使用function对象调用方式和直接调用方法的方式都是一样的,结果都是2
2、存储类对象
可以看到这里的Add类对象也是可以作为一个作为一个function对象,但是这里需要注意Add类一定要进行重载operator这个方法
3、存储成员方法
代码如下:
可以看出这里可以直接边类的成员方法赋值给function,不过这里function的Args里面第一个参数必须要求传递类对象。
调用function时候第一个参数也是必须要是Add类的对象
4、lamada表达式情况
可以看出lamada表达式也是可以直接赋值给function对象
5、类成员变量情况
可以看到类成员也可以直接通过function方式获取得到正确数据。
bind
学习文档:
https://legacy.cplusplus.com/reference/functional/bind/?kw=bind
总结bind作用:
bind是将函数和其参数进行绑定的工具,可以将一个函数和部分参数绑定在一起,生成一个新的函数对象,这个新的函数对象可以像原函数一样进行调用,但会自动填充绑定的参数。
核心参数补充:
第一个参数 fn
一个function对象,方法指针,或者是类成员变量
第二个参数 args
所有参数的集合列表,要买具体的值,要么placeholders
返回值
就是一个function的对象,当调用时候调用的就是fn这个方法体和带上传递的参数
重点强调一下最后两个:
If fn is a pointer to member, the first argument expected by the returned function is an object of the class *fn is a member (or a reference to it, or a pointer to it).
大概意思是如果fn是一个类成员相关的,那么第一个参数就需要是这个类的引用或者指针。
demo代码:
// bind example
#include <iostream> // std::cout
#include <functional> // std::bind
// a function: (also works with function object: std::divides<double> my_divide;)
double my_divide (double x, double y) {return x/y;}
struct MyPair {
double a,b;
double multiply() {return a*b;}
};
int main () {
using namespace std::placeholders; // adds visibility of _1, _2, _3,...
// binding functions:
auto fn_five = std::bind (my_divide,10,2); // returns 10/2
std::cout << fn_five() << '\n'; // 5
auto fn_half = std::bind (my_divide,_1,2); // returns x/2
std::cout << fn_half(10) << '\n'; // 5
auto fn_invert = std::bind (my_divide,_2,_1); // returns y/x
std::cout << fn_invert(10,2) << '\n'; // 0.2
auto fn_rounding = std::bind<int> (my_divide,_1,_2); // returns int(x/y)
std::cout << fn_rounding(10,3) << '\n'; // 3
MyPair ten_two {10,2};
//这里fn就是类成员方法,第一个参数就要求是这个类的对象,不过这里用的是空缺std::placeholders _1
// binding members:
auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply()
//这里调用时候传递了真正的对象ten_two
std::cout << bound_member_fn(ten_two) << '\n'; // 20
//这里fn就是类成员变量,第一个参数就要求是这个类的对象
auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a
std::cout << bound_member_data() << '\n'; // 10
return 0;
}
std::placeholders _1,_2…就代表预制第1,2、、、个参数,后续真正调用function对象时候再传递进去
实战使用结合
可以看到这里的mRegistration构造方法第二个参数就是std::bind,即绑定方法,绑定是一个CallbackRepeater::callback的成员方法,而且有3个保留参数,即后续会传递进来的。bind后就变成了function类型对象。那么这里的Registration第二个构造参数是啥呢?
这里的Callback实际就是地道的function对象
using Callback =
std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime)>;
最后传递到了VSyncDispatchTimerQueueEntry中,在调用VSyncDispatchTimerQueueEntry的callback方法时候会调用上面的function类
本文章对应视频手把手教你学framework:
hal+perfetto+surfaceflinger
https://mp.weixin.qq.com/s/LbVLnu1udqExHVKxd74ILg
私聊作者+v(androidframework007)
七件套专题:
点击这里 https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw
视频:https://www.bilibili.com/video/BV1wc41117L4/