chromium的base源码阅读之Callback一

简介

本文描述下chromium使用base库中的callback,相关代码位于base库的根目录下的callback及callback相关,bind及bind相关的,本系列本人只是简单说一下自己的感受,有些存在的问题还望大家一块讨论。
相关base库timer讲解可参考 chromium的base源码阅读之Timer.
callback是chromium自己封装的回调函数,需要配合bind使用,如果你只是简单的使用,并不是想要深入了解里边的原理,这一篇希望可以帮到你

callback注释解析

一般一些优秀的源码都是在注释的时候充分的解释了这个相关的功能使用情况等,同样,我们先来看看callback.h文件的注释。另外callback的绑定目前我看来好像是只有对成员函数和静态函数(包括C语言中的函数),没有发现有对lambda表达式的绑定及其他可调用对象的绑定。

介绍

// -----------------------------------------------------------------------------
// Introduction
// -----------------------------------------------------------------------------
//
// The templated Callback class is a generalized function object. Together
// with the Bind() function in bind.h, they provide a type-safe method for
// performing partial application of functions.
//
// Partial application (or "currying") is the process of binding a subset of
// a function's arguments to produce another function that takes fewer
// arguments. This can be used to pass around a unit of delayed execution,
// much like lexical closures are used in other languages. For example, it
// is used in Chromium code to schedule tasks on different MessageLoops.
//
// A callback with no unbound input parameters (base::Callback<void(void)>)
// is called a base::Closure. Note that this is NOT the same as what other
// languages refer to as a closure -- it does not retain a reference to its
// enclosing environment.
//
// MEMORY MANAGEMENT AND PASSING
//
// The Callback objects themselves should be passed by const-reference, and
// stored by copy. They internally store their state via a refcounted class
// and thus do not need to be deleted.
//
// The reason to pass via a const-reference is to avoid unnecessary
// AddRef/Release pairs to the internal state.

首先是简单的介绍,大概的意思是说:
这个callback的模板类是实现回调函数功能并配合bind使用,提供类型安全的函数的功能。他可以被用于延迟执行的效果(不过回调函数都有这个功能),比如说被用在不同的事件循环的task(这个也是base库里的,事件循环内的执行是以task为单位的),没有bound参数的callback被称为Closure(闭包),即无参数情况,base::Callback<void(void)>,在timer中我们也有提及,base中对Closure的使用还是蛮多的。

快速上手

// BINDING A BARE FUNCTION
//
//   int Return5() { return 5; }
//   base::Callback<int(void)> func_cb = base::Bind(&Return5);
//   LOG(INFO) << func_cb.Run();  // Prints 5.
  • 绑定一个普通函数为base::Callback对象,通过对象的成员函数Run()来执行
// BINDING A CLASS METHOD
//
//   The first argument to bind is the member function to call, the second is
//   the object on which to call it.
//
//   class Ref : public base::RefCountedThreadSafe<Ref> {
//    public:
//     int Foo() { return 3; }
//     void PrintBye() { LOG(INFO) << "bye."; }
//   };
//   scoped_refptr<Ref> ref = new Ref();
//   base::Callback<void(void)> ref_cb = base::Bind(&Ref::Foo, ref);
//   LOG(INFO) << ref_cb.Run();  // Prints out 3.
//
//   By default the object must support RefCounted or you will get a compiler
//   error. If you're passing between threads, be sure it's
//   RefCountedThreadSafe! See "Advanced binding of member functions" below if
//   you don't want to use reference counting.
  • 绑定一个成员函数为base::Callback对象,首先类的对象必须是支持RefCounted的,RefCounted是用来引用计数的,例子中的
    RefCountedThreadSafe是线程安全的引用计数类,且该对象使用scoped_refptr来装,scoped_refptr是基于引用计数的智能指针,这些也都是谷歌的特有的独自开发的,至于为什么要使用智能指针,我们放到后边对比来说,如果你不想使用智能指针,下边也有可以提供给使用的。
