@node ns-3 Callbacks @chapter ns-3 Callbacks Some new users to @command{ns-3} are unfamiliar with an extensively used programming idiom used throughout the code: the ``ns-3 callback''. This chapter provides some motivation on the callback, guidance on how to use it, and details on its implementation. @menu * Motivation:: * Using the Callback API:: * Callback locations in ns-3:: * Implementation details:: @end menu @node Motivation @section Motivation Consider that you have two simulation models A and B, and you wish to have them pass information between them during the simulation. One way that you can do that is that you can make A and B each explicitly knowledgable about the other, so that they can invoke methods on each other. @verbatim class A { public: void ReceiveInput ( // parameters ); ... } (in another source file:) class B { public: void ReceiveInput ( // parameters); void DoSomething (void); ... private: A* a_instance; // pointer to an A } void B::DoSomething() { // Tell a_instance that something happened a_instance->ReceiveInput ( // parameters); ... } @end verbatim This certainly works, but it has the drawback that it introduces a dependency on A and B to know about the other at compile time (this makes it harder to have independent compilation units in the simulator) and is not generalized; if in a later usage scenario, B needs to talk to a completely different C object, the source code for B needs to be changed to add a ``c_instance'' and so forth. It is easy to see that this is a brute force mechanism of communication that can lead to programming cruft in the models. This is not to say that objects should not know about one another if there is a hard dependency between them, but that often the model can be made more flexible if its interactions are less constrained at compile time. This is not an abstract problem for network simulation research, but rather it has been a source of problems in previous simulators, when researchers want to extend or modify the system to do different things (as they are apt to do in research). Consider, for example, a user who wants to add an IPsec security protocol sublayer between TCP and IP: @verbatim ------------ ----------- | TCP | | TCP | ------------ ----------- | becomes -> | ----------- ----------- | IP | | IPsec | ----------- ----------- | ----------- | IP | ----------- @end verbatim If the simulator has made assumptions, and hard coded into the code, that IP always talks to a transport protocol above, the user may be forced to hack the system to get the desired interconnections. An alternative that provides this flexibility is to use a level of indirection that is commonly known in programming as a callback. A callback function is not invoked explicitly by the caller but is rather delegated to another function that receives the callback function's address and can call it. You may be familiar with function pointers in C or C++; these can be used to implement callbacks. For more information on introductory callbacks, an online reference is: @uref{http://www.inquiry.com/techtips/cpp_pro/10min/10min0300.asp,,Declaring Function Pointers and Implementing Callbacks} and @uref{http://en.wikipedia.org/wiki/Callback_(computer_science),,Callback (computer science)-- Wikipedia}. The callback API in @command{ns-3} is designed to minimize the overall coupling between various pieces of of the simulator by making each module depend on the callback API itself rather than depend on other modules. It acts as a sort of third-party to which work is delegated and which forwards this work to the proper target module. 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 @command{ns-3}. @node Using the Callback API @section Using the Callback API The Callback API is fairly minimal, providing only two services: @itemize @bullet @item callback type declaration: a way to declare a type of callback with a given signature, and, @item 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. @end itemize This is best observed via walking through an example, based on @code{samples/main-callback.cc}. @node Using the Callback API with static functions @subsection Using the Callback API with static functions Consider a function: @verbatim static double CbOne (double a, double b) { std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl; return a; } @end verbatim Consider also the following main program snippett: @verbatim int main (int argc, char *argv[]) { // return type: double // first arg type: double // second arg type: double Callback one; } @end verbatim This class template Callback implements what is known as the Functor Design Pattern. It is used to declare the type of a callback. It contains 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 function has more than five arguments, then this can be handled by extending the callback implementation). So in the above, we have a declared a callback named "one" that will eventually hold a function pointer. 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, the compilation will fail. 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: @verbatim static double CbOne (double a, double b) {} ^ ^ ^ | ---| ------| | | | Callback one; @end verbatim 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: @verbatim // build callback instance which points to cbOne function one = MakeCallback (&CbOne); @end verbatim Then, later in the program, if the callback is to be used, it can be used as follows: @verbatim // this is not a null callback NS_ASSERT (!one.IsNull ()); // invoke cbOne function through callback instance double retOne; retOne = one (10.0, 20.0); @end verbatim The check @code{IsNull()} ensures that the callback is not null; that there is a function to call behind this callback. Then, @code{one()} returns the same result as if @code{CbOne()} had been called directly. @node Using the Callback API with member functions @subsection 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: @verbatim 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); ... } @end verbatim Here, we pass a (raw) pointer to the @code{MakeCallback<>} function, that says, when @code{two ()} is invoked, to call the @code{CbTwo} function on the object pointed to by @code{&cb}. A variation of this is used when objects are referred to by ns-3 smart pointers. The MakeCallback API takes a raw pointer, so we need to call @code{PeekPointer ()} to obtain this raw pointer. So the example above would look like: @verbatim class MyCb : public Object { 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; Ptr cb = CreateObject (); // build callback instance which points to MyCb::cbTwo two = MakeCallback (&MyCb::CbTwo, PeekPointer (cb)); ... } @end verbatim @node Building Null Callbacks @subsection 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 @code{MakeNullCallback<>} construct: @verbatim two = MakeNullCallback (); // invoking a null callback is just like // invoking a null function pointer: // it will crash at runtime. //int retTwoNull = two (20.0); NS_ASSERT (two.IsNull ()); @end verbatim @node Callback locations in ns-3 @section Callback locations in @command{ns-3} Where are callbacks frequently used in @command{ns-3}? Here are some of the more visible ones to typical users: @subsection Socket API @subsection Layer-2/Layer-3 API @subsection Tracing subsystem @subsection Routing Route Reply @node Implementation details @section Implementation details This section is advanced explanation for C++ experts interested in the implementation, and may be skipped by most users. This code was originally written based on the techniques described @uref{http://www.codeproject.com/cpp/TTLFunction.asp,,here}. It was subsequently rewritten to follow the architecture outlined in @uref{http://www.amazon.com/Modern-C\%2B\%2B-Design-Programming-Patterns/dp/0201704315/ref=pd_bbs_sr_1/102-0157303-1900156?ie=UTF8\&s=books\&qid=1187982662\&sr=1-1,,Modern C++ Design: Generic Programming and Design Patterns Applied-- Alexandrescu}, chapter 5, "Generalized Functors". This code uses: @itemize @bullet @item default template parameters to saves users from having to specify empty parameters when the number of parameters is smaller than the maximum supported number @item the pimpl idiom: the Callback class is passed around by value and delegates the crux of the work to its pimpl pointer. @item 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. @item a reference list implementation to implement the Callback's value semantics. @end itemize 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.