Python回调函数用法实例详解,回调函数

  在争辩回调函数前大家先看一下转眼二种现象。

Python回调函数用法实例详解,python回调函数实例

正文实例讲述了Python回调函数用法。分享给大家供我们参考。具体分析如下:

1、百度宏观上对回调函数的表达:

回调函数正是3个经过函数指针调用的函数。若是你把函数的指针(地址)作为参数字传送递给另四个函数,当以此指针被用为调用它所针对的函数时,大家就说那是回调函数。回调函数不是由该函数的兑现方一向调用,而是在特定的轩然大波或条件发出时由其它的1方调用的,用于对该事件或标准举办响应。

二、什么是回调:

软件模块之间连接存在着必然的接口,从调用格局上,能够把她们分成三类:同步调用、回调剂异步调用。同步调用是壹种阻塞式调用,调用方要等待对方施行完毕才回来,它是1种单向调用;回调是一种双向调用格局,也等于说,被调用方在接口被调用时也会调用对方的接口;异步调用是1体系似音信或事件的建制,可是它的调用方向正好相反,接口的劳务在收受某种音信或发生某种事件时,会积极性打招呼客户方(即调用客户方的接口)。回调弄整理异步调用的涉及格外严密,平常我们采用回调来兑现异步音信的登记,通过异步调用来贯彻音信的通报。同步调用是3者个中最轻松易行的,而回调又每每是异步调用的根底,由此,上边大家根本切磋回调机制在不相同软件框架结构中的完结。

叁、一个小例子:

#call.py 
import called 
def callback(): 
  print "in callback" 
def main(): 
  #called.test() 
  called.test_call(callback) 
  print "in call.py" 
main() 
#called.py 
''''' 
def test(): 
  print "in called.py test()" 
''' 
def test_call(p_call): 
  print "in called.py test_call()" 
  p_call() 
[email protected]:~/test/python$ python call.py 
in called.py test_call() 
in callback 
in call.py 
[email protected]:~/test/python$

互连网搜到的二个面向对象实现的事例:

当您要加盟回调(Callback)功效的时候,代码往往会偏重于回调的落成而不是主题材料自身了。1个化解办法便是兑现一个通用的底蕴类来缓慢解决回调的供给,然后再来达成您为有些事件(伊夫nt)所绑定(Binding)的措施(Method)。

代码如下:

class CallbackBase: 
  def __init__(self): 
 self.__callbackMap = {} 
 for k in (getattr(self, x) for x in dir(self)): 
   if hasattr(k, "bind_to_event"): 
 self.__callbackMap.setdefault(k.bind_to_event, []).append(k) 
   elif hasattr(k, "bind_to_event_list"): 
 for j in k.bind_to_event_list: 
   self.__callbackMap.setdefault(j, []).append(k) 
  ## staticmethod is only used to create a namespace 
  @staticmethod 
  def callback(event): 
 def f(g, ev = event): 
   g.bind_to_event = ev 
   return g 
 return f 
  @staticmethod 
  def callbacklist(eventlist): 
 def f(g, evl = eventlist): 
   g.bind_to_event_list = evl 
   return g 
 return f 
  def dispatch(self, event): 
 l = self.__callbackMap[event] 
 f = lambda *args, **kargs: \ 
   map(lambda x: x(*args, **kargs), l) 
 return f 
## Sample 
class MyClass(CallbackBase): 
  EVENT1 = 1 
  EVENT2 = 2 
  @CallbackBase.callback(EVENT1) 
  def handler1(self, param = None): 
 print "handler1 with param: %s" % str(param) 
 return None 
  @CallbackBase.callbacklist([EVENT1, EVENT2]) 
  def handler2(self, param = None): 
 print "handler2 with param: %s" % str(param) 
 return None 
  def run(self, event, param = None): 
 self.dispatch(event)(param) 
if __name__ == "__main__": 
  a = MyClass() 
  a.run(MyClass.EVENT1, 'mandarina') 
  a.run(MyClass.EVENT2, 'naranja') 

这里有四个类,它有七个事件(EVENT一和EVENT二)和两个管理函数(handler)。第3个管理函数handler一注册了EVENT壹,而第1个管理函数handler二当EVENT一只怕EVENT贰发生的时候都会试行(即注册了方方面面包车型地铁事件)。

运作函数(run)在MyClass的主循环中,它会将相应的事件派送(dispatch)出去。那(这里指dispatch函数)会回去3个函数,大家能够把装有供给传给那些函数的参数列表传给它。这些函数运行截止会再次回到二个列表(list),列表中是怀有的重回值。

莫不,使用Metaclass能够得以落成的更优雅一些呢。

盼望本文所述对我们的Python程序设计有着帮忙。

本文实例讲述了Python回调函数用法。分享给大家供大家参考。具体分析如下:
壹、百度百…

转载自:

  一、你在敲代码,上午了,于是你去炒菜,然后敲代码。

