Extending NS by adding a new basic network object usually involves working around making OTcl linkage from C++ code, since the object class in the data path should be written in C++ for efficiency reason. This section introduces C++/OTcl linkages available in NS by giving an example of creating a simple and dull agent called "MyAgent" that has no behavior of an agent (i.e. no packet creation and transmission). Figures 18 to 21 show parts of the C++ source file for "MyAgent", together which makes a complete implementation (with 3 extra header lines). Also, an OTcl script with which you can test "MyAgent" is presented at the end of this section.
Suppose that you created a new network object class in C++, say "MyAgent" that is derived from the "Agent" class, and want to make it possible to create an instance of this object in OTcl. To do this you have to define a linkage object, say "MyAgentClass", which should be derived from "TclClass". This linkage object creates an OTcl object of specified name ("Agent/MyAgentOtcl" in this example), and creates a linkage between the OTcl object and the C++ object ("MyAgent" in this example), of which the instance launching procedure is specified in the "create" member function. Figure 18 shows the "MyAgent" class definition and the linkage class definition.
Figure 18. Example C++ Network Component and The Linkage Object
When NS is first started, it executes the constructor for the static variable "class_my_agent", and thus an instance of "MyAgentClass" is created. In this process, the "Agent/MyAgentOtcl" class and its appropriate methods (member functions) are created in OTcl space. Whenever a user in OTcl space tries to create an instance of this object using the command "new Agent/MyAgentOtcl", it invokes "MyAgentClass::create" that creates an instance of "MyAgent" and returns the address. Note that creating a C++ object instance from OTcl does not mean that you can invoke member functions or access member variables of the C++ object instance from OTcl.
Suppose your new C++ object, "MyAgent", has two parameter variables, say "my_var1" and "my_var2", that you want to configure (or change) easily from OTcl using the input simulation script. To do this you should use a binding function for each of the C++ class variables you want to export. A binding function creates a new member variable of given name (first argument) in the matching OTcl object class ("Agent/MyAgentOtcl"), and create bi-directonal bindings between the OTcl class variable and the C++ variable whose address is specified as the second variable. Figure 19 shows how to make bindings for "my_var1" and "my_var2" shown in Figure 18.
Figure 19. Variable Binding Creation Example
Note that the binding functions are placed in the "MyAgent" constructor function to establish the bindings when an instance of this object is created. NS support four different binding functions for five different variable types as follows:
- bind(): real or integer variables - bind_time(): time variable - bind_bw(): bandwidth variable - bind_bool(): boolean variable |
In this way, a user designing and running a simulation using an OTcl script can change or access configurable parameters (or variable values) of network components implemented in C++. Note that whenever you export a C++ variable, it is recommended that you also set the default value for that variable in the "ns-2/tcl/lib/ns-lib.tcl" file. Otherwise, you will get a warning message when you create an instant of your new object.
In addition to exporting some of your C++ object variables, you may also want to give the control of your C++ object to OTcl. This is done by defining a "command" member function of your C++ object ("MyAgent"), which works as an OTcl command interpreter. As a matter of fact, an OTcl command defined in a "command" member function of a C++ object looks the same as a member function of the matching OTcl object to a user. Figure 20 shows an example "command" member function definition for the "MyAgent" object in Figure 18.
Figure 20. Example OTcl command interpreter
When an instance of the shadow OTcl that matches the "MyAgent" object is created in OTcl space (i.e. set myagent [new Agent/MyAgentOtcl]), and the user tries to call a member function of that object (i.e. $myagent call-my-priv-func), OTcl searches for the given member function name in the OTcl object. If the given member function name cannot be found, then it invokes the "MyAgent::command" passing the invoked OTcl member function name and arguments in argc/argv format. If there is an action defined for the invoked OTcl member function name in the "command" member function, it carries out what is asked and returns the result. If not, the "command" function for its ancestor object is recursively called until the name is found. If the name cannot be found in any of the ancestors, an error message is return to the OTcl object, and the OTcl object gives an error message to the user. In this way, an user in OTcl space can control a C++ object's behavior.
As you implement a new network object in C++, you might want to execute an OTcl command from the C++ object. Figure 21 shows the implementation of "MyPrivFunc" member function of "MyAgent" in Figure 18, which makes an OTcl interpreter print out the value in "my_var1" and "my_var2" private member variables.
Figure 21. Execute OTcl command from a C++ Object
To execute an OTcl command from C++, you should get a reference to "Tcl::instance()" that is declared as a static member variable. This offers you a couple of functions with which you can pass an OTcl command to the interpreter (the first line of "MyPrivFunc" does this). This example shows two ways to pass an OTcl command to the interpreter. For a complete list of OTcl command passing functions, refer to the NS documentation.
Until now, we examined the essential OTcl linkages available in NS using the "MyAgent" example. Assuming that running and testing this example would help the reader's understanding further, we present a procedure that helps you to compile, run and test "MyAgent".
Figure 22. Test OTcl Script and The Result