// RUNNING A CALLBACK
//
//   Callbacks can be run with their "Run" method, which has the same
//   signature as the template argument to the callback.
//
//   void DoSomething(const base::Callback<void(int, std::string)>& callback) {
//     callback.Run(5, "hello");
//   }
//
//   Callbacks can be run more than once (they don't get deleted or marked when
//   run). However, this precludes using base::Passed (see below).
//
//   void DoSomething(const base::Callback<double(double)>& callback) {
//     double myresult = callback.Run(3.14159);
//     myresult += callback.Run(2.71828);
//   }
  • 运行callback,通过该类的Run()成员函数调用,如例子可以运行多次,除非使用Passed
// PASSING UNBOUND INPUT PARAMETERS
//
//   Unbound parameters are specified at the time a callback is Run(). They are
//   specified in the Callback template type:
//
//   void MyFunc(int i, const std::string& str) {}
//   base::Callback<void(int, const std::string&)> cb = base::Bind(&MyFunc);
//   cb.Run(23, "hello, world");
  • Unbound(我也不知道该怎么翻译)参数,是指在callback执行Run时指定的参数,例子中的Unbound参数的调用情况
// PASSING BOUND INPUT PARAMETERS
//
//   Bound parameters are specified when you create thee callback as arguments
//   to Bind(). They will be passed to the function and the Run()ner of the
//   callback doesn't see those values or even know that the function it's
//   calling.
//
//   void MyFunc(int i, const std::string& str) {}
//   base::Callback<void(void)> cb = base::Bind(&MyFunc, 23, "hello world");
//   cb.Run();
//
//   A callback with no unbound input parameters (base::Callback<void(void)>)
//   is called a base::Closure. So we could have also written:
//
//   base::Closure cb = base::Bind(&MyFunc, 23, "hello world");
//
//   When calling member functions, bound parameters just go after the object
//   pointer.
//
//   base::Closure cb = base::Bind(&MyClass::MyFunc, this, 23, "hello world");
  • bind的参数:另外一种参数叫bind参数,是在声明callback对象时通过Bind指定的,其可以伴随着callback的整个生命周期,如例子中cb其实两个参数的,通过bind后,执行Run时,unbound参数其实就是没有的,下边两个同理。

这里要说一下base::Closure,我们可以定位到他的声明处,在callback_forward.h中:

namespace base {

template <typename Sig>
class Callback;

typedef Callback<void(void)> Closure;

}  // namespace base

由上可知,他是一个参数为空,返回值为空的Callback类,所以可以通过Bind来定义Closure,同时类似其他语言的闭包。

// PARTIAL BINDING OF PARAMETERS
//
//   You can specify some parameters when you create the callback, and specify
//   the rest when you execute the callback.
//
//   void MyFunc(int i, const std::string& str) {}
//   base::Callback<void(const std::string&)> cb = base::Bind(&MyFunc, 23);
//   cb.Run("hello world");
//
//   When calling a function bound parameters are first, followed by unbound
//   parameters.

这个例子是说明的参数情况是一部分是Bind时指定,另一部分是unbound参数,即调用时指定。callback可以这样指定参数的情况,比较灵活,这个和stl的bind类似。

高级使用

以上这些是一些基本用法,下边的是比较高级的用法,所谓高级的用法,就是涉及到一些智能指针,还有结合base里边的一些生态资源。

// -----------------------------------------------------------------------------
// Quick reference for advanced binding
// -----------------------------------------------------------------------------
//
// BINDING A CLASS METHOD WITH WEAK POINTERS
//
//   base::Bind(&MyClass::Foo, GetWeakPtr());
//
//   The callback will not be issued if the object is destroyed at the time
//   it's issued. DANGER: weak pointers are not threadsafe, so don't use this
//   when passing between threads!

首先这个是包装的是成员函数,所用的对象时一个弱引用,以上意思是如果在调用时指针被销毁了则不会调用,但是这个不是线程安全的,所以不要在多线程中传递这个callback对象。

