在了解NS3回调机制前先学习C/C++中的函数指针
函数指针指的是指向函数的指针(* ptr)
声明形式:
返回类型 (*函数指针名)(函数形參表)(=初始值)
C语言中的函数指针:
int (*p)(int a)=0;
int function(int a){a=0;}
a=function;// or 加上地址符&
int res=a(12)//类似 function(12)
typedef 返回类型(*函数指针类型名)(函参列表)
如
typedef int(*callback)(int a)
将函数指针名替换成了类型名即声明了一个新类型:
using namespace std;
typedef void(*FUN)(int,int); //定义函数指针类型
void min(int a,int b);
void max(int a,int b);
void min(int a,int b)
{
int minvalue=a<b?a:b;
std::cout<<"min value is "<<minvalue<<"\n";
}
void max(int a,int b)
{
int maxvalue=a>b?a:b;
std::cout<<"Max value is "<<maxvalue<<"\n";
}
//void test(void (*f)(int a,int b),int a,int b)
void test(FUN f,int a,int b)
{
f(a,b);
}
int main()
{
FUN pFun=NULL; //定义函数指针变量pFun
pFun=&min;
test(min,1,2);
test(&max,1,2);
return 0;
}
C++语言中的函数指针:
C++与C语言函数指针区别是指向调用类成员函数class A{
public:
int methord(int a);
};
int (A::*p)(int a)=0;//要加类名
p=&A::methord;
回调callback:
当程序跑起来时,一般情况下,应用程序(application program)会时常通过API调用call库里所预先备好的函数。但是有些库函数(library function)却要求应用先传给它一个函数,好在合适的时候调用call传给它的应用层函数,以完成目标任务。这个被传入的、后又被调用的函数就称为 回调函数(callback function)。
打个比方,有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是打客房电话,也可以是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头上浇盆水。这里,“叫醒”这个行为是旅馆提供的,相当于库函数,但是叫醒的方式是由旅客决定并告诉旅馆的,也就是回调函数。而旅客告诉旅馆怎么叫醒自己的动作,也就是把回调函数传入库函数的动作,称为 登记回调函数(to register a callback function)。如下图所示(图片来源:维基百科):
应用层函数A--->call 底层库函数B------>call (back)应用层函数C
可以看到,回调函数通常和应用处于同一抽象层(因为传入什么样的回调函数是在应用级别决定的)。而回调就成了一个高层调用底层,底层再 回过头来 调用高层的过程。也是其callbak得名如此的原因。
回调机制提供了非常大的灵活性,图中的库函数改称为中间函数了,这是因为回调并不仅仅用在应用和库之间。任何时候,只要想获得类似于上面情况的灵活性,都可以利用回调。跟直接调用call不同,在回调中,我们利用某种方式,把回调函数像参数一样传入中间函数(实质就是函数指针)。可以这么理解,在传入一个回调函数之前,中间函数是不完整的。换句话说,程序可以在运行时,通过登记不同的回调函数,来决定、改变中间函数的行为。这就比简单的函数调用要灵活太多,举例:
#include<iostream>
using namespace std;
typedef int (*geteven)(int a);//声明函数指针并声明为新类新geteven
int get2res(int a)
{return 2*a;}
int get4res(int a)
{return 4*a;}
int getOdd(int a,geteven fun){
return 1+fun(a);
}
int main(){
int a=1;
int res;
res=getOdd(a,get2res);//2*k+1
cout<<res<<" ";
res=getOdd(a,get4res);//4*k+1
cout<<res;
return 0;}
起始函数即中间函数调用者为main函数,中间函数为getOdd函数,回调函数有两种get2res和get4res,如上所说,中间函数getOdd函数需要起始函数传入参数函数指针才是一个完整函数,,程序可以在运行时,通过登记不同的回调函数(get2res和get4res),最后中间函数完成的功能不一样。 总而言之:调用者A提供的函数来完成功能,借指针传递参数给被调用者B,可以全然不管所比较的数据类型。被调用者B回头调用调用者的函数C,故称其为callback
NS3回调callback:
ns3主要提供Callback类API接口来为用户提供服务 ,分为两步骤:
1用给定的签名声明回调的类型
2回调实例化
Callback 与 MakeCallback是成对出现的,其类型如下
1. Callback其实类似于“指向函数的指针"Callback模板类: Callback< R, T1, T2, T3, T4, T5, T6, T7, T8, T9 >其中 R为Callback的返回类型,必选T1...T9是Callback的实参,可选,默认值为empty
2. MakeCallback
template<typename T , typename OBJ , typename R , typename T1 >
Callback< R, T1 > ns3::MakeCallback (R(T::*)(T1) mem_ptr, OBJ objPtr)
模板类MakeCallback的返回值为Callback类型
mem_ptr: class method member pointer
objPtr: class instance
返回值: a wrapper Callback Build Callbacks for class method members which takes one argument and potentially return a value.
针对静态函数
static double
CbOne(double a,double b)
{
std::out<<"a="<<a<<"b="<<b<<std::endl;
return a;
}
int main(int argc,char *argv[])
{
//返回类型 double
//第一个参数 double
//第二个参数 double
Callback<double,double,double> one; //回掉实例化,第一个参数是返回值,第二和第三是参数
one = MakeCallback(&CbOne);//讲回调one,通过API MakeCallback()与相应的函数进行匹配
NS_ASSERT(!one.IsNull());//检查一下回调one是否为空
double retone;
retone = one(10.0,20.2);//此时one的功能和CbOne的功能是一样的
}
针对类成员函数
class Mycb{
public:
int CbTwo(double a){
std::out<<"a="<<a<<std::endl;
return -5;
}
}
//返回类型 int
//参数 double
Callback<int,double> two;
Mycb cb;
//创建一个回调,并让他指向Mycb::CbTwo
two = MakeCallback(&Mycb::CbTwo,&cb);//与静态函数差别多一个参数,即实例化的cb的地址
//调用的时候也就是调用cb的CbTwo函数
NS_ASSERT(!two.IsNull());//判断回调two不为空
int retTwo;
retTwo = two(10.0);
NS_ASSERT(retTwo == -5);
return 0;
}
针对Null的回调
Two = MakeNullCallback<int,double>(); //构建空的回调
int retTwo = Two(10.2);