要起来应用 Boost.Function, 就要包蕴头文件 "boost/function.hpp",
也许某些带数字的版本,从 "boost/function/function0.hpp" 到 "boost/function/function10.hpp".
假让你掌握您想保留在 function 中的函数的参数数量,这样做能够让编写翻译器仅包涵需求的头文件。借使含有 "boost/function.hpp",
那么就能把别的的头文件也隐含进去。

  二、你在敲代码,下午了,于是你去炒菜,然后打了个电话给美团,点了份外卖,继续敲代码。

清楚被存函数的顶尖艺术是把它想象为3个平常的函数对象,该函数对象用于封装另二个函数(或函数对象)。这么些被存的函数的最大用处是它能够被一再调用,而无须在成立 function 时立刻利用。在表明 function时,证明中最重大的局地是函数的签字。那部分便是告诉 function 它将保存的函数或函数对象的签字和再次来到类型。大家曾经见到,有两种艺术来施行那几个宣称。这里有贰个全部的主次,程序注明了二个 boost::function ,它能够保存重回 bool (或某些可以隐式转变为 bool的门类)并收受四个参数的类函数实体,首个参数能够转移为 int,
第二个参数可以调换为 double.

  从代码的角度看,一正是我们平时的功能函数的调用,2是调用回调函数。

#include <iostream>
#include "boost/function.hpp"

bool some_func(int i,double d) 
{
  return i>d;
}

int main() 
{
  boost::function<bool (int,double)> f;
  f=&some_func;
  f(10,1.1);
}

  能够阅览回调函数一个那个主要的好处便是您的程序成为异步了。约等于您不要再调用这几个函数的时候一贯等候那些时刻的达到、事件的发生或刹车的发出(万1平昔不发出,你的程序会什么?),

当 function f 第3回创造时,它不保留任何函数。它是空的,能够在二个布尔上下文中打开测试。如果您筹划调用八个从未保留任何函数或函数对象的 function ,它将抛出2个体系 bad_function_call 的尤其。为了制止这一个难题,大家用常常的赋值语法把三个指向 some_func 的指针赋值给 f 。这导致 f 保存了到 some_func 的指针。最后,大家用参数10(三个 int) 和 1.1
(一个 double)来调用 f (用函数调用操作符)。要调用三个 function,
你不可能不提供被存函数或函数对象所期望的纯粹数据的参数。

再此时期你能够做做别的事情,可能随处转悠。当回调函数被试行时,你的次序重新获得推行的火候,此时你能够继续做供给的业务了。

回调的底子

作者们先来探视在未有 Boost.Function
以前笔者们怎么兑现二个简约的回调,然后再把代码改为运用 function,
并看看会推动什么优势。大家从叁个支撑某种轻松的回调方式的类开端,它能够向其余对新值关心的目的报告值的改换。这里的回调是1种守旧的C风格回调,即利用普通函数。那种回调用可用于象GUI调整这样的场地,它能够通报观望者用户改换了它的值,而无需对监听该音讯的客户有别的非凡的学问。

#include <iostream>
#include <vector>
#include <algorithm>
#include "boost/function.hpp"

void print_new_value(int i) 
{
  std::cout << "The value has been updated and is now " << i << '/n';
}

void interested_in_the_change(int i)
{
  std::cout << "Ah, the value has changed./n";
}

class notifier 
{
  typedef void (*function_type)(int);
  std::vector<function_type> vec_;
  int value_;
public:
  void add_observer(function_type t)
  {
    vec_.push_back(t);
  }

  void change_value(int i) 
 {
    value_=i;
    for (std::size_t i=0;i<vec_.size();++i) 
  {
      (*vec_[i])(value_);
    }
  }
};

int main() 
{
  notifier n;
  n.add_observer(&print_new_value);
  n.add_observer(&interested_in_the_change);

  n.change_value(42);
}

此间的三个函数,print_new_value 和 interested_in_the_change,
它们的函数具名都相当于 notifier 类的渴求。那个函数指针被保留在1个 vector 内,并且无论几时它的值被转移,那个函数都会在3个循环里被调用。调用这么些函数的一种语法是:

(*vec_[i])(value_);

值(value_)被传送给解引用的函数指针(即 vec_[i] 所再次来到的)。另壹种写法也是实用的,即那样:

vec_[i](value_);

那种写法看起来越来越雅观些,但更为重要的是,它还足以允许你把函数指针改变为
Boost.Function
而没有变动调用的语法。今后,工作如故好端端的,但是,唉,函数对象不能够用于那几个 notifier 类。事实上,除了函数指针以外,别的任毕建华西都不能够用,那实在是1种局限。但是,假若咱们应用
Boost.Function,它就足以干活。重写那么些 notifier 类分外轻巧。

class notifier 
{
  typedef boost::function<void(int)> function_type;
  std::vector<function_type> vec_;
  int value_;
public:
 template <typename T> void add_observer(T t)
 {
    vec_.push_back(function_type(t));
  }

  void change_value(int i) 
  {
    value_=i;
    for (std::size_t i=0;i<vec_.size();++i) 
    {
      vec_[i](value_);
    }
  }
};

