NS3-回调


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 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 calculator);

    void calculate();
private:
    Callback m_calculator;
};

A::A (Callback 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 m_complexCalculator;
  • 创建回调对象:
Complex first(1, 3);
Callback callback = MakeCallback(&Complex::add, &first);

完整程序

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */

#include 

#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 calculator);
    A(Callback complexCalculator);

    void calculate();
    void complexCalculate(Complex b);
private:
    Callback m_calculator;
    Callback m_complexCalculator;
};

A::A (Callback calculator)
{
    m_calculator = calculator;
}

A::A (Callback 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 

#include "ns3/core-module.h"

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("TryCallback");

class A {
public:
    A(Callback calculator);

    void calculate();
private:
    Callback m_calculator;
};

A::A (Callback 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 

#include "ns3/core-module.h"

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("TryCallback");

class A {
public:
    A();
    A(Callback calculator);

    void calculate();

    void
    setCalculator (const Callback& calculator)
    {
        m_calculator = calculator;
    }

private:
    Callback m_calculator;
};

A::A(){}

A::A (Callback 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()); //构造空回调
    one.calculate();
}

回调作为属性(CallbackValue)

NS-3专门给回调构造了一个属性值类型CallbackValue,并且实现了其访问器和检查器。

程序演示:

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */

#include 

#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& calculator)
    {
      m_calculator = calculator;
    }

  private:
    Callback m_calculator;
  };

  A::A(){}

    TypeId
    A::GetTypeId (void)
    {
    static TypeId tid = TypeId ("ns3::A")
      .AddConstructor ()
      .SetParent ()
      .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 = CreateObject();//使用智能指针创建一个A类的实例
    a->SetAttribute("calculator", CallbackValue(MakeCallback(&add)));//为属性calculator指定一个回调函数add
    a->calculate();
} 
 

例子中将之前A当中的有参构造函数删除,此时向A当中指定回调的任务现在由属性框架来完成。因此A必须继承Object类,并且实现GetTypeId()方法来使用属性框架。在GetTypeId()方法中,创建了一个属性叫做”calculator”,其绑定到了成员变量m_calculator上,属性值类型为CallbackValue。该属性默认回调值为空,使用了变量访问器和默认检查器。

你可能感兴趣的:(NS3-回调)