// BINDING A CLASS METHOD WITH MANUAL LIFETIME MANAGEMENT
//
//   base::Bind(&MyClass::Foo, base::Unretained(this));
//
//   This disables all lifetime management on the object. You're responsible
//   for making sure the object is alive at the time of the call. You break it,
//   you own it!

这里出现了一个新东西,即 base::Unretained,使用这个的话,就是需要我们手动来保证对象的声明周期,即我们自己要保证在callback对象执行Run的时候,这个this对象是存活的。 base::Unretained的位置在bind_helper.h中,我们看下他的源码:

template <typename T>
static inline internal::UnretainedWrapper<T> Unretained(T* o) {
  return internal::UnretainedWrapper<T>(o);
}

是对该对象进行了一下包装,返回了UnretainedWrapper对象,我们再来看下UnretainedWrapper的实现:

template <typename T>
class UnretainedWrapper {
 public:
  explicit UnretainedWrapper(T* o) : ptr_(o) {}
  T* get() const { return ptr_; }
 private:
  T* ptr_;
};

看起来也没什么特殊的,传进来参数的时候,指针做一次拷贝,提供了get函数用于获取。
我们继续往下看:

// BINDING A CLASS METHOD AND HAVING THE CALLBACK OWN THE CLASS
//
//   MyClass* myclass = new MyClass;
//   base::Bind(&MyClass::Foo, base::Owned(myclass));
//
//   The object will be deleted when the callback is destroyed, even if it's
//   not run (like if you post a task during shutdown). Potentially useful for
//   "fire and forget" cases.

这里的成员函数的对象是用base::Owned包装,大家也注意到了和上边对比使用的是现new出一个对象就直接绑定到callback上了,而上边的base::Unretained的是使用的this对象,Owned是让callback来管理对象的生命周期,等到callback在销毁的时候这个对象也会被销毁,而Unretained是我们手动来管理他的生命周期。
同样Own也是内部也是通过OwnedWrapper类来包装,我们直接看OwnedWrapper这个类的实现:

template <typename T>
class OwnedWrapper {
 public:
  explicit OwnedWrapper(T* o) : ptr_(o) {}
  ~OwnedWrapper() { delete ptr_; }
  T* get() const { return ptr_; }
  OwnedWrapper(const OwnedWrapper& other) {
    ptr_ = other.ptr_;
    other.ptr_ = NULL;
  }

 private:
  mutable T* ptr_;

简答说下,使用指针接收后,在这个类析构函数时会删除这个指针,至于为什么就会达到管理声明周期的功能,我们后边来详解。

// IGNORING RETURN VALUES
//
//   Sometimes you want to call a function that returns a value in a callback
//   that doesn't expect a return value.
//
//   int DoSomething(int arg) { cout << arg << endl; }
//   base::Callback<void<int>) cb =
//       base::Bind(base::IgnoreResult(&DoSomething));

这个是用IgnoreResult来包装函数,达到不会有返回值的效果。

bind参数

我们再来简单说下关于bind时函数的参数是其他形式时的一些情况。

// PASSING PARAMETERS OWNED BY THE CALLBACK
//
//   void Foo(int* arg) { cout << *arg << endl; }
//   int* pn = new int(1);
//   base::Closure foo_callback = base::Bind(&foo, base::Owned(pn));
//   The parameter will be deleted when the callback is destroyed, even if it's
//   not run (like if you post a task during shutdown).

如果调用的参数是一个指针形式(int *),我们可以使用Owned来包装pn的指针,这样callback在调用完析构时,正好可以销毁他,如果没有调用callback对象就析构,当然我们也可以销毁他,所以我们可以用Owned来包装调用。

// PASSING PARAMETERS AS A scoped_ptr
//
//   void TakesOwnership(scoped_ptr<Foo> arg) {}
//   scoped_ptr<Foo> f(new Foo);
//   // f becomes null during the following call.
//   base::Closure cb = base::Bind(&TakesOwnership, base::Passed(&f));

//   Ownership of the parameter will be with the callback until the it is run,
//   when ownership is passed to the callback function. This means the callback
//   can only be run once. If the callback is never run, it will delete the
//   object when it's destroyed.

当参数是一个scoped_ptr时,我们要bind一个scoped_ptr对象传递来调用时,我们用Passed来进行包装。通过Passed包装实际上是传递一个右值引用过去,所以在执行完后f就会变成空,传递的这个参数的生命周期也是callback来控制,要提一下的是,callback只能调用一次,没有执行的话,在callback销毁时也会被删除。
我们可以简单看下Pass的包装:

template <typename T>
static inline internal::PassedWrapper<T> Passed(T scoper) {
  return internal::PassedWrapper<T>(scoper.Pass());
}
template <typename T>
static inline internal::PassedWrapper<T> Passed(T* scoper) {
  return internal::PassedWrapper<T>(scoper->Pass());
}

也是通过Wrapper的类来包装,唯一不一样的是会调用对象的Pass()函数,我们这里看下scoped_ptr里关于Passed的声明,通过
MOVE_ONLY_TYPE_FOR_CPP_03来实现右值引用的传递:

template <class T, class D = base::DefaultDeleter<T> >
class scoped_ptr {
  MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)