首先要做的事是,把 typedef 改为表示 boost::function 而不是函数指针。在此之前,大家定义的是三个函数指针;现在,我们使用泛型方法,比一点也不慢就能够面到它的用途。接着,大家把成员函数 add_observer 的签订契约改为泛化的参数类型。大家也足以把它改为接受1个 boost::function,但那样会必要此类的用户必须也驾驭 function 的施用办法[2],而不是一味知道那几个阅览者类型的渴求就行了。应该专注到 add_observer 的那种调换并不应该是转向 function 的结果;无论怎样代码应该能够继续工作。我们把它改为泛型的;未来,不管是函数指针、函数对象,依旧 boost::function 实例都能够被传送给 add_observer,
而无须对已有用户代码实行别的更改。把元素出席到 vector 的代码有部分修改,以后亟需成立多个 boost::function<void(int)> 实例。最终,我们把调用这个函数的语法改为能够运用函数、函数对象以及 boost::function 实例[3]。那种对差异类别的好像函数的”东西”的庞大协助能够立即用于带状态的函数对象,它们能够兑现部分用函数很难做到的事务。

设若大家不明白Boost.Function,大家供给将拉长到接口上的此外交事务物都必须立时向用户解释清楚。

class knows_the_previous_value 
{
  int last_value_;
public:
  void operator()(int i) 
  {
    static bool first_time=true;
    if (first_time) {
      last_value_=i;
      std::cout <<  "This is the first change of value, so I don't know the previous one./n";
      first_time=false;
      return;
    }
    std::cout << "Previous value was " << last_value_ << '/n';
    last_value_=i;
  }
};

那个函数对象保存从前的值,并在值被改变时把旧值输出到 std::cout 。注意,当它首先次被调用时,它并不知道旧值。这些函数对象在函数中利用一个静态 bool 变量来检查那点,该变量被起头化为 true.
由于函数中的静态变量是在函数第一遍被调用时张开开首化的,所以它仅在首先次调用时被设为 true 。就算也得以在平常函数中动用静态变量来提供景况,然而大家亟须知道那么不太好,而且很难完结拾2线程安全。由此,带状态的函数对象总是优于带静态变量的平常函数。notifier 类并不关心那是还是不是函数对象,只要符合供给就可以接受。以下更新的例证示范了它怎么样运用。

int main() {
  notifier n;
  n.add_observer(&print_new_value);
  n.add_observer(&interested_in_the_change);
  n.add_observer(knows_the_previous_value());

  n.change_value(42);
  std::cout << '/n';
  n.change_value(30);
}

主要一点要注意的是,大家新扩张的几个观望者不是函数指针,而是一个 knows_the_previous_value 函数对象的实例。运营那段程序的输出如下:

The value has been updated and is now 42
Ah, the value has changed.
This is the first change of value, so I don't know the previous one.

The value has been updated and is now 30
Ah, the value has changed.
Previous value was 42

在此间最大的亮点不是放宽了对函数的渴求(或然说,增添了对函数对象的协理),而是大家能够运用带状态的目的,那是卓殊供给的。大家对 notifier 类所做的改换非常轻巧,而且用户代码不受影响。如上所示,把
Boost.Function 引进2个已有个别设计中是格外轻便的。

  回调函数是三个不被设计者直接调用,而是被其余人回过来调用的函数

类成员函数

Boost.Function
不援助参数绑定,那在每一回调用2个 function 将在调用同1个类实例的分子函数时是必要的。幸运的是,尽管那一个类实例被传送给 function 的话,大家就足以直接调用它的成员函数。这些 function 的具名必须包涵类的花色以及成员函数的签署。换言之,显式传入的类实例要作为隐式的第二个参数,this。那样就获得了3个在交付的对象上调用成员函数的函数对象。看一下以下那些类:

class some_class 
{
public:
  void do_stuff(int i) const 
  {
    std::cout << "OK. Stuff is done. " << i << '/n';
  }
};

员函数 do_stuff 要从1个 boost::function 实例里被调用。要到位那或多或少,大家须求function
接受3个 some_class实例,具名的其余一些为一个 void 重回以及多少个 int 参数。对于怎么把 some_class 实例传给
function,我们有三种选择:传值,传引用,可能传址。如何要传值,代码就应当那样写(很少以传值形式传递函数对象)

boost::function<void(some_class,int)> f;

小心,再次来到类型依旧在最初始,后跟成员函数所在的类,最终是成员函数的参数类型。它就象传递二个 this 给三个函数,该函数暗地里用类实例调用一个非成员函数。要把函数 f 配置为成员函数 do_stuff,
然后调用它,大家那样写:

f=&some_class::do_stuff;
f(some_class(),2);

一经要传引用,我们要改一下函数的签约,并传递一个 some_class 实例

boost::function<void(some_class&,int)> f;
f=&some_class::do_stuff;
some_class s;
f(s,1);

说起底,假如要传 some_class 的指针(裸指针或智能指针皆可),我们将在这样写:

boost::function<void(some_class*,int)> f;
f=&some_class::do_stuff;
some_class s;
f(&s,3);

