Context 类
在 Ceph 的代码中, 随处可见通过异步回调的模式, 对这些回调的流程有充分的理解, 能够帮助我们更好的理解 Ceph
Ceph 中提到回调, 不得不说的就是 Context
类, 它是回调的顶层抽象类,其实现了回调的基本逻辑. Context
有大量的子类, 这些子类继承 Context
并实现各自的业务.
Context
有两个重要的成员函数, finish
和 complete
, 子类继承 Context 后只需要实现自己的 finish
, 然后就可以通过调用 complete
, 实现了对自身 finish
的回调.
Context
本身代码比较简单
class Context {
Context(const Context& other);
const Context& operator=(const Context& other);
protected:
virtual void finish(int r) = 0; // 派生类实现接口
virtual bool sync_finish(int r) {
return false;
}
public:
Context() {}
virtual ~Context() {}
virtual void complete(int r) { /// 调用者调用接口,函数执行结束后销毁对象
finish(r); // int r 一般用来让子类决定是否执行
delete this;
}
virtual bool sync_complete(int r) {
if (sync_finish(r)) {
delete this;
return true;
}
return false;
}
};
回调适配器
通过会回调适配器, 可以将一个普通的成员函数封装成一个可回调函数
以 C_CallbackAdapter
为例, 先来看下代码
template <typename T, void (T::*MF)(int)>
class C_CallbackAdapter : public Context {
T *obj;
public:
C_CallbackAdapter(T *obj) : obj(obj) {}
protected:
void finish(int r) override {
(obj->*MF)(r);
}
};
C_CallbackAdapter
继承自 Context
并且实现了 finish 。 其模板参数未类型 T
和 T
中的一个成员指针 MF
, 一般回调适配器这里的 T
就是要被回调的 class, 对应的成员指针 MF
也就是一个普通成员函数的指针。然后通过传入一个 T
类型的对象 obj
, 就可以在 finish
中调用到成员函数 T::MF
一般的回调适配器还会有一个对应的注册函数如 C_CallbackAdapter
的注册函数*create_context_callback(T *obj)
template <typename T, void(T::*MF)(int) = &T::complete>
Context *create_context_callback(T *obj) {
return new detail::C_CallbackAdapter<T, MF>(obj);
}
Demo
可以写个小 Demo 来方便理解毁掉的流程, 这个 Demo 实现了一个完成的回调的流程
filename context.cpp
#include <iostream>
using namespace std;
// 简化的简陋版 Context
class Context {
public:
virtual void finish(int r) = 0;
virtual void complete(int r) {
finish(r);
delete this;
}
};
// 回调适配器
template<typename T, void (T::*MF)(int)>
class C_CallbackAdapter : public Context {
T *obj;
public:
explicit C_CallbackAdapter(T *obj) : obj(obj) {};
void finish(int r) override {
(obj->*MF)(r);
}
};
// 回调适配器的注册函数
template<typename T, void(T::*MF)(int)>
Context *create_context_callback(T *obj) {
return new C_CallbackAdapter<T, MF>(obj);
}
// 被回调的类
class MyContext {
public:
void be_call(int r) {
std::cout << "this is be call " << std::endl;
}
};
int main() {
auto *test_callback = new MyContext;
Context *ctx = create_context_callback<MyContext, &MyContext::be_call>(test_callback);
int r = 0;
ctx->complete(r); // 有了 Context ctx 指针就可以将其传到各种接受 Context 类型的地方, 然后随时调用其中的 test_callback.complete() 了,这也就是费劲包装 MyContext 类型的意义所在, 然后调用 complete() 就可以执行 MyContext 中的 be_call
return 0;
}
执行
[root@centos7 context]# g++ context.cpp
[root@centos7 context]# ls
a.out main.cpp
[root@centos7 context]# ./a.out
this is be call
[root@centos7 context]#