title: 6、NS中的回调
tags: NS-3,C++,Simulation,Callback,Function Pointer
slug: storywriter/upgrade_log
grammar_mindmap: true
renderNumberedHeading: true
grammar_code: true
grammar_decorate: true
grammar_mathjax: true
NS-3中的回调其实就是C 语言基本的函数指针的封装类。
- 函数行参
- 类的成员变量
回调函数是当特定事件或者满足某种条件时(时间超时)被调用,用于对该事件或者条件进行响应,是一种可扩展编程的常用手段。回调的最大好处在于扩展性强。不需要和具体的函数进行绑定,而可以在创建的时候动态决定到底调用那个函数。例如,此时我们不想再调用加法,而想调用乘法,那么可以给A对象实例绑定乘法操作,而无需改变A类的定义。
NS3中使用回调思想来处理各种协议调用或者追踪系统。
NS3中的回调(callback.cc)
NS3中对普通函数、对象的方法、类的静态方法等进行了的封装(Callback对象,其中使用C++的模板来表示对应函数的返回值和参数。其中
第一个模板参数表示函数的返回值
,其后的模板参数表示函数的参数),在src/core/module/callback.h中实现了回调最多可以添加9个参数。
NS3中的回调
普通函数的回调|(FunctorCallbackImpl)
BoundFunctorCallbackImpl
方法的回调|(MemPtrCallbackImpl)
BoundMemPtrCallbackImpl
具体的实现
MakeCallback()
MakeBoundCallback()
普通函数的回调(FunctorCallbackImpl)
- 回调的类型:Callback<对应函数的返回值类型,....>
- MakeCallback()函数来创建一个回调对象
Callback<int, int, int> callback = MakeCallback(&add);
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
#include "ns3/core-module.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("TryCallback");
class A {
public:
A(Callback<int, int, int> calculator);
void calculate();
private:
Callback<int, int, int> m_calculator;
};
A::A (Callback<int, int, int> calculator)
{
m_calculator = calculator;
}
void
A::calculate ()
{
int result = m_calculator(5, 6);
NS_LOG_UNCOND(result);
}
int add(int a, int b)
{
return a + b;
}
int
main (int argc, char *argv[])
{
A one(MakeCallback(&add));
one.calculate();
}
方法的回调(MemPtrCallbackImpl)
在C++当中,方法被封装在类或者对象当中,因此其使用方法和普通函数是不同的。因此,NS-3为方法回调专门创建了一个类型MemPtrCallbackImpl。这个类型也可以直接通过MakeCallback()函数来创建,和普通函数回调不同的是,创建方法的回调必须传入具体的对象.
- 定义回调:
Callback<Complex, Complex &> m_complexCalculator;
- 创建回调对象:
Complex first(1, 3);
Callback<Complex, Complex &> callback = MakeCallback(&Complex::add, &first);
完整程序
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
#include <iostream>
#include "ns3/core-module.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("TryCallback");
class Complex {
public:
Complex(int a, int b);
Complex add(Complex & another);
friend std::ostream & operator <<(std::ostream & out, Complex & c);
private:
int m_a;
int m_b;
};
Complex::Complex (int a, int b)
{
m_a = a;
m_b = b;
}
Complex Complex::add (Complex & another)
{
int a = m_a + another.m_a;
int b = m_b + another.m_b;
return Complex(a, b);
}
std::ostream & operator <<(std::ostream & out, Complex & c) {
out << c.m_a << " + " << c.m_b << "i";
return out;
}
class A {
public:
A(Callback<int, int, int> calculator);
A(Callback<Complex, Complex &> complexCalculator);
void calculate();
void complexCalculate(Complex b);
private:
Callback<int, int, int> m_calculator;
Callback<Complex, Complex &> m_complexCalculator;
};
A::A (Callback<int, int, int> calculator)
{
m_calculator = calculator;
}
A::A (Callback<Complex, Complex &> complexCalculator)
{
m_complexCalculator = complexCalculator;
}
void
A::complexCalculate (Complex b)
{
Complex result = m_complexCalculator(b);
std::cout << result << std::endl;
}
void
A::calculate ()
{
int result = m_calculator(5, 6);
NS_LOG_UNCOND(result);
}
int add(int a, int b)
{
return a + b;
}
int multiply(int a, int b)
{
return a * b;
}
int
main (int argc, char *argv[])
{
Complex first(1, 3);
Complex second(2, 5);
A one(MakeCallback(&Complex::add, &first));
one.complexCalculate(second);//3+8i
}
绑定参数的回调(MakeBoundCallback)
NS-3提供了一种绑定参数的回调,可以在创建回调的时候就给回调绑定好部分参数。绑定的参数,只能从第一个参数开始顺序绑定,不能跳过任何参数
对于对象的方法,由于对象本身就具有存储数据的能力,就无需绑定参数的回调这个概念。因此,NS-3当中并未提供绑定参数的对象方法的回调。
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
#include <iostream>
#include "ns3/core-module.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("TryCallback");
class A {
public:
A(Callback<int, int> calculator);
void calculate();
private:
Callback<int, int> m_calculator;
};
A::A (Callback<int, int> calculator)
{
m_calculator = calculator;
}
void
A::calculate ()
{
int result = m_calculator(6);
NS_LOG_UNCOND(result);
}
int add(int a, int b)
{
return a + b;
}
int multiply(int a, int b)
{
return a * b;
}
int
main (int argc, char *argv[])
{
A one(MakeBoundCallback(&add, 8));
one.calculate();
}
判断回调是否绑定函数(Callback::IsNull())
如果不给回调绑定函数就直接调用,程序会发生异常。
- 为保证程序的健壮性,NS3中提供了方法Callback::IsNull()来判断回调是否已经绑定函数。
- 已经给回调绑定函数,再使用完成后,想取消绑定,则可以使用NS-3提供的MakeNullCallback()方法,将回调置空。
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
#include <iostream>
#include "ns3/core-module.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("TryCallback");
class A {
public:
A();
A(Callback<int, int, int> calculator);
void calculate();
void
setCalculator (const Callback<int, int, int>& calculator)
{
m_calculator = calculator;
}
private:
Callback<int, int, int> m_calculator;
};
A::A(){}
A::A (Callback<int, int, int> calculator)
{
m_calculator = calculator;
}
void
A::calculate ()
{
if(m_calculator.IsNull()) {
NS_LOG_UNCOND("callback is not bind to any function.");
} else {
int result = m_calculator(6, 8);
NS_LOG_UNCOND(result);
}
}
int add(int a, int b)
{
return a + b;
}
int multiply(int a, int b)
{
return a * b;
}
int
main (int argc, char *argv[])
{
A one(MakeCallback(&add));
one.calculate();
one.setCalculator(MakeNullCallback<int, int, int>()); //构造空回调
one.calculate();
}
回调作为属性(CallbackValue)
NS-3专门给回调构造了一个属性值类型CallbackValue,并且实现了其访问器和检查器。
程序演示:
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
#include <iostream>
#include "ns3/core-module.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("TryCallback");
namespace ns3 {
class A : public Object{
public:
static TypeId GetTypeId (void);
A();
void calculate();
void
setCalculator (const Callback<int, int, int>& calculator)
{
m_calculator = calculator;
}
private:
Callback<int, int, int> m_calculator;
};
A::A(){}
TypeId
A::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::A")
.AddConstructor<A> ()
.SetParent<Object> ()
.AddAttribute ("calculator", "help text",
CallbackValue (),
MakeCallbackAccessor (&A::m_calculator),
MakeCallbackChecker ())
;
return tid;
}
void
A::calculate ()
{
if(m_calculator.IsNull()) {
NS_LOG_UNCOND("callback is bind to any function.");
} else {
int result = m_calculator(6, 8);
NS_LOG_UNCOND(result);
}
}
int add(int a, int b)
{
return a + b;
}
int multiply(int a, int b)
{
return a * b;
}
}
int
main (int argc, char *argv[])
{
Ptr<A> a = CreateObject<A>();//使用智能指针创建一个A类的实例
a->SetAttribute("calculator", CallbackValue(MakeCallback(&add)));//为属性calculator指定一个回调函数add
a->calculate();
}
例子中将之前A当中的有参构造函数删除,此时向A当中指定回调的任务现在由属性框架来完成。因此A必须继承Object类,并且实现GetTypeId()方法来使用属性框架。在GetTypeId()方法中,创建了一个属性叫做”calculator”,其绑定到了成员变量m_calculator上,属性值类型为CallbackValue。该属性默认回调值为空,使用了变量访问器和默认检查器。