装有这一个传递”虚拟 this“实例的方法都早就在库中提供。当然,那种技术也是有限制的:你必须显式地传递类实例;而美好上,你更乐于以此实例被绑定在函数中。乍1看,这不啻是
Boost.Function 的通病,但有别的库能够支撑参数的绑定,如 Boost.Bind 和
Boost.Lambda. 大家将要本章稍后的地点示范这么些库会给 Boost.Function
带有何便宜。

  回调是1种尤其重大的机制,首要用以实现软件的分支设计,使得差异软件模块的开拓者的工作进度能够单独出来,不受时间和空间的限定,需如若透过预定好的接口(或标准)相互符合在联合具名

带状态的函数对象

大家已经阅览,由于支持了函数对象,就足以给回调函数扩张状态。思考那样三个类,keeping_state,
它是三个带状态的函数对象。keeping_state 的实例记录3个总额,它在历次调用操作符实践时被扩充。以往,将此类的三个实例用于四个 boost::function 实例,结果有些出人意外。

#include <iostream>
#include "boost/function.hpp"

class keeping_state 
{
  int total_;
public:
  keeping_state():total_(0) {}

  int operator()(int i) 
  {
    total_+=i;
    return total_;
  }

  int total() const 
  {
    return total_;
  }
};

int main() 
{
  keeping_state ks;
  boost::function<int(int)> f1;
  f1=ks;

  boost::function<int(int)> f2;
  f2=ks;

  std::cout << "The current total is " << f1(10) << '/n';
  std::cout << "The current total is " << f2(10) << '/n';
  std::cout << "After adding 10 two times, the total is " 
    << ks.total() << '/n';
}

写完那段代码并随之实行它,技术员恐怕希望保存在 ks 的总量是20,但不是;事实上,总和为0。以下是那段程序的运维结果

The current total is 10
The current total is 10
After adding 10 two times, the total is 0

由来是每2个 function 实例(f1 和 f2)都饱含二个 ks 的正片,那多少个实例获得的总和都以拾,但 ks 未有生成。那也许是也说不定不是你想要的,可是切记,boost::function 的缺省级银行为是复制它要调用的函数对象,那一点很主要。要是那形成不科学的语义,或然只要有些函数对象的复制代价太高,你就非得把函数对象包装在 boost::reference_wrapper 中,那样 boost::function 的复制就能够是二个 boost::reference_wrapper 的正片,它正好持有一个到原始函数对象的引用。你不要直接动用 boost::reference_wrapper ,你能够采纳另三个助手函数,ref 和 cref
这两函数重临一个具有到某一定项目标引用或 const 引用的 reference_wrapper。在前例中,要获得大家想要的语义,固然用同三个 keeping_state 实例,大家就供给把代码修改如下:

int main() {
  keeping_state ks;
  boost::function<int(int)> f1;
  f1=boost::ref(ks);

  boost::function<int(int)> f2;
  f2=boost::ref(ks);

  std::cout << "The current total is " << f1(10) << '/n';
  std::cout << "The current total is " << f2(10) << '/n';
  std::cout << "After adding 10 two times, the total is " 
    << ks.total() << '/n';
}

boost::ref 的用途是打招呼 boost::function,大家想保留1个到函数对象的引用,而不是二个正片。运转那个顺序有以下输出:

The current total is 10
The current total is 20
After adding 10 two times, the total is 20

那多亏大家想要的结果。使用 boost::ref 和 boost::cref 的分歧之处就象引用与 const 引用的差距,对于后者,你只好调用在那之中的常量成员函数。以下例子使用三个名称叫 something_else 的函数对象,它有三个 const 的调用操作符。

class something_else 
{
public:
  void operator()() const 
  {
    std::cout << "This works with boost::cref/n";
  }
};

对此这几个函数对象,大家得以运用 boost::ref 或 boost::cref.

something_else s;
boost::function0<void> f1;
f1=boost::ref(s);
f1();
boost::function0<void> f2;
f2=boost::cref(s);
f2();

万一我们改造了 something_else 的达成,使其函数为非const,
则只有 boost::ref 能够利用,而 boost::cref 将形成二个编写翻译期错误。

class something_else 
{
public:
  void operator()() 
  {
    std::cout << 
      "This works only with boost::ref, or copies/n";
  }
};

something_else s;
boost::function0<void> f1;
f1=boost::ref(s); // This still works
f1(); 
boost::function0<void> f2;
f2=boost::cref(s); // This doesn't work; 
                   // the function call operator is not const
f2();

设若一个 function 包涵一个被 boost::reference_wrapper 所包装的函数对象,那么复制构造函数与赋值操作就能够复制该引用,即 function 的正片将引向原先的函数对象。