  COMPILE_ASSERT(base::internal::IsNotRefCounted<T>::value,
                 T_is_refcounted_type_and_needs_scoped_refptr);

// MOVE_ONLY_TYPE_FOR_CPP_03的声明
#define MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \
 private: \
  struct rvalue_type { \
    explicit rvalue_type(type* object) : object(object) {} \
    type* object; \
  }; \
  type(type&); \
  void operator=(type&); \
 public: \
  operator rvalue_type() { return rvalue_type(this); } \
  type Pass() { return type(rvalue_type(this)); } \
 private:

#endif  // BASE_MOVE_H_

看到最下边传递一个右值的对象,具体我们先不说。

// PASSING PARAMETERS AS A scoped_refptr
//
//   void TakesOneRef(scoped_refptr<Foo> arg) {}
//   scoped_refptr<Foo> f(new Foo)
//   base::Closure cb = base::Bind(&TakesOneRef, f);
//
//   This should "just work." The closure will take a reference as long as it
//   is alive, and another reference will be taken for the called function.

当然这样调用也是可以的,既然是智能指针,当然也可以管理Foo对象的析构,而f对象也是可以伴随着callback使用,可以多次调用。

// PASSING PARAMETERS BY REFERENCE
//
//   void foo(int arg) { cout << arg << endl }
//   int n = 1;
//   base::Closure has_ref = base::Bind(&foo, base::ConstRef(n));
//   n = 2;
//   has_ref.Run();  // Prints "2"
//
//   Normally parameters are copied in the closure. DANGER: ConstRef stores a
//   const reference instead, referencing the original parameter. This means
//   that you must ensure the object outlives the callback!

base里边也为我们封装了常引用的包装类(base::ConstRef),平常的调用参数都是拷贝的,使用常引用时要注意原始对象要保持存活的。

总结

首先我们简单的通过Bind函数能够把我们的函数封装成CallBack对象,然后通过Run()函数传递参数调用,对于参数我们有两种,一种bound的参数,另一种是Unbound的参数,对于unbound参数比较简单,我们在Run的时候直接传递就可以调用了,但是对于bound的参数,比较复杂,最基本的就是使用scoped_refptr ref = new Ref();智能指针来封装直接调用。或者使用GetWeakPtr(),Unretained(),Owned(),Passed(),ConstRef()来进行封装,如果想要忽略返回值,可以用IgnoreResult来封装。

这里先说到这里,只是说了下callback的使用,翻译了下文档,然后也加了一些自己的理解,应该对于使用callbak的够用了,后边我们再详细分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值