restore material from callbacks manual chapter that was truncated during conversion to Sphinx
authorCraig Dowell <craigdo@ee.washington.edu>
Mon, 12 Nov 2012 11:46:24 -0800
changeset 9135 5016d4c1ad81
parent 9134 7a750f032acd
child 9136 96b16f69f91b
restore material from callbacks manual chapter that was truncated during conversion to Sphinx
doc/manual/source/callbacks.rst
--- a/doc/manual/source/callbacks.rst	Sat Nov 10 19:16:38 2012 +0100
+++ b/doc/manual/source/callbacks.rst	Mon Nov 12 11:46:24 2012 -0800
@@ -253,3 +253,333 @@
     SpecificFunctor<A, int> sf(&a, &A::Hello);
     sf(5);
   }
+
+.. note:: The previous code is not real ns-3 code.  It is simplistic example
+   code used only to illustrate the concepts involved and to help you understand 
+   the system more.  Do not expect to find this code anywhere in the ns-3 tree.
+
+Notice that there are two variables defined in the class above.  The m_p 
+variable is the object pointer and m_pmi is the variable containing the 
+address of the function to execute.
+
+Notice that when ``operator()`` is called, it in turn calls the method provided
+with the object pointer using the C++ PMI syntax.
+
+To use this, one could then declare some model code that takes a generic functor
+as a parameter::
+
+  void LibraryFunction (Functor functor);
+
+The code that will talk to the model would build a specific functor and pass it to ``LibraryFunction``:: 
+
+  MyClass myClass;
+  SpecificFunctor<MyClass, int> functor (&myclass, MyClass::MyMethod);
+
+When ``LibraryFunction`` is done, it executes the callback using the 
+``operator()`` on the generic functor it was passed, and in this particular
+case, provides the integer argument::
+
+  void 
+  LibraryFunction (Functor functor)
+  {
+    // Execute the library function
+    functor(1234);
+  }
+
+Notice that ``LibraryFunction`` is completely decoupled from the specific
+type of the client.  The connection is made through the Functor polymorphism.
+
+The Callback API in |ns3| implements object-oriented callbacks using
+the functor mechanism.  This callback API, being based on C++ templates, is 
+type-safe; that is, it performs static type checks to enforce proper signature
+compatibility between callers and callees.  It is therefore more type-safe to 
+use than traditional function pointers, but the syntax may look imposing at 
+first.  This section is designed to walk you through the Callback system so 
+that you can be comfortable using it in |ns3|.
+
+Using the Callback API
+**********************
+
+The Callback API is fairly minimal, providing only two services:
+
+1. callback type declaration: a way to declare a type of callback
+with a given signature, and,
+
+2. callback instantiation: a way to instantiate a
+template-generated forwarding callback which can forward any calls
+to another C++ class member method or C++ function.
+
+This is best observed via walking through an example, based on
+``samples/main-callback.cc``.
+
+Using the Callback API with static functions
+++++++++++++++++++++++++++++++++++++++++++++
+
+Consider a function::
+
+  static double
+  CbOne (double a, double b)
+  {
+    std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;
+    return a;
+  }
+
+Consider also the following main program snippet::
+
+  int main (int argc, char *argv[])
+  {
+    // return type: double
+    // first arg type: double
+    // second arg type: double
+    Callback<double, double, double> one;
+  }
+
+This is an example of a C-style callback -- one which does not include or need
+a ``this`` pointer.  The function template ``Callback`` is essentially the
+declaration of the variable containing the pointer-to-function.  In the example
+above, we explicitly showed a pointer to a function that returned an integer and
+took a single integer as a parameter,  The ``Callback`` template function is
+a generic version of that -- it is used to declare the type of a callback.  
+
+.. note:: Readers unfamiliar with C++ templates may consult `<http://www.cplusplus.com/doc/tutorial/templates/>`_.
+
+The ``Callback`` template requires one mandatory argument (the return type 
+of the function to be assigned to this callback) and up to five optional 
+arguments, which each specify the type of the arguments (if your particular
+callback function has more than five arguments, then this can be handled
+by extending the callback implementation).
+
+So in the above example, we have a declared a callback named "one" that will
+eventually hold a function pointer.  The signature of the function that it will
+hold must return double and must support two double arguments.  If one tries 
+to pass a function whose signature does not match the declared callback, 
+a compilation error will occur.  Also, if one tries to assign to a callback
+an incompatible one, compilation will succeed but a run-time 
+NS_FATAL_ERROR will be raised.  The sample program 
+``src/core/examples/main-callback.cc`` demonstrates both of these error cases
+at the end of the ``main()`` program.
+
+Now, we need to tie together this callback instance and the actual target function
+(CbOne).  Notice above that CbOne has the same function signature types as the 
+callback-- this is important.  We can pass in any such properly-typed function 
+to this callback.  Let's look at this more closely::
+
+  static double CbOne (double a, double b) {}
+            ^           ^          ^
+            |        ---|    ------|
+            |        |       | 
+  Callback<double, double, double> one;
+
+You can only bind a function to a callback if they have the matching signature.
+The first template argument is the return type, and the additional template 
+arguments are the types of the arguments of the function signature.
+
+Now, let's bind our callback "one" to the function that matches its signature::
+
+  // build callback instance which points to cbOne function
+  one = MakeCallback (&CbOne);
+
+This call to ``MakeCallback`` is, in essence, creating one of the specialized
+functors mentioned above.  The variable declared using the ``Callback`` 
+template function is going to be playing the part of the generic functor.  The
+assignment ``one = MakeCallback (&CbOne)`` is the cast that converts the 
+specialized functor known to the callee to a generic functor known to the caller.
+
+Then, later in the program, if the callback is needed, it can be used as follows::
+
+  NS_ASSERT (!one.IsNull ());
+
+  // invoke cbOne function through callback instance
+  double retOne;
+  retOne = one (10.0, 20.0);
+
+The check for ``IsNull()`` ensures that the callback is not null -- that there 
+is a function to call behind this callback.  Then, ``one()`` executes the
+generic ``operator()`` which is really overloaded with a specific implementation
+of ``operator()`` and returns the same result as if ``CbOne()`` had been 
+called directly.
+
+Using the Callback API with member functions
+++++++++++++++++++++++++++++++++++++++++++++
+
+Generally, you will not be calling static functions but instead public member 
+functions of an object.  In this case, an extra argument is needed to the 
+MakeCallback function, to tell the system on which object the function should be 
+invoked.  Consider this example, also from main-callback.cc::
+
+  class MyCb {
+  public:
+    int CbTwo (double a) {
+        std::cout << "invoke cbTwo a=" << a << std::endl;
+        return -5;
+    }
+  };
+
+  int main ()
+  {
+    ...
+    // return type: int
+    // first arg type: double
+    Callback<int, double> two;
+    MyCb cb;
+    // build callback instance which points to MyCb::cbTwo
+    two = MakeCallback (&MyCb::CbTwo, &cb);
+    ...
+  }
+
+Here, we pass an additional object pointer to the ``MakeCallback<>`` function.
+Recall from the background section above that ``Operator()`` will use the pointer to 
+member syntax when it executes on an object::
+
+      virtual int operator() (ARG arg)
+      {
+        (*m_p.*m_pmi)(arg);
+      }
+
+And so we needed to provide the two variables (``m_p`` and ``m_pmi``) when
+we made the specific functor.  The line::
+
+    two = MakeCallback (&MyCb::CbTwo, &cb);
+
+does precisely that.  In this case, when ``two ()`` is invoked::
+
+  int result = two (1.0);
+
+will result in a call tothe ``CbTwo`` member function (method) on the object 
+pointed to by ``&cb``.   
+
+Building Null Callbacks
++++++++++++++++++++++++
+
+It is possible for callbacks to be null; hence it may be wise to
+check before using them.  There is a special construct for a null
+callback, which is preferable to simply passing "0" as an argument;
+it is the ``MakeNullCallback<>`` construct::
+
+  two = MakeNullCallback<int, double> ();
+  NS_ASSERT (two.IsNull ());
+
+Invoking a null callback is just like invoking a null function pointer: it will
+crash at runtime.
+
+Bound Callbacks
+***************
+
+A very useful extension to the functor concept is that of a Bound Callback.  
+Previously it was mentioned that closures were originally function calls 
+packaged up for later execution.  Notice that in all of the Callback 
+descriptions above, there is no way to package up any parameters for use 
+later -- when the ``Callback`` is called via ``operator()``.  All of 
+the parameters are provided by the calling function.  
+
+What if it is desired to allow the client function (the one that provides the
+callback) to provide some of the parameters?  `Alexandrescu <http://erdani.com/book/main.html>`_ calls the process of
+allowing a client to specify one of the parameters *"binding"*.  One of the 
+parameters of ``operator()`` has been bound (fixed) by the client.
+
+Some of our pcap tracing code provides a nice example of this.  There is a
+function that needs to be called whenever a packet is received.  This function
+calls an object that actually writes the packet to disk in the pcap file 
+format.  The signature of one of these functions will be::
+
+  static void DefaultSink (Ptr<PcapFileWrapper> file, Ptr<const Packet> p);
+
+The static keyword means this is a static function which does not need a
+``this`` pointer, so it will be using C-style callbacks.  We don't want the
+calling code to have to know about anything but the Packet.  What we want in
+the calling code is just a call that looks like::
+
+  m_promiscSnifferTrace (m_currentPkt);
+
+What we want to do is to *bind* the ``Ptr<PcapFileWriter> file`` to the 
+specific callback implementation when it is created and arrange for the 
+``operator()`` of the Callback to provide that parameter for free.
+
+We provide the ``MakeBoundCallback`` template function for that purpose.  It
+takes the same parameters as the ``MakeCallback`` template function but also
+takes the parameters to be bound.  In the case of the example above::
+
+    MakeBoundCallback (&DefaultSink, file);
+
+will create a specific callback implementation that knows to add in the extra
+bound arguments.  Conceptually, it extends the specific functor described above
+with one or more bound arguments::
+
+  template <typename T, typename ARG, typename BOUND_ARG>
+  class SpecificFunctor : public Functor
+   {
+   public:
+      SpecificFunctor(T* p, int (T::*_pmi)(ARG arg), BOUND_ARG boundArg)
+      {
+        m_p = p;
+        m_pmi = pmi;
+        m_boundArg = boundArg;
+      }
+
+      virtual int operator() (ARG arg)
+      {
+        (*m_p.*m_pmi)(m_boundArg, arg);
+      }
+  private:
+      void (T::*m_pmi)(ARG arg);
+      T* m_p;
+      BOUND_ARG m_boundArg;
+   };
+
+You can see that when the specific functor is created, the bound argument is saved
+in the functor / callback object itself.  When the ``operator()`` is invoked with
+the single parameter, as in::
+
+  m_promiscSnifferTrace (m_currentPkt);
+
+the implementation of ``operator()`` adds the bound parameter into the actual
+function call::
+
+  (*m_p.*m_pmi)(m_boundArg, arg);
+
+Traced Callbacks
+****************
+*Placeholder subsection*
+
+Callback locations in ns-3
+**************************
+
+Where are callbacks frequently used in |ns3|?  Here are some of the
+more visible ones to typical users:
+
+* Socket API
+* Layer-2/Layer-3 API
+* Tracing subsystem
+* API between IP and routing subsystems
+
+Implementation details
+**********************
+
+The code snippets above are simplistic and only designed to illustrate the mechanism
+itself.  The actual Callback code is quite complicated and very template-intense and
+a deep understanding of the code is not required.  If interested, expert users may
+find the following useful.
+
+The code was originally written based on the techniques described in 
+`<http://www.codeproject.com/cpp/TTLFunction.asp>`_.
+It was subsequently rewritten to follow the architecture outlined in 
+`Modern C++ Design, Generic Programming and Design Patterns Applied, Alexandrescu, chapter 5, Generalized Functors <http://www.moderncppdesign.com/book/main.html>`_.
+
+This code uses:
+
+* default template parameters to saves users from having to
+  specify empty parameters when the number of parameters
+  is smaller than the maximum supported number
+* the pimpl idiom: the Callback class is passed around by
+  value and delegates the crux of the work to its pimpl pointer.
+* two pimpl implementations which derive from CallbackImpl
+  FunctorCallbackImpl can be used with any functor-type
+  while MemPtrCallbackImpl can be used with pointers to
+  member functions.
+* a reference list implementation to implement the Callback's
+  value semantics.
+
+This code most notably departs from the Alexandrescu implementation in that it
+does not use type lists to specify and pass around the types of the callback 
+arguments. Of course, it also does not use copy-destruction semantics and 
+relies on a reference list rather than autoPtr to hold the pointer.