int main()
 {
  keeping_state ks;
  boost::function1<int,int> f1;  // 译注:原文为boost::function<int,int> f1,有误
  f1=boost::ref(ks);

  boost::function1<int,int> f2(f1);  // 译注:原文为boost::function<int,int> f2(f1),有误 
  boost::function1<short,short> f3;  // 译注:原文为boost::function<short,short> f3,有误 
  f3=f1;

  std::cout << "The current total is " << f1(10) << '/n';
  std::cout << "The current total is " << f2(10) << '/n';
  std::cout << "The current total is " << f3(10) << '/n';
  std::cout << "After adding 10 three times, the total is " 
    << ks.total() << '/n';
}

那无差距使用 boost::ref 并把函数对象 ks 赋给每三个 function
实例。给回调函数扩充状态,可以发挥巨大的才干,那也多亏利用
Boost.Function 与使用函数对象比较有所的不行出色的独到之处。

  怎样能够完结回调机制:

与 Boost.Function 一同利用 Boost.Bind  

当我们把 Boost.Function
与有个别帮忙参数绑定的库结合起来使用时,事情变得特别风趣。Boost.Bind
为一般函数、成员函数以及成员变量提供参数绑定。那非凡适合于
Boost.Function,
大家平常供给那类绑定,由于大家接纳的类自己并不是函数对象。那么,大家用
Boost.Bind 把它们转换为函数对象,然后大家得以用 Boost.Function
来保存它们并稍后调用。在将图形用户分界面(GUIs)与什么响应用户的操作举办分离时,差不离总是要选取某种回调方法。假诺那种回调机制是遵照函数指针的,就很难防止对能够应用回调的类别的一些限制,也就充实了分界面表现与事务逻辑之间的耦合风险。通过选择Boost.Function,大家可避防止那个专门的工作,并且当与某些支持参数绑定的库结合使用时,大家能够一举成功地把上下文提必要调用的函数。那是本库最常见的用途之1,把职业逻辑即从表示层分离出来。

以下例子包蕴1个格局级的磁带录音机,定义如下:

class tape_recorder 
{
public:
  void play() 
  {
    std::cout << "Since my baby left me.../n";
  }

  void stop()
  {
    std::cout << "OK, taking a break/n";
  }

  void forward() 
  {
    std::cout << "whizzz/n";
  }

  void rewind() 
  {
    std::cout << "zzzihw/n";
  }

  void record(const std::string& sound)
  {
    std::cout << "Recorded: " << sound << '/n';
  }
};

本条磁带录音机可以从1个GUI进行支配,大概也说不定从八个剧本客户端实行调整,或许从其他源举办调控,这象征大家不想把那么些函数的实行与它们的得以落成耦合起来。建设构造这种分离的三个常用的章程是,用特别的对象承担推行命令,而让客户对命令怎样实行毫无所知。那也被称为命令格局(Command pattern),并且在它那些有效。那种情势的一定达成中的二个标题是,须求为种种命令创设单独的类。以下片断示范了它看起来是个怎么样样子:

class command_base 
{
public:
  virtual bool enabled() const=0;
  virtual void execute()=0;

  virtual ~command_base() {}
};

class play_command : public command_base 
{
  tape_recorder* p_;
public:
  play_command(tape_recorder* p):p_(p) {}

  bool enabled() const 
  {
    return true;
  }

  void execute() 
  {
    p_->play();
  }
};

class stop_command : public command_base 
{
  tape_recorder* p_;
public:
  stop_command(tape_recorder* p):p_(p) {}

  bool enabled() const 
  {
    return true;
  }

  void execute() 
  {
    p_->stop();
  }
};

那并不是多个不胜吸引的方案,因为它使得代码膨胀,有无数轻便易行的命令类,而它们只是简短地承担调用三个对象的单个成员函数。有时候,那是必备的,因为那几个命令或者必要贯彻职业逻辑和调用函数,但普通它只是由于大家所运用的工具备所限制而已。这么些命令类可以如此使用:

int main() {
  tape_recorder tr;

  // 使用命令模式
  command_base* pPlay=new play_command(&tr);
  command_base* pStop=new stop_command(&tr);

  // 在按下某个按钮时调用
  pPlay->execute();
  pStop->execute();

  delete pPlay;
  delete pStop;
}

前几日,不用再成立额外的切切实实的命令类,借使大家兑现的吩咐都是调用3个再次来到 void 且未有参数(先权且忽略函数
record,
它含有3个参数)的积极分子函数的话,大家得以来点泛化。不用再创立一组具体的下令,大家能够在类中保留贰个针对性精确成员函数的指针。那是迈向精确方向(就算随时了少数功能)的一大步,就象那样:

class tape_recorder_command : public command_base 
{
  void (tape_recorder::*func_)(); 
  tape_recorder* p_;
public:

  tape_recorder_command(
    tape_recorder* p,
    void (tape_recorder::*func)()) : p_(p),func_(func) {}

  bool enabled() const 
  {
    return true;
  }

  void execute() 
  {
    (p_->*func_)();
  }
};

