From 86d985d22d4dd443b72f267fe64a03bdc3ec4164 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Mon, 12 Nov 2012 11:46:24 -0800 Subject: [PATCH] restore material from callbacks manual chapter that was truncated during conversion to Sphinx --- doc/manual/source/callbacks.rst | 330 ++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) diff --git a/doc/manual/source/callbacks.rst b/doc/manual/source/callbacks.rst index 6164e86a4..d0997ccb8 100644 --- a/doc/manual/source/callbacks.rst +++ b/doc/manual/source/callbacks.rst @@ -253,3 +253,333 @@ Here is an example of the usage:: SpecificFunctor 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 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 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 ``_. + +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 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 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 (); + 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 `_ 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 file, Ptr 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 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 + 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 +``_. +It was subsequently rewritten to follow the architecture outlined in +`Modern C++ Design, Generic Programming and Design Patterns Applied, Alexandrescu, chapter 5, Generalized Functors `_. + +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.