以此命令形式的贯彻要大多了,因为它无需咱们再次创下立一组落成一样事情的单身的类。这里的例外在于我们保留了3个 tape_recorder 成员函数指针在 func_ 中,它要在构造函数中提供。命令的奉行部分也许并不是你要表现给你的爱人看的东西,因为成员指针操作符对于某个人的话恐怕还不太熟习。但是,那可以被看为二个低层的兑现细节,所以还算好。有了这么些类,大家能够展开泛化管理,不再必要贯彻独立的命令类。

  tape_recorder tr;

  // 使用改进的命令模式
  command_base* pPlay=
    new tape_recorder_command(&tr,&tape_recorder::play);
  command_base* pStop=
    new tape_recorder_command(&tr,&tape_recorder::stop);

  // 从一个GUI或一个脚本客户端进行调用
  pPlay->execute();
  pStop->execute();

  delete pPlay;
  delete pStop;
}

您大概还从未精通,我们早就在上马兑现1个简练的 boost::function 版本,它早已足以完结我们想要的。不要再一次发明轮子,让我们首要关心手边的劳作:分离调用与贯彻。以下是2个全新落成的 command 类,它更易于编写、维护以及明白。

class command {
  boost::function<void()> f_;
public:
  command() {}
  command(boost::function<void()> f):f_(f) {}

  void execute() {
    if (f_) {
      f_();
    }
  }

  template <typename Func> void set_function(Func f) {
    f_=f;
  }

  bool enabled() const {
    return f_;
  }
};

经过应用
Boost.Function,我们能够立刻从同时相称函数和函数对象——包含由绑定器生成的函数对象——的得心应手之中受益。那一个 command 类把函数保存在三个回去 void 且不接受参数的 boost::function 中。为了让那么些类越来越灵活,大家提供了在运营期修改函数对象的措施,使用贰个泛型的积极分子函数,set_function.

template <typename Func> void set_function(Func f) 
{
  f_=f;
}

通过使用泛型方法,任何函数、函数对象,也许绑定器都约等于大家的 command 类。大家也能够挑选把 boost:: function作为参数,并动用 function 的转型构造函数来落成平等的效果。那几个 command 类十一分通用,我们得以把它用于大家的 tape_recorder 类大概其余地点。与前方的应用二个基类与多少个有血有肉派生类(在这里大家利用指针来完成多态的表现)的措施比较,还有1个非凡的长处正是,它更易于管理生存期难点,大家不再须求删除命令对象,它们能够按值传递和保存。大家在布尔上下文中使用 function f_ 来测试命令是或不是可用。假诺函数不包罗3个对象,即一个函数或函数对象,它将回来 false,
那代表我们不可能调用它。那些测试在 execute 的落到实处中开展。以下是利用大家那么些新类的三个例证:

int main() {
  tape_recorder tr;

  command play(boost::bind(&tape_recorder::play,&tr));
  command stop(boost::bind(&tape_recorder::stop,&tr));
  command forward(boost::bind(&tape_recorder::stop,&tr));
  command rewind(boost::bind(&tape_recorder::rewind,&tr));
  command record;

  // 从某些GUI控制中调用...
  if (play.enabled()) {
    play.execute();
  }

  // 从某些脚本客户端调用...
  stop.execute();

  // Some inspired songwriter has passed some lyrics
  std::string s="What a beautiful morning...";
  record.set_function(
    boost::bind(&tape_recorder::record,&tr,s));
  record.execute();
}

为了创制1个实际的下令,大家使用 Boost.Bind
来创立函数对象,当通过这一个目的的调用操作符进行调用时,就能够调用正确的 tape_recorder 成员函数。那几个函数对象是自完备的;它们无参函数对象,即它们得以一向调用,无须传入参数,那多亏 boost::function<void()> 所表示的。换言之,以下代码片断成立了二个函数对象,它在安顿好的 tape_recorder 实例上调用成员函数
play 。

boost::bind(&tape_recorder::play,&tr)

平凡,大家不可能保存 bind 所重临的函数对象,但鉴于 Boost.Function
包容于任何函数对象,所以它可以。

boost::function<void()> f(boost::bind(&tape_recorder::play,&tr));

专注,这些类也支撑调用 record,
它含有贰个连串为 const std::string& 的参数,那是出于成员函数 set_function.
因为那么些函数对象必须是无参的,所以大家供给绑定上下文以便 record 依然能够拿走它的参数。当然,那是绑定器的干活。由此,在调用 record 以前,我们创造2个涵盖被录音的字符串的函数对象。

std::string s="What a beautiful morning...";
record.set_function(boost::bind(&tape_recorder::record,&tr,s));

施行那些保存在 record 的函数对象,将要 tape_recorder 实例 tr 上执行 tape_recorder::record,并传播字符串。有了
Boost.Function 和 Boost.Bind,
就足以兑现解耦,让调用代码对于被调用代码一窍不通。以那种格局结合使用那多少个库相当实用。你曾经在这些 command 类中看看了,以往大家该清理一下了。由于
Boost.Function 的一花独放功用,你所需的只是以下代码:

typedef boost::function<void()> command;
1 void func (void (*p)(void *),void * arg);

与 Boost.Function 一齐使用 Boost.Lambda

与 Boost.Function 兼容于由 Boost.Bind 成立的函数对象同样,它也协助由
Boost.Lambda 创制的函数对象。你用 拉姆da
库创设的别样函数对象都也就是相应的 boost::function.
我们在前一节壹度探讨了基于绑定的1部分剧情,使用 Boost.Lambda
的显要差别之处是它能做得越来越多。我们可以随意地成立一些小的、无名氏的函数,并把它们保存在 boost::function 实例中以用于后续的调用。大家曾经在前一章中研讨了
lambda
表达式,在那一章的具有例子中所创造的函数对象都得以保留在贰个 function 实例中。function 与创制函数对象的库的3结合使用会非常有力。

   例子:

代价的思量

有一句谚语说,世界上从不无偿的午饭,对于 Boost.Function
来讲也是这般。与利用函数指针相比较,使用 Boost.Function
也有一对毛病,尤其是目的大小的扩大。显明,叁个函数指针只占用3个函数指针的空中尺寸(那自然了!),而三个 boost::function实例占的上空有3倍大。假设急需大量的回调函数,那可能会成为1个主题材料。函数指针在调用时的功能也稍高级中学一年级些,因为函数指针是被直接调用的,而
Boost.Function
恐怕要求运用一回函数指针的调用。最终,恐怕在一些要求与C库保持后向包容的情景下,只好动用函数指针。

就算 Boost.Function
恐怕存在那么些弱点,可是平时它们都不是何许实际难题。额外增加的高低不大,而且(恐怕存在的)额外的函数指针调用所拉动的代价与真正实践对象函数所消费的光阴比较平常都以老大小的。须要利用函数而不可能利用
Boost.Function
的场所13分罕见。使用这些库所推动的赫赫优点及灵活性明显凌驾那几个代价。

 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 void my1(void * arg)
 5 {
 6     char * str = (char *)arg;
 7     printf("%s\n",str);
 8 }
 9 
10 void my2(void * arg)
11 {
12     char * str = (char *)arg;
13     printf("%d\n",strlen(str));
14 }
15 
16 void func(void (*f)(void *), void *p)
17 {
18     f(p);
19 }
20 
21 int main(int argc, char const *argv[])
22 {
23     
24     char * msg = "hello";
25     func(my1,(void *)msg);
26     func(my2,(void *)msg);
27 
28     return 0;
29 }

幕后的底细

最少领悟一下以此库怎么着行事的基础知识是相当班值日得的。大家来看一下保存并调用多少个函数指针、三个分子函数指针和3个函数对象那三种处境。那三种状态是例外的。要确实看到
Boost.Function
怎么着职业,唯有看源代码——可是我们的做法有些差别,大家试着搞精晓那些分裂的版本究竟在拍卖办法上有个别什么两样。大家也有1个不一供给的类,即当调用3个成员函数时,必须传递一个实例的指针给 function1 (那是大家的类的名字)的构造函数。function1 援救唯有八个参数的函数。与
Boost.Function
比较3个比较宽松的投条件是,即使是对此成员函数,也只须求提供重返类型和参数类型。那几个供给的一向结果正是,构造函数必须被盛传3个类的实例用于成员函数的调用(类型能够自动测算)。

大家就要接纳的章程是,创造三个泛型基类,它表明了一个虚构的调用操作符函数;然后,从那一个基类派生五个类,分别支持二种分裂款型的函数调用。那些类担负所有的做事,而另一个类,function1,
依靠其构造函数的参数来调节实例化哪贰个具体类。以下是调用器的基类,invoker_base.

template <typename R, typename Arg> class invoker_base
 {
public:
  virtual R operator()(Arg arg)=0;
};

随后,大家伊始定义 function_ptr_invoker,
它是三个有血有肉调用器,公有派生自 invoker_base.
它的目标是调用普通函数。这些类也接受七个档期的顺序,即再次回到类型和参数类型,它们被用于构造函数,构造函数接受三个函数指针作为参数。

template <typename R, typename Arg> class function_ptr_invoker 
  : public invoker_base<R,Arg> {
  R (*func_)(Arg);
public:
  function_ptr_invoker(R (*func)(Arg)):func_(func) {}

  R operator()(Arg arg) {
    return (func_)(arg);
  }
};

那个类模板可用以调用大43个收受2个参数的一般性函数。调用操作符简单地以给定的参数调用保存在 func_ 中的函数。请小心(的确有点出乎意料)声Bellamy(Bellamy)个保留函数指针的变量的那行代码。

R (*func_)(Arg);

您也足以用2个 typedef 来让它好读一些。

typedef R (*FunctionT)(Arg);
FunctionT func_;

随之,大家供给三个方可拍卖成员函数调用的类模板。记住,它须要在构造时交由三个类实例的指针,那点与
Boost.Function
的做法不平等。这样能够节省大家的打字,因为是编写翻译器而不是技术员来演绎那个类。

template <typename R, typename Arg, typename T> 
class member_ptr_invoker : 
  public invoker_base<R,Arg> {
  R (T::*func_)(Arg);
  T* t_;
public:
  member_ptr_invoker(R (T::*func)(Arg),T* t)
    :func_(func),t_(t) {}

  R operator()(Arg arg) {
    return (t_->*func_)(arg);
  }
};

那个类模板与常见函数指针的十一分版本很相似。它与前三个版本的不一致在于,构造函数保存了三个分子函数指针与3个目的指针,而调用操作符则在该对象(t_)上调用该成员函数(func_)。

说起底,大家须要二个优良函数对象的本子。这是持有落成中最轻巧的3个,至少在大家的格局中是这么。通过应用单个模板参数,我们只标明项目 T 必须是1个真正的函数对象,因为我们想要调用它。说得够多了。

template <typename R, typename Arg, typename T> 
class function_object_invoker : 
  public invoker_base<R,Arg> {
  T t_;
public:
  function_object_invoker(T t):t_(t) {}

  R operator()(Arg arg) {
    return t_(arg);
  }
};

前天大家早已有了那个适用的积木,剩下来的正是把它们放在一块儿组成大家的和煦的 boost::function,
即 function1 类。我们想要1种情势来开掘要实例化哪1个调用器。然后大家能够把它存入1个 invoker_base 指针。这里的窃门便是,提供一些构造函数,它们有力量去反省对于给出的参数,哪类调用器是正确的。那只是是重载而已,用了一丢丢手段,包蕴泛化四个构造函数。

template <typename R, typename Arg> class function1 {
  invoker_base<R,Arg>* invoker_;
public:
  function1(R (*func)(Arg)) : 
  invoker_(new function_ptr_invoker<R,Arg>(func)) {}

  template <typename T> function1(R (T::*func)(Arg),T* p) : 
    invoker_(new member_ptr_invoker<R,Arg,T>(func,p)) {}

  template <typename T> function1(T t) : 
    invoker_(new function_object_invoker<R,Arg,T>(t)) {}

  R operator()(Arg arg) {
    return (*invoker_)(arg);
  }

  ~function1() {
    delete invoker_;
  }
};

如你所见,那当中最难的1部分是天经地义地定义出推导系统以支撑函数指针、类成员函数以及函数对象。无论选取何种设计来兑现那类功用的库,那都以必须的。最终,给出一些例子来测试大家以此方案。

bool some_function(const std::string& s) {
  std::cout << s << " This is really neat/n";
  return true;
}

class some_class {
public:
  bool some_function(const std::string& s) {
    std::cout << s << " This is also quite nice/n";
    return true;
  }
};

class some_function_object {
public:
  bool operator()(const std::string& s) {
    std::cout << s << 
      " This should work, too, in a flexible solution/n";
    return true;
  }
};

我们的 function1 类尚可以下有所函数。

int main() {
  function1<bool,const std::string&> f1(&some_function);
  f1(std::string("Hello"));

  some_class s;
  function1<bool,const std::string&> 
    f2(&some_class::some_function,&s);

  f2(std::string("Hello"));

  function1<bool,const std::string&>
    f3(boost::bind(&some_class::some_function,&s,_1));

  f3(std::string("Hello"));

  some_function_object fso;
  function1<bool,const std::string&> 
    f4(fso);
  f4(std::string("Hello"));
}

它也得以应用象 Boost.Bind 和 Boost.Lambda 那样的 binder
库所重临的函数对象。我们的类与 Boost.Function
中的类相比较要简明多了,不过也早就得以看出创设和平运动用那样2个库的难点以及有关化解办法。知道一点关于3个库是怎么样兑现的事体,对于有效应用那么些库是那多少个管用的。

 

 

  运营结果: 

1 hello
2 5

 

  大家并不曾直接调用my一和my二那三个函数,而是经过func那一个中介来调用他们。

  当然,回调函数也是能够带回重回值的。

1 void * func (void * (*p)(void *),void * arg);

  例子:

 1 #include <stdio.h>
 2 #include <string.h>
 3 
 4 void * my1(void * arg)
 5 {
 6     char * str = (char *)arg;
 7     printf("%s\n",str);
 8     return (void *)"my1";
 9 }
10 
11 void * my2(void * arg)
12 {
13     char * str = (char *)arg;
14     printf("%d\n",strlen(str));
15     return (void *)"my2";
16 }
17 
18 void * func(void *(*f)(void *), void *p)
19 {
20     return f(p);
21 }
22 
23 int main(int argc, char const *argv[])
24 {
25     
26     char * msg = "hello";
27     printf("%s\n",func(my1,(void *)msg));
28     printf("%s\n",func(my2,(void *)msg));
29 
30     return 0;
31 }

  运营结果:

1 hello
2 my1
3 5
4 my2

   那二种模型都有个联合的风味,含有两类参数,1类是函数指针,另一类是传给回调函数的参数。个中函数指针是必须掌握的,唯有知道你想要回调的函数本领够调用它。假若回调函数无需参数则能够流传一个NULL。

 

  几时利用回调函数

  不鲜明该职务几时能触发时

 

 

 

 (侵删)

接待我们共同斟酌

参考