.\" Man page generated from reStructuredText. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "NS-3-MANUAL" "1" "1649059818" "ns-3.35" "ns-3 project" .SH NAME ns-3-manual \- ns-3 Manual .sp This is the \fIns\-3 Manual\fP\&. Primary documentation for the ns\-3 project is available in five forms: .INDENT 0.0 .IP \(bu 2 \fI\%ns\-3 Doxygen\fP: Documentation of the public APIs of the simulator .IP \(bu 2 Tutorial, Manual \fI(this document)\fP, and Model Library for the \fI\%latest release\fP and \fI\%development tree\fP .IP \(bu 2 \fI\%ns\-3 wiki\fP .UNINDENT .sp This document is written in \fI\%reStructuredText\fP for \fI\%Sphinx\fP and is maintained in the \fBdoc/manual\fP directory of ns\-3’s source code. .SH ORGANIZATION .sp This chapter describes the overall \fIns\-3\fP software organization and the corresponding organization of this manual. .sp \fIns\-3\fP is a discrete\-event network simulator in which the simulation core and models are implemented in C++. \fIns\-3\fP is built as a library which may be statically or dynamically linked to a C++ main program that defines the simulation topology and starts the simulator. \fIns\-3\fP also exports nearly all of its API to Python, allowing Python programs to import an “ns3” module in much the same way as the \fIns\-3\fP library is linked by executables in C++. .INDENT 0.0 .INDENT 2.5 [image] Software organization of \fIns\-3\fP.UNINDENT .UNINDENT .sp The source code for \fIns\-3\fP is mostly organized in the \fBsrc\fP directory and can be described by the diagram in \fI\%Software organization of ns\-3\fP\&. We will work our way from the bottom up; in general, modules only have dependencies on modules beneath them in the figure. .sp We first describe the core of the simulator; those components that are common across all protocol, hardware, and environmental models. The simulation core is implemented in \fBsrc/core\fP\&. Packets are fundamental objects in a network simulator and are implemented in \fBsrc/network\fP\&. These two simulation modules by themselves are intended to comprise a generic simulation core that can be used by different kinds of networks, not just Internet\-based networks. The above modules of \fIns\-3\fP are independent of specific network and device models, which are covered in subsequent parts of this manual. .sp In addition to the above \fIns\-3\fP core, we introduce, also in the initial portion of the manual, two other modules that supplement the core C++\-based API. \fIns\-3\fP programs may access all of the API directly or may make use of a so\-called \fIhelper API\fP that provides convenient wrappers or encapsulation of low\-level API calls. The fact that \fIns\-3\fP programs can be written to two APIs (or a combination thereof) is a fundamental aspect of the simulator. We also describe how Python is supported in \fIns\-3\fP before moving onto specific models of relevance to network simulation. .sp The remainder of the manual is focused on documenting the models and supporting capabilities. The next part focuses on two fundamental objects in \fIns\-3\fP: the \fBNode\fP and \fBNetDevice\fP\&. Two special NetDevice types are designed to support network emulation use cases, and emulation is described next. The following chapter is devoted to Internet\-related models, including the sockets API used by Internet applications. The next chapter covers applications, and the following chapter describes additional support for simulation, such as animators and statistics. .sp The project maintains a separate manual devoted to testing and validation of \fIns\-3\fP code (see the \fI\%ns\-3 Testing and Validation manual\fP). .SH WORKING WITH GIT AS A USER .sp The ns\-3 project used Mercurial in the past as its source code control system, but it has moved to Git in December 2018. Git is a VCS like Mercurial, Subversion or CVS, and it is used to maintain many open\-source (and closed\-source) projects. While git and mercurial have a lot of common properties, if you are new to git you should read first an introduction to it. The most up\-to\-date guide is the Git Book, at \fI\%https://git\-scm.com/book/en/v2/Getting\-Started\-Git\-Basics\fP\&. .sp The ns\-3 project is officially hosted on GitLab.com at \fI\%https://gitlab.com/nsnam/\fP\&. For convenience and historical reasons, ns\-3\-dev mirrors are currently posted on Bitbucket.com and GitHub.com, and kept in sync with the official repository periodically via cron jobs. We recommend that users who have been working from one of these mirrors repoint their remotes so that they pull origin or upstream from GitLab.com (see below explanation about how to configure remotes). .sp This section of the manual provides common tips for both users and maintainers. Since the first part is shared, in this manual section we will start with a personal repository and then explain what to do in some typical cases. ns\-3 users often combine ns\-3\-dev with other repositories (pybindgen, netanim, apps from the app store). This manual chapter does not cover this use case; it only focuses on the single ns\-3\-dev repository. See other project documentation such as the ns\-3 tutorial for descriptions on bundled releases distributed as source archives, or on the bake build tool for managing multiple repositories. The guidelines listed below also largely pertain to the user who is using (and cloning) bake from the GitLab.com repository. .SS ns\-3’s Git workflow in a nutshell .sp Experienced git users will not necessarily need instruction on how to set up personal repositories (below). However, they should be aware of the project’s workflow: .INDENT 0.0 .IP \(bu 2 The main repository’s \fBmaster\fP branch is the main development branch. The project maintains only this one branch and strives to maintain a mostly linear history on it. .IP \(bu 2 Releases are made by creating a branch from the \fBmaster\fP branch and tagging the branch with the release number when ready, and then merging the release branch back to the \fBmaster\fP branch. Releases can be identified by a git tag, and a modified \fBVERSION\fP file in the branch. However, the modified \fBVERSION\fP file is not merged back to \fBmaster\fP\&. .INDENT 2.0 .IP \(bu 2 If a hotfix release must be made to update a past release, a new hotfix support branch will be created by branching from the tip of the last relevant release. Changesets from \fBmaster\fP branch (such as bug fixes) may be cherry\-picked to the hotfix branch. The hotfix release is tagged with the hotfix version number, and merged back to the \fBmaster\fP branch. .UNINDENT .IP \(bu 2 Merges to the ns\-3 \fBmaster\fP branch are fast forwarded when possible, and commits can be squashed as appropriate, to maintain a clean linear history. Merge commits can be avoided in simple cases. .INDENT 2.0 .IP \(bu 2 More complicated merges might not be able to be fast forwarded, with the result that there will be a merge commit upon the merge. .UNINDENT .IP \(bu 2 Maintainers can commit obvious non\-critical fixes (documentation improvements, typos etc.) directly into the \fBmaster\fP branch. Users who are not maintainers can create GitLab.com Merge Requests for small items such as these, for maintainers to review. .IP \(bu 2 Maintainers can directly commit bug fixes to their maintained modules without review/approval by other maintainers, although a review phase is recommended for non\-trivial fixes. Larger commits that touch multiple modules should be reviewed and approved by the set of affected maintainers. .IP \(bu 2 When proposing code (new features, bug fixes, etc.) for a module maintained by someone else, the typical workflow will be to fork the \fBnsnam/ns\-3\-dev.git\fP repository, create a local feature branch on your fork, and use GitLab.com to generate a Merge Request towards \fBnsnam/ns\-3\-dev.git\fP when ready. The Merge Request will then be reviewed, and in response to changes requested or comments from maintainers, authors are are asked to modify their feature branch and rebase to the tip of \fBns\-3\-dev.git\fP as needed. .UNINDENT .SS Setup of a personal repository .sp We will provide two ways, one anonymous (but will impede the creation of merge requests) and the other, preferred, that include forking the repository through the GitLab.com web interface. .SS Directly cloning ns\-3\-dev .sp If you go to the official ns\-3\-dev page, hosted at \fI\%https://gitlab.com/nsnam/ns\-3\-dev\fP, you can find a button that says \fBClone\fP\&. If you are not logged in, then you will see only the option of cloning the repository through HTTPS, with this command: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git clone https://gitlab.com/nsnam/ns\-3\-dev.git .ft P .fi .UNINDENT .UNINDENT .sp If this command exits successfully, you will have a newly created \fIns\-3\-dev\fP directory with all the source code. .SS Forking ns\-3\-dev on GitLab.com .sp Assume that you are the user \fIjohn\fP on GitLab.com and that you want to create a new repository that is synced with nsnam/ns\-3\-dev. .INDENT 0.0 .IP 1. 3 Log into GitLab.com .IP 2. 3 Navigate to \fI\%https://gitlab.com/nsnam/ns\-3\-dev\fP .IP 3. 3 In the top\-right corner of the page, click \fBFork\fP\&. .UNINDENT .sp Note that you may only do this once; if you try to fork again, Gitlab will take you to the page of the original fork. So, if you are planning to maintain two or more separate forks (for example, one for your private work, another for maintenance, etc.), you are doing a mistake. Instead, you should add these forks as a remote of your existing directory (see below for adding remotes). Usually, it is a good thing to add the maintainer’s repository as remotes, because it can happen that “bleeding edge” features will appear there before landing in ns\-3\-dev. .sp For more information on forking with Gilab, there is plenty of visual documentation (\fI\%https://docs.gitlab.com/ee/gitlab\-basics/fork\-project.html\fP). To work with your forked repository, you have two ways: one is a clean clone while the other is meant to re\-use an existing ns\-3 git repository. .SS Clone your forked repository on your machine .sp Git is a distributed versioning system. This means that \fInobody\fP will touch your personal repository, until you do something. Please note that every gitlab user has, at least, two repositories: the first is represented by the repository hosted on gitlab servers, which will be called in the following \fBorigin\fP\&. Then, you have your clone on your machine. This means that you could have many clones, on different machines, which points to \fBorigin\fP\&. .sp To clone the newly created fork to your system, go to the homepage of your fork (that should be in the form \fIhttps://gitlab.com/your\-user\-name/ns\-3\-dev\fP) and click the \fIClone\fP button. Then, go to your computer’s terminal, and issue the command (please refer to \fI\%https://docs.gitlab.com/ee/gitlab\-basics/command\-line\-commands.html#clone\-your\-project\fP for more documentation): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git clone https://gitlab.com/your\-user\-name/ns\-3\-dev $ cd ns\-3\-dev .ft P .fi .UNINDENT .UNINDENT .sp In this example we used the HTTPS address because in some place the git + ssh address is blocked by firewalls. If you are not under this constraint, it is recommended to use the git + ssh address to avoid the username/password typing at each request. .SS Naming conventions .sp Git is able to fetch and push changes to several repositories, each of them is called \fBremote\fP\&. With time, you probably will have many remotes, each one with many branches. To avoid confusion, it is recommended to give meaningful names to the remotes; in the following, we will use \fBorigin\fP to indicate the ns\-3\-dev repository in your personal namespace (your forked version, server\-side) and \fBnsnam\fP to indicate the ns\-3\-dev repository in the nsnam namespace, server\-side. .SS Add the official ns\-3 repository as remote upstream .sp You could have already used git in the past, and therefore already having a ns\-3 git repository somewhere. Or, instead, you could have it cloned for the first time in the step above. In both cases, when you fork/clone a repository, your history is no more bound to the repository itself. At this point, it is your duty to sync your fork with the original repository. The first remote repository we have encountered is \fBorigin\fP; we must add the official ns\-3 repo as another remote repository: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git remote add nsnam https://gitlab.com/nsnam/ns\-3\-dev .ft P .fi .UNINDENT .UNINDENT .sp With the command above, we added a remote repository, named nsnam, which links to the official ns\-3 repo. To show your remote repositories: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git remote show .ft P .fi .UNINDENT .UNINDENT .sp To see to what \fBorigin\fP is linking to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git remote show origin .ft P .fi .UNINDENT .UNINDENT .sp Many options are available; please refer to the git manual for more. .SS Add your forked repository as remote .sp If you were a user of the old github mirror, you probably have an existing git repository installed somewhere. In your case, it is not necessary to clone your fork and to port all your work in the new directory; you can add the fork as new remote: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git remote rename origin old\-origin $ git remote add origin https://gitlab.com/your\-user\-name/ns\-3\-dev .ft P .fi .UNINDENT .UNINDENT .sp After these two commands, you will have a remote, named origin, that points to your forked repository on gitlab. .SS Keep in sync your repository with latest ns\-3\-dev updates .sp We assume, from now to the end of this document, that you will not make commits on top of the master branch. It should be kept clean from \fIany\fP personal modifications: all the works must be done in branches. Therefore, to move the current HEAD of the master branch to the latest commit in ns\-3\-dev, you should do: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git checkout master $ git fetch nsnam $ git pull nsnam master .ft P .fi .UNINDENT .UNINDENT .sp If you tried a pull which resulted in a conflict and you would like to start over, you can recover with git reset (but this never happens if you do not commit over master). .SS Start a new branch to do some work .sp Look at the available branches: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git branch \-a .ft P .fi .UNINDENT .UNINDENT .sp you should see something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C * master remotes/origin/master remotes/nsnam/master .ft P .fi .UNINDENT .UNINDENT .sp The branch master is your local master branch; remotes/origin/master point at the master branch on your repository located in the Gitlab server, while remotes/nsnam/master points to the official master branch. .sp Before entering in details on how to create a new branch, we have to explain why it is recommended to do it. First of all, if you put all your work in a separate branch, you can easily see the diff between ns\-3 mainline and your feature branch (with \fBgit diff master\fP). Also, you can integrate more easily the upstream advancements in your work, and when you wish, you can create a \fIconflict\-free\fP merge request, that will ease the maintainer’s job in reviewing your work. .sp To create a new branch, starting from master, the command is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git checkout master $ git checkout \-b [name_of_your_new_branch] .ft P .fi .UNINDENT .UNINDENT .sp To switch between branches, remove the \-b option. You should now see: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git branch \-a * master [name_of_your_new_branch] remotes/origin/master remotes/nsnam/master .ft P .fi .UNINDENT .UNINDENT .SS Edit and commit the modifications .sp After you edit some file, you should commit the difference. As a policy, git users love small and incremental patches. So, commit early, and commit often: you could rewrite your history later. .sp Suppose we edited \fBsrc/internet/model/tcp\-socket\-base.cc\fP\&. With git status, we can see the repository status: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git status On branch tcp\-next Your branch is up\-to\-date with \(aqmirror/tcp\-next\(aq. Changes not staged for commit: modified: src/internet/model/tcp\-socket\-base.cc .ft P .fi .UNINDENT .UNINDENT .sp and we can see the edits with git diff: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git diff nat@miyamoto ~/Work/ns\-3\-dev\-git (tcp\-next)$ git diff diff \-\-git i/src/internet/model/tcp\-socket\-base.cc w/src/internet/model/tcp\-socket\-base.cc index 1bf0f69..e2298b0 100644 \-\-\- i/src/internet/model/tcp\-socket\-base.cc +++ w/src/internet/model/tcp\-socket\-base.cc @@ \-1439,6 +1439,10 @@ TcpSocketBase::ReceivedAck (Ptr packet, const TcpHeader& tcpHeader) // There is a DupAck ++m_dupAckCount; + // I\(aqm introducing a subtle bug! + + m_tcb\->m_cWnd = m_tcb\->m_ssThresh; + if (m_tcb\->m_congState == TcpSocketState::CA_OPEN) { // From Open we go Disorder .ft P .fi .UNINDENT .UNINDENT .sp To create a commit, select the file you want to add to the commit with git add: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git add src/internet/model/tcp\-socket\-base.cc .ft P .fi .UNINDENT .UNINDENT .sp and then commit the result: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git commit \-m "My new TCP broken" .ft P .fi .UNINDENT .UNINDENT .sp Of course, it would be better to have some rules for the commit message: they will be reported in the next subsection. .SS Commit message guidelines .sp The commit title should not go over the 80 char limit. It should be prefixed by the name of the module you are working on, and if it fixes a bug, it should reference it in the commit title. For instance, a good commit title would be: .INDENT 0.0 .INDENT 3.5 tcp: My new TCP broken .UNINDENT .UNINDENT .sp Another example is: .INDENT 0.0 .INDENT 3.5 tcp: (fixes #2322) Corrected the uint32_t wraparound during recovery .UNINDENT .UNINDENT .sp In the body message, try to explain what the problem was, and how you resolved that. If it is a new feature, try to describe it at a very high level, and highlight any modifications that changed the behaviour or the interface towards the users or other modules. .SS Commit log .sp You can see the history of the commits with git log. To show a particular commit, copy the sha\-id and use \fBgit show \fP\&. The ID is unique, so it can be referenced in emails or in issues. The next step is useful if you plan to contribute back your changes, but also to keep your feature branch updated with the latest changes from ns\-3\-dev. .SS Rebase your branch on top of master .sp Meanwhile you were busy with your branch, the upstream master could have changed. To rebase your work with the now new master, first of all sync your master branch (pulling the nsnam/master branch into your local master branch) as explained before; then .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git checkout [name_of_your_new_branch] $ git rebase master .ft P .fi .UNINDENT .UNINDENT .sp The last command will rewind your work, update the HEAD of your branch to the actual master, and then re\-apply all your work. If some of your work conflicts with the actual master, you will be asked to fix these conflicts if automatic merge fails. .SS Pushing your changes to origin .sp After you have done some work on a branch, if you would like to share it with others, there is nothing better than pushing your work to your origin repository, on Gitlab servers. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git checkout [name_of_your_new_branch] $ git push origin [name_of_your_new_branch] .ft P .fi .UNINDENT .UNINDENT .sp The \fBgit push\fP command can be used every time you need to push something from your computer to a remote repository, except when you propose changes to the main ns\-3\-dev repository: your changes must pass a review stage. .sp Please note that for older git version, the push command looks like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git push \-u origin [name_of_your_new_branch] .ft P .fi .UNINDENT .UNINDENT .SS Submit work for review .sp After you push your branch to origin, you can follow the instructions here \fI\%https://docs.gitlab.com/ee/gitlab\-basics/add\-merge\-request.html\fP to create a merge request. Please remember to add, as reviewer, at least one maintainer. To get the information on who is maintaining what, please refer to the \fI\%nsnam website\fP\&. .SS Porting patches from mercurial repositories to git .sp \fIPlaceholder section; please improve it.\fP .SH WORKING WITH GIT AS A MAINTAINER .sp As a maintainer, you are a person who has write access to the main nsnam repository. You could push your own work (without passing from code review) or push someone else’s work. Let’s investigate the two cases. .SS Pushing your own work .sp Since you have been added to the Developer list on Gitlab (if not, please open an issue) you can use the git + ssh address when adding nsnam as remote. Once you have done that, you can do your modifications to a local branch, then update the master to point to the latest changes of the nsnam repo, and then: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git checkout master $ git pull nsnam master $ git merge [your_branch_name] $ git push nsnam master .ft P .fi .UNINDENT .UNINDENT .sp Please note that if you want to keep track of your branch, you can use as command \fBgit merge \-\-no\-ff [your_branch_name]\fP\&. It is always recommended to rebase your branch before merging, to have a clean history. That is not a requirement, though: git perfectly handles a master with parallel merged branches. .SS Review and merge someone else’s work .sp Gitlab.com has a plenty of documentation on how to handle merge requests. Please take a look here: \fI\%https://docs.gitlab.com/ee/user/project/merge_requests/index.html\fP\&. .sp If you are committing a patch from someone else, and it is not coming through a Merge Request process, you can use the –author=’’ argument to ‘git commit’ to assign authorship to another email address (such as we have done in the past with the Mercurial \-u option). .SS Making a release .sp As stated above, the project has adopted a workflow to aim for a mostly linear history on a single \fBmaster\fP branch. Releases are branches from this \fBmaster\fP branch but the branches themselves are not long\-lived; the release branches are merged back to \fBmaster\fP in a special way. However, the release branches can be checked out by using the git tag facility; a named release such as ‘ns\-3.30’ can be checked out on a branch by specifying the release name ‘ns\-3.30’ (or ‘ns\-3.30.1’ etc.). .sp A compact way to represent a git history is the following command: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git log \-\-graph \-\-decorate \-\-oneline \-\-all .ft P .fi .UNINDENT .UNINDENT .sp At the point just before the ns\-3.34 release, the log looked like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C * 9df8ef4 (HEAD \-> master) doc: Update ns\-3 version in tutorial examples * 9319cdd (origin/master, origin/HEAD) Update CHANGES.html and RELEASE_NOTES * 8da68b5 wifi: Fix typo in channel access manager test .ft P .fi .UNINDENT .UNINDENT .sp We want the release to create a small branch that is merged (in a special way) back to the mainline, yielding something like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C * 4b27025 (master) Update release files to start next release * fd075f6 Merge ns\-3.34\-release branch |\e | * 3fab3cf (HEAD, tag: ns\-3.34) Update availability in RELEASE_NOTES | * c50aaf7 Update VERSION and documentation tags for ns\-3.34 release |/ * 9df8ef4 doc: Update ns\-3 version in tutorial examples * 9319cdd (origin/master, origin/HEAD) Update CHANGES.html and RELEASE_NOTES .ft P .fi .UNINDENT .UNINDENT .sp The first commit on the release branch changes the ‘3\-dev’ string in VERSION and the various documentation conf.py files to ‘3.34’. The second commit on the release branch updates RELEASE_NOTES to state the URL of the release. .sp Starting with commit 9df8ef4, the following steps were taken to create the ns\-3.34 release. First, this commit hash ‘9df8ef4’ will be used later in the merge process. .sp First, create a new release branch locally: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git checkout \-b \(aqns\-3.34\-release\(aq Switched to a new branch \(aqns\-3.34\-release\(aq .ft P .fi .UNINDENT .UNINDENT .sp We change the VERSION field from ‘3\-dev’ to ‘3.34’: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ sed \-i \(aqs/3\-dev/3.34/g\(aq VERSION $ cat VERSION 3.34 .ft P .fi .UNINDENT .UNINDENT .sp We next change the file conf.py in the tutorial, manual, and models directories to change the strings ‘ns\-3\-dev’ to ns\-3.34. .sp When you are done, the ‘git status’ command should show: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C VERSION | 2 +\- doc/manual/source/conf.py | 4 ++\-\- doc/models/source/conf.py | 4 ++\-\- doc/tutorial/source/conf.py | 4 ++\-\- .ft P .fi .UNINDENT .UNINDENT .sp Make a commit of these files: .INDENT 0.0 .INDENT 3.5 $ git commit \-a \-m”Update VERSION and documentation tags for ns\-3.34 release” .UNINDENT .UNINDENT .sp Next, make the following change to RELEASE_NOTES and commit it: .INDENT 0.0 .TP .B :: .INDENT 7.0 .INDENT 3.5 .UNINDENT .UNINDENT .sp \-This release is not yet available. +This release is available from: +https://www.nsnam.org/release/ns\-allinone\-3.34.tar.bz2 .sp $ git commit \-m”Update availability in RELEASE_NOTES” RELEASE_NOTES .UNINDENT .sp Finally, add a git annotated tag: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git tag \-a \(aqns\-3.34\(aq \-m"ns\-3.34 release" .ft P .fi .UNINDENT .UNINDENT .sp Now, let’s merge back to \fBmaster\fP\&. However, we want to avoid touching the \fBVERSION\fP and \fBconf.py\fP files on \fBmaster\fP; we want the RELEASE_NOTES change and new tag. We can accomplish this with a special merge as follows. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git checkout master $ git merge \-\-no\-commit \-\-no\-ff ns\-3.34\-release Automatic merge went well; stopped before committing as requested .ft P .fi .UNINDENT .UNINDENT .sp Now, we want to reset VERSION to the previous string, which existed before we branched. We can use \fBgit reset\fP on this file and then finish the merge. Recall its commit hash of \fB9df8ef4\fP from above. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git reset 9df8ef4 VERSION Unstaged changes after reset: M VERSION $ sed \-i \(aqs/3.34/3\-dev/g\(aq VERSION $ cat VERSION 3\-dev .ft P .fi .UNINDENT .UNINDENT .sp Repeat the above resets and change back to \fB3\-dev\fP for each conf.py file. .sp Finally, commit the branch and delete our local release branch. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git commit \-m"Merge ns\-3.34\-release branch" $ git branch \-d ns\-3.34\-release .ft P .fi .UNINDENT .UNINDENT .sp The git history now looks like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git log \-\-graph \-\-decorate \-\-oneline \-\-all * fd075f6 (HEAD \-> master) Merge ns\-3.34\-release branch |\e | * 3fab3cf (HEAD, tag: ns\-3.34) Update availability in RELEASE_NOTES | * c50aaf7 Update VERSION and documentation tags for ns\-3.34 release |/ * 9df8ef4 doc: Update ns\-3 version in tutorial examples * 9319cdd (origin/master, origin/HEAD) Update CHANGES.html and RELEASE_NOTES .ft P .fi .UNINDENT .UNINDENT .sp This may now be pushed to \fBnsnam/ns\-3\-dev.git\fP and development can continue. .sp \fBImportant:\fP When pushing to the remote, don’t forget to push the tags: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git push \-\-follow\-tags .ft P .fi .UNINDENT .UNINDENT .sp Future users who want to check out the ns\-3.34 release will do something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git checkout \-b my\-local\-ns\-3.34 ns\-3.34 Switched to a new branch \(aqmy\-local\-ns\-3.34\(aq .ft P .fi .UNINDENT .UNINDENT .sp \fBNote:\fP It is a good idea to avoid naming the new branch the same as the tag name; in this case, ‘ns\-3.34’. .sp Let’s assume now that master evolves with new features and bugfixes. They are committed to \fBmaster\fP on \fBnsnam/ns\-3\-dev.git\fP as usual: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git checkout master \&... (some changes) $ git commit \-m"make some changes" \-a $ echo \(aqd\(aq >> d $ git add d $ git commit \-m"Add new feature" d \&... (some more changes) $ git commit \-m"some more changes" \-a \&... (now fix a really important bug) $ echo \(aqabc\(aq >> a $ git commit \-m"Fix missing abc bug on file a" a .ft P .fi .UNINDENT .UNINDENT .sp Now the tree looks like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git log \-\-graph \-\-decorate \-\-oneline \-\-all * ee37d41 (HEAD \-> master) Fix missing abc bug on file a * 9a3432a some more changes * ba28d6d Add new feature * e50015a make some changes * fd075f6 Merge ns\-3.34\-release branch |\e | * 3fab3cf (tag: ns\-3.34) Update availability in RELEASE_NOTES | * c50aaf7 Update VERSION and documentation tags for ns\-3.34 release |/ * 9df8ef4 doc: Update ns\-3 version in tutorial examples * 9319cdd Update CHANGES.html and RELEASE_NOTES .ft P .fi .UNINDENT .UNINDENT .sp Let’s assume that the changeset \fBee37d41\fP is considered important to fix in the ns\-3.34 release, but we don’t want the other changes introduced since then. The solution will be to create a new branch for a hotfix release, and follow similar steps. The branch for the hotfix should come from commit \fB3fab3cf\fP, and should cherry\-pick commit \fBee37d41\fP (which may require merge if it doesn’t apply cleanly), and then the hotfix branch can be tagged and merged as was done before. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git checkout \-b ns\-3.34.1\-release ns\-3.34 $ git cherry\-pick ee37d41 \&... (resolve any conflicts) $ git add a $ git commit $ sed \-i \(aqs/3.34/3.34.1/g\(aq VERSION $ cat VERSION 3.34.1 $ git commit \-m"Update VERSION to 3.34.1" VERSION $ git tag \-a \(aqns\-3.34.1\(aq \-m"ns\-3.34.1 release" .ft P .fi .UNINDENT .UNINDENT .sp Now the merge: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git checkout master $ git merge \-\-no\-commit \-\-no\-ff ns\-3.34.1\-release .ft P .fi .UNINDENT .UNINDENT .sp This time we may see something like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Auto\-merging a CONFLICT (content): Merge conflict in a Auto\-merging VERSION CONFLICT (content): Merge conflict in VERSION Automatic merge failed; fix conflicts and then commit the result. .ft P .fi .UNINDENT .UNINDENT .sp And we can then do: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git reset ee37d41 a $ git reset ee37d41 VERSION .ft P .fi .UNINDENT .UNINDENT .sp Which leaves us with: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Unstaged changes after reset: M VERSION M a .ft P .fi .UNINDENT .UNINDENT .sp We can next hand\-edit these files to restore them to original state, so that: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git status On branch master Your branch is ahead of \(aqorigin/master\(aq by 8 commits. (use "git push" to publish your local commits) All conflicts fixed but you are still merging. (use "git commit" to conclude merge) $ git commit $ git branch \-d ns\-3.34.1\-release .ft P .fi .UNINDENT .UNINDENT .sp The new log should show something like the below, with parallel git history paths until the merge back again: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git log \-\-graph \-\-decorate \-\-oneline \-\-all * 815ce6e (HEAD \-> master) Merge branch \(aqns\-3.34.1\-release\(aq |\e | * 12a29ca (tag: ns\-3.34.1) Update VERSION to 3.34.1 | * 21ebdbf Fix missing abc bug on file a * | ee37d41 Fix missing abc bug on file a * | 9a3432a some more changes * | ba28d6d Add new feature * | e50015a make some changes * | fd075f6 Merge ns\-3.34\-release branch |\e \e | |/ | * 3fab3cf (tag: ns\-3.34) Update availability in RELEASE_NOTES | * c50aaf7 Update VERSION and documentation tags for ns\-3.34 release |/ * 9df8ef4 doc: Update ns\-3 version in tutorial examples * 9319cdd Update CHANGES.html and RELEASE_NOTES $ git push origin master:master \-\-follow\-tags .ft P .fi .UNINDENT .UNINDENT .sp And we can continue to commit on top of master going forward. The two tags should be found in the \fBgit tag\fP output (among other tags): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ git tag ns\-3.34 ns\-3.34.1 .ft P .fi .UNINDENT .UNINDENT .SH RANDOM VARIABLES .sp \fIns\-3\fP contains a built\-in pseudo\-random number generator (PRNG). It is important for serious users of the simulator to understand the functionality, configuration, and usage of this PRNG, and to decide whether it is sufficient for his or her research use. .SS Quick Overview .sp \fIns\-3\fP random numbers are provided via instances of \fBns3::RandomVariableStream\fP\&. .INDENT 0.0 .IP \(bu 2 by default, \fIns\-3\fP simulations use a fixed seed; if there is any randomness in the simulation, each run of the program will yield identical results unless the seed and/or run number is changed. .IP \(bu 2 in \fIns\-3.3\fP and earlier, \fIns\-3\fP simulations used a random seed by default; this marks a change in policy starting with \fIns\-3.4\fP\&. .IP \(bu 2 in \fIns\-3.14\fP and earlier, \fIns\-3\fP simulations used a different wrapper class called \fBns3::RandomVariable\fP\&. As of \fIns\-3.15\fP, this class has been replaced by \fBns3::RandomVariableStream\fP; the underlying pseudo\-random number generator has not changed. .IP \(bu 2 to obtain randomness across multiple simulation runs, you must either set the seed differently or set the run number differently. To set a seed, call \fBns3::RngSeedManager::SetSeed()\fP at the beginning of the program; to set a run number with the same seed, call \fBns3::RngSeedManager::SetRun()\fP at the beginning of the program; see \fI\%Creating random variables\fP\&. .IP \(bu 2 each RandomVariableStream used in \fIns\-3\fP has a virtual random number generator associated with it; all random variables use either a fixed or random seed based on the use of the global seed (previous bullet); .IP \(bu 2 if you intend to perform multiple runs of the same scenario, with different random numbers, please be sure to read the section on how to perform independent replications: \fI\%Creating random variables\fP\&. .UNINDENT .sp Read further for more explanation about the random number facility for \fIns\-3\fP\&. .SS Background .sp Simulations use a lot of random numbers; one study found that most network simulations spend as much as 50% of the CPU generating random numbers. Simulation users need to be concerned with the quality of the (pseudo) random numbers and the independence between different streams of random numbers. .sp Users need to be concerned with a few issues, such as: .INDENT 0.0 .IP \(bu 2 the seeding of the random number generator and whether a simulation outcome is deterministic or not, .IP \(bu 2 how to acquire different streams of random numbers that are independent from one another, and .IP \(bu 2 how long it takes for streams to cycle .UNINDENT .sp We will introduce a few terms here: a RNG provides a long sequence of (pseudo) random numbers. The length of this sequence is called the \fIcycle length\fP or \fIperiod\fP, after which the RNG will repeat itself. This sequence can be partitioned into disjoint \fIstreams\fP\&. A stream of a RNG is a contiguous subset or block of the RNG sequence. For instance, if the RNG period is of length N, and two streams are provided from this RNG, then the first stream might use the first N/2 values and the second stream might produce the second N/2 values. An important property here is that the two streams are uncorrelated. Likewise, each stream can be partitioned disjointedly to a number of uncorrelated \fIsubstreams\fP\&. The underlying RNG hopefully produces a pseudo\-random sequence of numbers with a very long cycle length, and partitions this into streams and substreams in an efficient manner. .sp \fIns\-3\fP uses the same underlying random number generator as does \fIns\-2\fP: the MRG32k3a generator from Pierre L’Ecuyer. A detailed description can be found in \fI\%http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf\fP\&. The MRG32k3a generator provides 1.8x10^{19} independent streams of random numbers, each of which consists of 2.3x10^{15} substreams. Each substream has a period (\fIi.e.\fP, the number of random numbers before overlap) of 7.6x10^{22}\&. The period of the entire generator is 3.1x10^{57}\&. .sp Class \fBns3::RandomVariableStream\fP is the public interface to this underlying random number generator. When users create new random variables (such as \fBns3::UniformRandomVariable\fP, \fBns3::ExponentialRandomVariable\fP, etc.), they create an object that uses one of the distinct, independent streams of the random number generator. Therefore, each object of type \fBns3::RandomVariableStream\fP has, conceptually, its own “virtual” RNG. Furthermore, each \fBns3::RandomVariableStream\fP can be configured to use one of the set of substreams drawn from the main stream. .sp An alternate implementation would be to allow each RandomVariable to have its own (differently seeded) RNG. However, we cannot guarantee as strongly that the different sequences would be uncorrelated in such a case; hence, we prefer to use a single RNG and streams and substreams from it. .SS Creating random variables .sp \fIns\-3\fP supports a number of random variable objects from the base class \fBRandomVariableStream\fP\&. These objects derive from \fBns3::Object\fP and are handled by smart pointers. .sp The correct way to create these objects is to use the templated \fICreateObject<>\fP method, such as: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Ptr x = CreateObject (); .ft P .fi .UNINDENT .UNINDENT .sp then you can access values by calling methods on the object such as: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C myRandomNo = x\->GetInteger (); .ft P .fi .UNINDENT .UNINDENT .sp If you try to instead do something like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C myRandomNo = UniformRandomVariable().GetInteger (); .ft P .fi .UNINDENT .UNINDENT .sp your program will encounter a segmentation fault, because the implementation relies on some attribute construction that occurs only when \fICreateObject\fP is called. .sp Much of the rest of this chapter now discusses the properties of the stream of pseudo\-random numbers generated from such objects, and how to control the seeding of such objects. .SS Seeding and independent replications .sp \fIns\-3\fP simulations can be configured to produce deterministic or random results. If the \fIns\-3\fP simulation is configured to use a fixed, deterministic seed with the same run number, it should give the same output each time it is run. .sp By default, \fIns\-3\fP simulations use a fixed seed and run number. These values are stored in two \fBns3::GlobalValue\fP instances: \fBg_rngSeed\fP and \fBg_rngRun\fP\&. .sp A typical use case is to run a simulation as a sequence of independent trials, so as to compute statistics on a large number of independent runs. The user can either change the global seed and rerun the simulation, or can advance the substream state of the RNG, which is referred to as incrementing the run number. .sp A class \fBns3::RngSeedManager\fP provides an API to control the seeding and run number behavior. This seeding and substream state setting must be called before any random variables are created; e.g: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C RngSeedManager::SetSeed (3); // Changes seed from default of 1 to 3 RngSeedManager::SetRun (7); // Changes run number from default of 1 to 7 // Now, create random variables Ptr x = CreateObject (); Ptr y = CreateObject (); \&... .ft P .fi .UNINDENT .UNINDENT .sp Which is better, setting a new seed or advancing the substream state? There is no guarantee that the streams produced by two random seeds will not overlap. The only way to guarantee that two streams do not overlap is to use the substream capability provided by the RNG implementation. \fITherefore, use the substream capability to produce multiple independent runs of the same simulation.\fP In other words, the more statistically rigorous way to configure multiple independent replications is to use a fixed seed and to advance the run number. This implementation allows for a maximum of 2.3x10^{15} independent replications using the substreams. .sp For ease of use, it is not necessary to control the seed and run number from within the program; the user can set the \fBNS_GLOBAL_VALUE\fP environment variable as follows: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ NS_GLOBAL_VALUE="RngRun=3" ./waf \-\-run program\-name .ft P .fi .UNINDENT .UNINDENT .sp Another way to control this is by passing a command\-line argument; since this is an \fIns\-3\fP GlobalValue instance, it is equivalently done such as follows: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ ./waf \-\-command\-template="%s \-\-RngRun=3" \-\-run program\-name .ft P .fi .UNINDENT .UNINDENT .sp or, if you are running programs directly outside of waf: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ ./build/optimized/scratch/program\-name \-\-RngRun=3 .ft P .fi .UNINDENT .UNINDENT .sp The above command\-line variants make it easy to run lots of different runs from a shell script by just passing a different RngRun index. .SS Class RandomVariableStream .sp All random variables should derive from class \fBRandomVariable\fP\&. This base class provides a few methods for globally configuring the behavior of the random number generator. Derived classes provide API for drawing random variates from the particular distribution being supported. .sp Each RandomVariableStream created in the simulation is given a generator that is a new RNGStream from the underlying PRNG. Used in this manner, the L’Ecuyer implementation allows for a maximum of 1.8x10^19 random variables. Each random variable in a single replication can produce up to 7.6x10^22 random numbers before overlapping. .SS Base class public API .sp Below are excerpted a few public methods of class \fBRandomVariableStream\fP that access the next value in the substream. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C /** * \ebrief Returns a random double from the underlying distribution * \ereturn A floating point random value */ double GetValue (void) const; /** * \ebrief Returns a random integer from the underlying distribution * \ereturn Integer cast of ::GetValue() */ uint32_t GetInteger (void) const; .ft P .fi .UNINDENT .UNINDENT .sp We have already described the seeding configuration above. Different RandomVariable subclasses may have additional API. .SS Types of RandomVariables .sp The following types of random variables are provided, and are documented in the \fIns\-3\fP Doxygen or by reading \fBsrc/core/model/random\-variable\-stream.h\fP\&. Users can also create their own custom random variables by deriving from class \fBRandomVariableStream\fP\&. .INDENT 0.0 .IP \(bu 2 class \fBUniformRandomVariable\fP .IP \(bu 2 class \fBConstantRandomVariable\fP .IP \(bu 2 class \fBSequentialRandomVariable\fP .IP \(bu 2 class \fBExponentialRandomVariable\fP .IP \(bu 2 class \fBParetoRandomVariable\fP .IP \(bu 2 class \fBWeibullRandomVariable\fP .IP \(bu 2 class \fBNormalRandomVariable\fP .IP \(bu 2 class \fBLogNormalRandomVariable\fP .IP \(bu 2 class \fBGammaRandomVariable\fP .IP \(bu 2 class \fBErlangRandomVariable\fP .IP \(bu 2 class \fBTriangularRandomVariable\fP .IP \(bu 2 class \fBZipfRandomVariable\fP .IP \(bu 2 class \fBZetaRandomVariable\fP .IP \(bu 2 class \fBDeterministicRandomVariable\fP .IP \(bu 2 class \fBEmpiricalRandomVariable\fP .UNINDENT .SS Semantics of RandomVariableStream objects .sp RandomVariableStream objects derive from \fBns3::Object\fP and are handled by smart pointers. .sp RandomVariableStream instances can also be used in \fIns\-3\fP attributes, which means that values can be set for them through the \fIns\-3\fP attribute system. An example is in the propagation models for WifiNetDevice: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C TypeId RandomPropagationDelayModel::GetTypeId (void) { static TypeId tid = TypeId ("ns3::RandomPropagationDelayModel") .SetParent () .SetGroupName ("Propagation") .AddConstructor () .AddAttribute ("Variable", "The random variable which generates random delays (s).", StringValue ("ns3::UniformRandomVariable"), MakePointerAccessor (&RandomPropagationDelayModel::m_variable), MakePointerChecker ()) ; return tid; } .ft P .fi .UNINDENT .UNINDENT .sp Here, the \fIns\-3\fP user can change the default random variable for this delay model (which is a UniformRandomVariable ranging from 0 to 1) through the attribute system. .SS Using other PRNG .sp There is presently no support for substituting a different underlying random number generator (e.g., the GNU Scientific Library or the Akaroa package). Patches are welcome. .SS Setting the stream number .sp The underlying MRG32k3a generator provides 2^64 independent streams. In ns\-3, these are assigned sequentially starting from the first stream as new RandomVariableStream instances make their first call to GetValue(). .sp As a result of how these RandomVariableStream objects are assigned to underlying streams, the assignment is sensitive to perturbations of the simulation configuration. The consequence is that if any aspect of the simulation configuration is changed, the mapping of RandomVariables to streams may (or may not) change. .sp As a concrete example, a user running a comparative study between routing protocols may find that the act of changing one routing protocol for another will notice that the underlying mobility pattern also changed. .sp Starting with ns\-3.15, some control has been provided to users to allow users to optionally fix the assignment of selected RandomVariableStream objects to underlying streams. This is the \fBStream\fP attribute, part of the base class RandomVariableStream. .sp By partitioning the existing sequence of streams from before: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> stream 0 stream (2^64 \- 1) .ft P .fi .UNINDENT .UNINDENT .sp into two equal\-sized sets: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C <\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> ^ ^^ ^ | || | stream 0 stream (2^63 \- 1) stream 2^63 stream (2^64 \- 1) <\- automatically assigned \-\-\-\-\-\-\-\-\-\-\-><\- assigned by user \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-> .ft P .fi .UNINDENT .UNINDENT .sp The first 2^63 streams continue to be automatically assigned, while the last 2^63 are given stream indices starting with zero up to 2^63\-1. .sp The assignment of streams to a fixed stream number is optional; instances of RandomVariableStream that do not have a stream value assigned will be assigned the next one from the pool of automatic streams. .sp To fix a RandomVariableStream to a particular underlying stream, assign its \fBStream\fP attribute to a non\-negative integer (the default value of \-1 means that a value will be automatically allocated). .SS Publishing your results .sp When you publish simulation results, a key piece of configuration information that you should always state is how you used the random number generator. .INDENT 0.0 .IP \(bu 2 what seeds you used, .IP \(bu 2 what RNG you used if not the default, .IP \(bu 2 how were independent runs performed, .IP \(bu 2 for large simulations, how did you check that you did not cycle. .UNINDENT .sp It is incumbent on the researcher publishing results to include enough information to allow others to reproduce his or her results. It is also incumbent on the researcher to convince oneself that the random numbers used were statistically valid, and to state in the paper why such confidence is assumed. .SS Summary .sp Let’s review what things you should do when creating a simulation. .INDENT 0.0 .IP \(bu 2 Decide whether you are running with a fixed seed or random seed; a fixed seed is the default, .IP \(bu 2 Decide how you are going to manage independent replications, if applicable, .IP \(bu 2 Convince yourself that you are not drawing more random values than the cycle length, if you are running a very long simulation, and .IP \(bu 2 When you publish, follow the guidelines above about documenting your use of the random number generator. .UNINDENT .SH HASH FUNCTIONS .sp \fIns\-3\fP provides a generic interface to general purpose hash functions. In the simplest usage, the hash function returns the 32\-bit or 64\-bit hash of a data buffer or string. The default underlying hash function is \fI\%murmur3\fP, chosen because it has good hash function properties and offers a 64\-bit version. The venerable \fI\%FNV1a\fP hash is also available. .sp There is a straight\-forward mechanism to add (or provide at run time) alternative hash function implementations. .SS Basic Usage .sp The simplest way to get a hash value of a data buffer or string is just: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C #include "ns3/hash.h" using namespace ns3; char * buffer = ... size_t buffer_size = ... uint32_t buffer_hash = Hash32 ( buffer, buffer_size); std::string s; uint32_t string_hash = Hash32 (s); .ft P .fi .UNINDENT .UNINDENT .sp Equivalent functions are defined for 64\-bit hash values. .SS Incremental Hashing .sp In some situations it’s useful to compute the hash of multiple buffers, as if they had been joined together. (For example, you might want the hash of a packet stream, but not want to assemble a single buffer with the combined contents of all the packets.) .sp This is almost as straight\-forward as the first example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C #include "ns3/hash.h" using namespace ns3; char * buffer; size_t buffer_size; Hasher hasher; // Use default hash function for () { buffer = get_next_buffer (); hasher (buffer, buffer_size); } uint32_t combined_hash = hasher.GetHash32 (); .ft P .fi .UNINDENT .UNINDENT .sp By default \fBHasher\fP preserves internal state to enable incremental hashing. If you want to reuse a \fBHasher\fP object (for example because it’s configured with a non\-default hash function), but don’t want to add to the previously computed hash, you need to \fBclear()\fP first: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C hasher.clear ().GetHash32 (buffer, buffer_size); .ft P .fi .UNINDENT .UNINDENT .sp This reinitializes the internal state before hashing the buffer. .SS Using an Alternative Hash Function .sp The default hash function is \fI\%murmur3\fP\&. \fI\%FNV1a\fP is also available. To specify the hash function explicitly, use this constructor: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Hasher hasher = Hasher ( Create () ); .ft P .fi .UNINDENT .UNINDENT .SS Adding New Hash Function Implementations .sp To add the hash function \fBfoo\fP, follow the \fBhash\-murmur3.h\fP/\fB\&.cc\fP pattern: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 Create a class declaration (\fB\&.h\fP) and definition (\fB\&.cc\fP) inheriting from \fBHash::Implementation\fP\&. .IP \(bu 2 \fBinclude\fP the declaration in \fBhash.h\fP (at the point where \fBhash\-murmur3.h\fP is included. .IP \(bu 2 In your own code, instantiate a \fBHasher\fP object via the constructor \fBHasher (Ptr ())\fP .UNINDENT .UNINDENT .UNINDENT .sp If your hash function is a single function, e.g. \fBhashf\fP, you don’t even need to create a new class derived from HashImplementation: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Hasher hasher = Hasher ( Create (&hashf) ); .ft P .fi .UNINDENT .UNINDENT .sp For this to compile, your \fBhashf\fP has to match one of the function pointer signatures: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C typedef uint32_t (*Hash32Function_ptr) (const char *, const size_t); typedef uint64_t (*Hash64Function_ptr) (const char *, const size_t); .ft P .fi .UNINDENT .UNINDENT .SS Sources for Hash Functions .sp Sources for other hash function implementations include: .INDENT 0.0 .INDENT 3.5 .INDENT 0.0 .IP \(bu 2 Peter Kankowski: \fI\%http://www.strchr.com\fP .IP \(bu 2 Arash Partow: \fI\%http://www.partow.net/programming/hashfunctions/index.html\fP .IP \(bu 2 SMHasher: \fI\%http://code.google.com/p/smhasher/\fP .IP \(bu 2 Sanmayce: \fI\%http://www.sanmayce.com/Fastest_Hash/index.html\fP .UNINDENT .UNINDENT .UNINDENT .SH EVENTS AND SIMULATOR .sp \fIns\-3\fP is a discrete\-event network simulator. Conceptually, the simulator keeps track of a number of events that are scheduled to execute at a specified simulation time. The job of the simulator is to execute the events in sequential time order. Once the completion of an event occurs, the simulator will move to the next event (or will exit if there are no more events in the event queue). If, for example, an event scheduled for simulation time “100 seconds” is executed, and the next event is not scheduled until “200 seconds”, the simulator will immediately jump from 100 seconds to 200 seconds (of simulation time) to execute the next event. This is what is meant by “discrete\-event” simulator. .sp To make this all happen, the simulator needs a few things: .INDENT 0.0 .IP 1. 3 a simulator object that can access an event queue where events are stored and that can manage the execution of events .IP 2. 3 a scheduler responsible for inserting and removing events from the queue .IP 3. 3 a way to represent simulation time .IP 4. 3 the events themselves .UNINDENT .sp This chapter of the manual describes these fundamental objects (simulator, scheduler, time, event) and how they are used. .SS Event .sp \fITo be completed\fP .SS Simulator .sp The Simulator class is the public entry point to access event scheduling facilities. Once a couple of events have been scheduled to start the simulation, the user can start to execute them by entering the simulator main loop (call \fBSimulator::Run\fP). Once the main loop starts running, it will sequentially execute all scheduled events in order from oldest to most recent until there are either no more events left in the event queue or Simulator::Stop has been called. .sp To schedule events for execution by the simulator main loop, the Simulator class provides the Simulator::Schedule* family of functions. .INDENT 0.0 .IP 1. 3 Handling event handlers with different signatures .UNINDENT .sp These functions are declared and implemented as C++ templates to handle automatically the wide variety of C++ event handler signatures used in the wild. For example, to schedule an event to execute 10 seconds in the future, and invoke a C++ method or function with specific arguments, you might write this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C void handler (int arg0, int arg1) { std::cout << "handler called with argument arg0=" << arg0 << " and arg1=" << arg1 << std::endl; } Simulator::Schedule(Seconds(10), &handler, 10, 5); .ft P .fi .UNINDENT .UNINDENT .sp Which will output: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C handler called with argument arg0=10 and arg1=5 .ft P .fi .UNINDENT .UNINDENT .sp Of course, these C++ templates can also handle transparently member methods on C++ objects: .sp \fITo be completed: member method example\fP .sp Notes: .INDENT 0.0 .IP \(bu 2 the ns\-3 Schedule methods recognize automatically functions and methods only if they take less than 5 arguments. If you need them to support more arguments, please, file a bug report. .IP \(bu 2 Readers familiar with the term ‘fully\-bound functors’ will recognize the Simulator::Schedule methods as a way to automatically construct such objects. .UNINDENT .INDENT 0.0 .IP 2. 3 Common scheduling operations .UNINDENT .sp The Simulator API was designed to make it really simple to schedule most events. It provides three variants to do so (ordered from most commonly used to least commonly used): .INDENT 0.0 .IP \(bu 2 Schedule methods which allow you to schedule an event in the future by providing the delay between the current simulation time and the expiration date of the target event. .IP \(bu 2 ScheduleNow methods which allow you to schedule an event for the current simulation time: they will execute _after_ the current event is finished executing but _before_ the simulation time is changed for the next event. .IP \(bu 2 ScheduleDestroy methods which allow you to hook in the shutdown process of the Simulator to cleanup simulation resources: every ‘destroy’ event is executed when the user calls the Simulator::Destroy method. .UNINDENT .INDENT 0.0 .IP 3. 3 Maintaining the simulation context .UNINDENT .sp There are two basic ways to schedule events, with and without \fIcontext\fP\&. What does this mean? .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj); .ft P .fi .UNINDENT .UNINDENT .sp vs. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Simulator::ScheduleWithContext (uint32_t context, Time const &time, MEM mem_ptr, OBJ obj); .ft P .fi .UNINDENT .UNINDENT .sp Readers who invest time and effort in developing or using a non\-trivial simulation model will know the value of the ns\-3 logging framework to debug simple and complex simulations alike. One of the important features that is provided by this logging framework is the automatic display of the network node id associated with the ‘currently’ running event. .sp The node id of the currently executing network node is in fact tracked by the Simulator class. It can be accessed with the Simulator::GetContext method which returns the ‘context’ (a 32\-bit integer) associated and stored in the currently\-executing event. In some rare cases, when an event is not associated with a specific network node, its ‘context’ is set to 0xffffffff. .sp To associate a context to each event, the Schedule, and ScheduleNow methods automatically reuse the context of the currently\-executing event as the context of the event scheduled for execution later. .sp In some cases, most notably when simulating the transmission of a packet from a node to another, this behavior is undesirable since the expected context of the reception event is that of the receiving node, not the sending node. To avoid this problem, the Simulator class provides a specific schedule method: ScheduleWithContext which allows one to provide explicitly the node id of the receiving node associated with the receive event. .sp \fIXXX: code example\fP .sp In some very rare cases, developers might need to modify or understand how the context (node id) of the first event is set to that of its associated node. This is accomplished by the NodeList class: whenever a new node is created, the NodeList class uses ScheduleWithContext to schedule a ‘initialize’ event for this node. The ‘initialize’ event thus executes with a context set to that of the node id and can use the normal variety of Schedule methods. It invokes the Node::Initialize method which propagates the ‘initialize’ event by calling the DoInitialize method for each object associated with the node. The DoInitialize method overridden in some of these objects (most notably in the Application base class) will schedule some events (most notably Application::StartApplication) which will in turn scheduling traffic generation events which will in turn schedule network\-level events. .sp Notes: .INDENT 0.0 .IP \(bu 2 Users need to be careful to propagate DoInitialize methods across objects by calling Initialize explicitly on their member objects .IP \(bu 2 The context id associated with each ScheduleWithContext method has other uses beyond logging: it is used by an experimental branch of ns\-3 to perform parallel simulation on multicore systems using multithreading. .UNINDENT .sp The Simulator::* functions do not know what the context is: they merely make sure that whatever context you specify with ScheduleWithContext is available when the corresponding event executes with ::GetContext. .sp It is up to the models implemented on top of Simulator::* to interpret the context value. In ns\-3, the network models interpret the context as the node id of the node which generated an event. This is why it is important to call ScheduleWithContext in ns3::Channel subclasses because we are generating an event from node i to node j and we want to make sure that the event which will run on node j has the right context. .SS Time .sp \fITo be completed\fP .SS Scheduler .sp \fITo be completed\fP .SH CALLBACKS .sp Some new users to \fIns\-3\fP are unfamiliar with an extensively used programming idiom used throughout the code: the \fIns\-3 callback\fP\&. This chapter provides some motivation on the callback, guidance on how to use it, and details on its implementation. .SS Callbacks Motivation .sp 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 knowledgeable about the other, so that they can invoke methods on each other: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class A { public: void ReceiveInput ( // parameters ); ... } (in another source file:) class B { public: void DoSomething (void); ... private: A* a_instance; // pointer to an A } void B::DoSomething() { // Tell a_instance that something happened a_instance\->ReceiveInput ( // parameters); ... } .ft P .fi .UNINDENT .UNINDENT .sp 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 \fBc_instance\fP 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. .sp 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. .sp 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: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\- | TCP | | TCP | \-\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\- | becomes \-> | \-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\- | IP | | IPsec | \-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\- | \-\-\-\-\-\-\-\-\-\-\- | IP | \-\-\-\-\-\-\-\-\-\-\- .ft P .fi .UNINDENT .UNINDENT .sp 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. This is clearly not an optimal way to design a generic simulator. .SS Callbacks Background .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Readers familiar with programming callbacks may skip this tutorial section. .UNINDENT .UNINDENT .sp The basic mechanism that allows one to address the problem above is known as a \fIcallback\fP\&. The ultimate goal is to allow one piece of code to call a function (or method in C++) without any specific inter\-module dependency. .sp This ultimately means you need some kind of indirection – you treat the address of the called function as a variable. This variable is called a pointer\-to\-function variable. The relationship between function and pointer\-to\-function pointer is really no different that that of object and pointer\-to\-object. .sp In C the canonical example of a pointer\-to\-function is a pointer\-to\-function\-returning\-integer (PFI). For a PFI taking one int parameter, this could be declared like,: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int (*pfi)(int arg) = 0; .ft P .fi .UNINDENT .UNINDENT .sp What you get from this is a variable named simply \fBpfi\fP that is initialized to the value 0. If you want to initialize this pointer to something meaningful, you have to have a function with a matching signature. In this case: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int MyFunction (int arg) {} .ft P .fi .UNINDENT .UNINDENT .sp If you have this target, you can initialize the variable to point to your function like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C pfi = MyFunction; .ft P .fi .UNINDENT .UNINDENT .sp You can then call MyFunction indirectly using the more suggestive form of the call: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int result = (*pfi) (1234); .ft P .fi .UNINDENT .UNINDENT .sp This is suggestive since it looks like you are dereferencing the function pointer just like you would dereference any pointer. Typically, however, people take advantage of the fact that the compiler knows what is going on and will just use a shorter form: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int result = pfi (1234); .ft P .fi .UNINDENT .UNINDENT .sp Notice that the function pointer obeys value semantics, so you can pass it around like any other value. Typically, when you use an asynchronous interface you will pass some entity like this to a function which will perform an action and \fIcall back\fP to let you know it completed. It calls back by following the indirection and executing the provided function. .sp In C++ you have the added complexity of objects. The analogy with the PFI above means you have a pointer to a member function returning an int (PMI) instead of the pointer to function returning an int (PFI). .sp The declaration of the variable providing the indirection looks only slightly different: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int (MyClass::*pmi) (int arg) = 0; .ft P .fi .UNINDENT .UNINDENT .sp This declares a variable named \fBpmi\fP just as the previous example declared a variable named \fBpfi\fP\&. Since the will be to call a method of an instance of a particular class, one must declare that method in a class: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class MyClass { public: int MyMethod (int arg); }; .ft P .fi .UNINDENT .UNINDENT .sp Given this class declaration, one would then initialize that variable like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C pmi = &MyClass::MyMethod; .ft P .fi .UNINDENT .UNINDENT .sp This assigns the address of the code implementing the method to the variable, completing the indirection. In order to call a method, the code needs a \fBthis\fP pointer. This, in turn, means there must be an object of MyClass to refer to. A simplistic example of this is just calling a method indirectly (think virtual function): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int (MyClass::*pmi) (int arg) = 0; // Declare a PMI pmi = &MyClass::MyMethod; // Point at the implementation code MyClass myClass; // Need an instance of the class (myClass.*pmi) (1234); // Call the method with an object ptr .ft P .fi .UNINDENT .UNINDENT .sp Just like in the C example, you can use this in an asynchronous call to another module which will \fIcall back\fP using a method and an object pointer. The straightforward extension one might consider is to pass a pointer to the object and the PMI variable. The module would just do: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C (*objectPtr.*pmi) (1234); .ft P .fi .UNINDENT .UNINDENT .sp to execute the callback on the desired object. .sp One might ask at this time, \fIwhat’s the point\fP? The called module will have to understand the concrete type of the calling object in order to properly make the callback. Why not just accept this, pass the correctly typed object pointer and do \fBobject\->Method(1234)\fP in the code instead of the callback? This is precisely the problem described above. What is needed is a way to decouple the calling function from the called class completely. This requirement led to the development of the \fIFunctor\fP\&. .sp A functor is the outgrowth of something invented in the 1960s called a closure. It is basically just a packaged\-up function call, possibly with some state. .sp A functor has two parts, a specific part and a generic part, related through inheritance. The calling code (the code that executes the callback) will execute a generic overloaded \fBoperator ()\fP of a generic functor to cause the callback to be called. The called code (the code that wants to be called back) will have to provide a specialized implementation of the \fBoperator ()\fP that performs the class\-specific work that caused the close\-coupling problem above. .sp With the specific functor and its overloaded \fBoperator ()\fP created, the called code then gives the specialized code to the module that will execute the callback (the calling code). .sp The calling code will take a generic functor as a parameter, so an implicit cast is done in the function call to convert the specific functor to a generic functor. This means that the calling module just needs to understand the generic functor type. It is decoupled from the calling code completely. .sp The information one needs to make a specific functor is the object pointer and the pointer\-to\-method address. .sp The essence of what needs to happen is that the system declares a generic part of the functor: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C template class Functor { public: virtual int operator() (T arg) = 0; }; .ft P .fi .UNINDENT .UNINDENT .sp The caller defines a specific part of the functor that really is just there to implement the specific \fBoperator()\fP method: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C template class SpecificFunctor : public Functor { public: SpecificFunctor(T* p, int (T::*_pmi)(ARG arg)) { m_p = p; m_pmi = _pmi; } virtual int operator() (ARG arg) { (*m_p.*m_pmi)(arg); } private: int (T::*m_pmi)(ARG arg); T* m_p; }; .ft P .fi .UNINDENT .UNINDENT .sp Here is an example of the usage: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class A { public: A (int a0) : a (a0) {} int Hello (int b0) { std::cout << "Hello from A, a = " << a << " b0 = " << b0 << std::endl; } int a; }; int main() { A a(10); SpecificFunctor sf(&a, &A::Hello); sf(5); } .ft P .fi .UNINDENT .UNINDENT .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 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. .UNINDENT .UNINDENT .sp 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. .sp Notice that when \fBoperator()\fP is called, it in turn calls the method provided with the object pointer using the C++ PMI syntax. .sp To use this, one could then declare some model code that takes a generic functor as a parameter: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C void LibraryFunction (Functor functor); .ft P .fi .UNINDENT .UNINDENT .sp The code that will talk to the model would build a specific functor and pass it to \fBLibraryFunction\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C MyClass myClass; SpecificFunctor functor (&myclass, MyClass::MyMethod); .ft P .fi .UNINDENT .UNINDENT .sp When \fBLibraryFunction\fP is done, it executes the callback using the \fBoperator()\fP on the generic functor it was passed, and in this particular case, provides the integer argument: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C void LibraryFunction (Functor functor) { // Execute the library function functor(1234); } .ft P .fi .UNINDENT .UNINDENT .sp Notice that \fBLibraryFunction\fP is completely decoupled from the specific type of the client. The connection is made through the Functor polymorphism. .sp The Callback API in \fIns\-3\fP 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 \fIns\-3\fP\&. .SS Using the Callback API .sp The Callback API is fairly minimal, providing only two services: .sp 1. callback type declaration: a way to declare a type of callback with a given signature, and, .sp 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. .sp This is best observed via walking through an example, based on \fBsamples/main\-callback.cc\fP\&. .SS Using the Callback API with static functions .sp Consider a function: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C static double CbOne (double a, double b) { std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl; return a; } .ft P .fi .UNINDENT .UNINDENT .sp Consider also the following main program snippet: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int main (int argc, char *argv[]) { // return type: double // first arg type: double // second arg type: double Callback one; } .ft P .fi .UNINDENT .UNINDENT .sp This is an example of a C\-style callback – one which does not include or need a \fBthis\fP pointer. The function template \fBCallback\fP 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 \fBCallback\fP template function is a generic version of that – it is used to declare the type of a callback. .sp \fBNOTE:\fP .INDENT 0.0 .INDENT 3.5 Readers unfamiliar with C++ templates may consult \fI\%http://www.cplusplus.com/doc/tutorial/templates/\fP\&. .UNINDENT .UNINDENT .sp The \fBCallback\fP 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). .sp 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 \fBsrc/core/examples/main\-callback.cc\fP demonstrates both of these error cases at the end of the \fBmain()\fP program. .sp 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: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C static double CbOne (double a, double b) {} ^ ^ ^ | | | | | | Callback one; .ft P .fi .UNINDENT .UNINDENT .sp 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. .sp Now, let’s bind our callback “one” to the function that matches its signature: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C // build callback instance which points to cbOne function one = MakeCallback (&CbOne); .ft P .fi .UNINDENT .UNINDENT .sp This call to \fBMakeCallback\fP is, in essence, creating one of the specialized functors mentioned above. The variable declared using the \fBCallback\fP template function is going to be playing the part of the generic functor. The assignment \fBone = MakeCallback (&CbOne)\fP is the cast that converts the specialized functor known to the callee to a generic functor known to the caller. .sp Then, later in the program, if the callback is needed, it can be used as follows: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C NS_ASSERT (!one.IsNull ()); // invoke cbOne function through callback instance double retOne; retOne = one (10.0, 20.0); .ft P .fi .UNINDENT .UNINDENT .sp The check for \fBIsNull()\fP ensures that the callback is not null – that there is a function to call behind this callback. Then, \fBone()\fP executes the generic \fBoperator()\fP which is really overloaded with a specific implementation of \fBoperator()\fP and returns the same result as if \fBCbOne()\fP had been called directly. .SS Using the Callback API with member functions .sp 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: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 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); ... } .ft P .fi .UNINDENT .UNINDENT .sp Here, we pass an additional object pointer to the \fBMakeCallback<>\fP function. Recall from the background section above that \fBOperator()\fP will use the pointer to member syntax when it executes on an object: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C virtual int operator() (ARG arg) { (*m_p.*m_pmi)(arg); } .ft P .fi .UNINDENT .UNINDENT .sp And so we needed to provide the two variables (\fBm_p\fP and \fBm_pmi\fP) when we made the specific functor. The line: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C two = MakeCallback (&MyCb::CbTwo, &cb); .ft P .fi .UNINDENT .UNINDENT .sp does precisely that. In this case, when \fBtwo ()\fP is invoked: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int result = two (1.0); .ft P .fi .UNINDENT .UNINDENT .sp will result in a call to the \fBCbTwo\fP member function (method) on the object pointed to by \fB&cb\fP\&. .SS Building Null Callbacks .sp 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 \fBMakeNullCallback<>\fP construct: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C two = MakeNullCallback (); NS_ASSERT (two.IsNull ()); .ft P .fi .UNINDENT .UNINDENT .sp Invoking a null callback is just like invoking a null function pointer: it will crash at runtime. .SS Bound Callbacks .sp 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 \fBCallback\fP is called via \fBoperator()\fP\&. All of the parameters are provided by the calling function. .sp What if it is desired to allow the client function (the one that provides the callback) to provide some of the parameters? \fI\%Alexandrescu\fP calls the process of allowing a client to specify one of the parameters \fI“binding”\fP\&. One of the parameters of \fBoperator()\fP has been bound (fixed) by the client. .sp 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: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C static void DefaultSink (Ptr file, Ptr p); .ft P .fi .UNINDENT .UNINDENT .sp The static keyword means this is a static function which does not need a \fBthis\fP 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: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C m_promiscSnifferTrace (m_currentPkt); .ft P .fi .UNINDENT .UNINDENT .sp What we want to do is to \fIbind\fP the \fBPtr file\fP to the specific callback implementation when it is created and arrange for the \fBoperator()\fP of the Callback to provide that parameter for free. .sp We provide the \fBMakeBoundCallback\fP template function for that purpose. It takes the same parameters as the \fBMakeCallback\fP template function but also takes the parameters to be bound. In the case of the example above: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C MakeBoundCallback (&DefaultSink, file); .ft P .fi .UNINDENT .UNINDENT .sp 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: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C 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; }; .ft P .fi .UNINDENT .UNINDENT .sp You can see that when the specific functor is created, the bound argument is saved in the functor / callback object itself. When the \fBoperator()\fP is invoked with the single parameter, as in: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C m_promiscSnifferTrace (m_currentPkt); .ft P .fi .UNINDENT .UNINDENT .sp the implementation of \fBoperator()\fP adds the bound parameter into the actual function call: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C (*m_p.*m_pmi)(m_boundArg, arg); .ft P .fi .UNINDENT .UNINDENT .sp It’s possible to bind two or three arguments as well. Say we have a function with signature: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C static void NotifyEvent (Ptr a, Ptr b, MyEventType e); .ft P .fi .UNINDENT .UNINDENT .sp One can create bound callback binding first two arguments like: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C MakeBoundCallback (&NotifyEvent, a1, b1); .ft P .fi .UNINDENT .UNINDENT .sp assuming \fIa1\fP and \fIb1\fP are objects of type \fIA\fP and \fIB\fP respectively. Similarly for three arguments one would have function with a signature: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C static void NotifyEvent (Ptr a, Ptr b, MyEventType e); .ft P .fi .UNINDENT .UNINDENT .sp Binding three arguments in done with: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C MakeBoundCallback (&NotifyEvent, a1, b1, c1); .ft P .fi .UNINDENT .UNINDENT .sp again assuming \fIa1\fP, \fIb1\fP and \fIc1\fP are objects of type \fIA\fP, \fIB\fP and \fIC\fP respectively. .sp This kind of binding can be used for exchanging information between objects in simulation; specifically, bound callbacks can be used as traced callbacks, which will be described in the next section. .SS Traced Callbacks .sp \fIPlaceholder subsection\fP .SS Callback locations in ns\-3 .sp Where are callbacks frequently used in \fIns\-3\fP? Here are some of the more visible ones to typical users: .INDENT 0.0 .IP \(bu 2 Socket API .IP \(bu 2 Layer\-2/Layer\-3 API .IP \(bu 2 Tracing subsystem .IP \(bu 2 API between IP and routing subsystems .UNINDENT .SS Implementation details .sp 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. .sp The code was originally written based on the techniques described in \fI\%http://www.codeproject.com/cpp/TTLFunction.asp\fP\&. It was subsequently rewritten to follow the architecture outlined in \fI\%Modern C++ Design, Generic Programming and Design Patterns Applied, Alexandrescu, chapter 5, Generalized Functors\fP\&. .sp This code uses: .INDENT 0.0 .IP \(bu 2 default template parameters to saves users from having to specify empty parameters when the number of parameters is smaller than the maximum supported number .IP \(bu 2 the pimpl idiom: the Callback class is passed around by value and delegates the crux of the work to its pimpl pointer. .IP \(bu 2 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. .IP \(bu 2 a reference list implementation to implement the Callback’s value semantics. .UNINDENT .sp 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. .SH OBJECT MODEL .sp \fIns\-3\fP is fundamentally a C++ object system. Objects can be declared and instantiated as usual, per C++ rules. \fIns\-3\fP also adds some features to traditional C++ objects, as described below, to provide greater functionality and features. This manual chapter is intended to introduce the reader to the \fIns\-3\fP object model. .sp This section describes the C++ class design for \fIns\-3\fP objects. In brief, several design patterns in use include classic object\-oriented design (polymorphic interfaces and implementations), separation of interface and implementation, the non\-virtual public interface design pattern, an object aggregation facility, and reference counting for memory management. Those familiar with component models such as COM or Bonobo will recognize elements of the design in the \fIns\-3\fP object aggregation model, although the \fIns\-3\fP design is not strictly in accordance with either. .SS Object\-oriented behavior .sp C++ objects, in general, provide common object\-oriented capabilities (abstraction, encapsulation, inheritance, and polymorphism) that are part of classic object\-oriented design. \fIns\-3\fP objects make use of these properties; for instance: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Address { public: Address (); Address (uint8_t type, const uint8_t *buffer, uint8_t len); Address (const Address & address); Address &operator = (const Address &address); ... private: uint8_t m_type; uint8_t m_len; ... }; .ft P .fi .UNINDENT .UNINDENT .SS Object base classes .sp There are three special base classes used in \fIns\-3\fP\&. Classes that inherit from these base classes can instantiate objects with special properties. These base classes are: .INDENT 0.0 .IP \(bu 2 class \fBObject\fP .IP \(bu 2 class \fBObjectBase\fP .IP \(bu 2 class \fBSimpleRefCount\fP .UNINDENT .sp It is not required that \fIns\-3\fP objects inherit from these class, but those that do get special properties. Classes deriving from class \fBObject\fP get the following properties. .INDENT 0.0 .IP \(bu 2 the \fIns\-3\fP type and attribute system (see \fI\%Configuration and Attributes\fP) .IP \(bu 2 an object aggregation system .IP \(bu 2 a smart\-pointer reference counting system (class Ptr) .UNINDENT .sp Classes that derive from class \fBObjectBase\fP get the first two properties above, but do not get smart pointers. Classes that derive from class \fBSimpleRefCount\fP: get only the smart\-pointer reference counting system. .sp In practice, class \fBObject\fP is the variant of the three above that the \fIns\-3\fP developer will most commonly encounter. .SS Memory management and class Ptr .sp Memory management in a C++ program is a complex process, and is often done incorrectly or inconsistently. We have settled on a reference counting design described as follows. .sp All objects using reference counting maintain an internal reference count to determine when an object can safely delete itself. Each time that a pointer is obtained to an interface, the object’s reference count is incremented by calling \fBRef()\fP\&. It is the obligation of the user of the pointer to explicitly \fBUnref()\fP the pointer when done. When the reference count falls to zero, the object is deleted. .INDENT 0.0 .IP \(bu 2 When the client code obtains a pointer from the object itself through object creation, or via GetObject, it does not have to increment the reference count. .IP \(bu 2 When client code obtains a pointer from another source (e.g., copying a pointer) it must call \fBRef()\fP to increment the reference count. .IP \(bu 2 All users of the object pointer must call \fBUnref()\fP to release the reference. .UNINDENT .sp The burden for calling \fBUnref()\fP is somewhat relieved by the use of the reference counting smart pointer class described below. .sp Users using a low\-level API who wish to explicitly allocate non\-reference\-counted objects on the heap, using operator new, are responsible for deleting such objects. .SS Reference counting smart pointer (Ptr) .sp Calling \fBRef()\fP and \fBUnref()\fP all the time would be cumbersome, so \fIns\-3\fP provides a smart pointer class \fBPtr\fP similar to \fBBoost::intrusive_ptr\fP\&. This smart\-pointer class assumes that the underlying type provides a pair of \fBRef\fP and \fBUnref\fP methods that are expected to increment and decrement the internal refcount of the object instance. .sp This implementation allows you to manipulate the smart pointer as if it was a normal pointer: you can compare it with zero, compare it against other pointers, assign zero to it, etc. .sp It is possible to extract the raw pointer from this smart pointer with the \fBGetPointer()\fP and \fBPeekPointer()\fP methods. .sp If you want to store a newed object into a smart pointer, we recommend you to use the CreateObject template functions to create the object and store it in a smart pointer to avoid memory leaks. These functions are really small convenience functions and their goal is just to save you a small bit of typing. .SS CreateObject and Create .sp Objects in C++ may be statically, dynamically, or automatically created. This holds true for \fIns\-3\fP also, but some objects in the system have some additional frameworks available. Specifically, reference counted objects are usually allocated using a templated Create or CreateObject method, as follows. .sp For objects deriving from class \fBObject\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Ptr device = CreateObject (); .ft P .fi .UNINDENT .UNINDENT .sp Please do not create such objects using \fBoperator new\fP; create them using \fBCreateObject()\fP instead. .sp For objects deriving from class \fBSimpleRefCount\fP, or other objects that support usage of the smart pointer class, a templated helper function is available and recommended to be used: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Ptr b = Create (); .ft P .fi .UNINDENT .UNINDENT .sp This is simply a wrapper around operator new that correctly handles the reference counting system. .sp In summary, use \fBCreate\fP if B is not an object but just uses reference counting (e.g. \fBPacket\fP), and use \fBCreateObject\fP if B derives from \fBns3::Object\fP\&. .SS Aggregation .sp The \fIns\-3\fP object aggregation system is motivated in strong part by a recognition that a common use case for \fIns\-2\fP has been the use of inheritance and polymorphism to extend protocol models. For instance, specialized versions of TCP such as RenoTcpAgent derive from (and override functions from) class TcpAgent. .sp However, two problems that have arisen in the \fIns\-2\fP model are downcasts and “weak base class.” Downcasting refers to the procedure of using a base class pointer to an object and querying it at run time to find out type information, used to explicitly cast the pointer to a subclass pointer so that the subclass API can be used. Weak base class refers to the problems that arise when a class cannot be effectively reused (derived from) because it lacks necessary functionality, leading the developer to have to modify the base class and causing proliferation of base class API calls, some of which may not be semantically correct for all subclasses. .sp \fIns\-3\fP is using a version of the query interface design pattern to avoid these problems. This design is based on elements of the \fI\%Component Object Model\fP and \fI\%GNOME Bonobo\fP although full binary\-level compatibility of replaceable components is not supported and we have tried to simplify the syntax and impact on model developers. .SS Examples .SS Aggregation example .sp \fBNode\fP is a good example of the use of aggregation in \fIns\-3\fP\&. Note that there are not derived classes of Nodes in \fIns\-3\fP such as class \fBInternetNode\fP\&. Instead, components (protocols) are aggregated to a node. Let’s look at how some Ipv4 protocols are added to a node.: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C static void AddIpv4Stack(Ptr node) { Ptr ipv4 = CreateObject (); ipv4\->SetNode (node); node\->AggregateObject (ipv4); Ptr ipv4Impl = CreateObject (); ipv4Impl\->SetIpv4 (ipv4); node\->AggregateObject (ipv4Impl); } .ft P .fi .UNINDENT .UNINDENT .sp Note that the Ipv4 protocols are created using \fBCreateObject()\fP\&. Then, they are aggregated to the node. In this manner, the Node base class does not need to be edited to allow users with a base class Node pointer to access the Ipv4 interface; users may ask the node for a pointer to its Ipv4 interface at runtime. How the user asks the node is described in the next subsection. .sp Note that it is a programming error to aggregate more than one object of the same type to an \fBns3::Object\fP\&. So, for instance, aggregation is not an option for storing all of the active sockets of a node. .SS GetObject example .sp GetObject is a type\-safe way to achieve a safe downcasting and to allow interfaces to be found on an object. .sp Consider a node pointer \fBm_node\fP that points to a Node object that has an implementation of IPv4 previously aggregated to it. The client code wishes to configure a default route. To do so, it must access an object within the node that has an interface to the IP forwarding configuration. It performs the following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Ptr ipv4 = m_node\->GetObject (); .ft P .fi .UNINDENT .UNINDENT .sp If the node in fact does not have an Ipv4 object aggregated to it, then the method will return null. Therefore, it is good practice to check the return value from such a function call. If successful, the user can now use the Ptr to the Ipv4 object that was previously aggregated to the node. .sp Another example of how one might use aggregation is to add optional models to objects. For instance, an existing Node object may have an “Energy Model” object aggregated to it at run time (without modifying and recompiling the node class). An existing model (such as a wireless net device) can then later “GetObject” for the energy model and act appropriately if the interface has been either built in to the underlying Node object or aggregated to it at run time. However, other nodes need not know anything about energy models. .sp We hope that this mode of programming will require much less need for developers to modify the base classes. .SS Object factories .sp A common use case is to create lots of similarly configured objects. One can repeatedly call \fBCreateObject()\fP but there is also a factory design pattern in use in the \fIns\-3\fP system. It is heavily used in the “helper” API. .sp Class \fBObjectFactory\fP can be used to instantiate objects and to configure the attributes on those objects: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C void SetTypeId (TypeId tid); void Set (std::string name, const AttributeValue &value); Ptr Create (void) const; .ft P .fi .UNINDENT .UNINDENT .sp The first method allows one to use the \fIns\-3\fP TypeId system to specify the type of objects created. The second allows one to set attributes on the objects to be created, and the third allows one to create the objects themselves. .sp For example: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ObjectFactory factory; // Make this factory create objects of type FriisPropagationLossModel factory.SetTypeId ("ns3::FriisPropagationLossModel") // Make this factory object change a default value of an attribute, for // subsequently created objects factory.Set ("SystemLoss", DoubleValue (2.0)); // Create one such object Ptr object = factory.Create (); factory.Set ("SystemLoss", DoubleValue (3.0)); // Create another object with a different SystemLoss Ptr object = factory.Create (); .ft P .fi .UNINDENT .UNINDENT .SS Downcasting .sp A question that has arisen several times is, “If I have a base class pointer (Ptr) to an object and I want the derived class pointer, should I downcast (via C++ dynamic cast) to get the derived pointer, or should I use the object aggregation system to \fBGetObject<> ()\fP to find a Ptr to the interface to the subclass API?” .sp The answer to this is that in many situations, both techniques will work. \fIns\-3\fP provides a templated function for making the syntax of Object dynamic casting much more user friendly: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C template Ptr DynamicCast (Ptr const&p) { return Ptr (dynamic_cast (PeekPointer (p))); } .ft P .fi .UNINDENT .UNINDENT .sp DynamicCast works when the programmer has a base type pointer and is testing against a subclass pointer. GetObject works when looking for different objects aggregated, but also works with subclasses, in the same way as DynamicCast. If unsure, the programmer should use GetObject, as it works in all cases. If the programmer knows the class hierarchy of the object under consideration, it is more direct to just use DynamicCast. .SH CONFIGURATION AND ATTRIBUTES .sp In \fIns\-3\fP simulations, there are two main aspects to configuration: .INDENT 0.0 .IP \(bu 2 The simulation topology and how objects are connected. .IP \(bu 2 The values used by the models instantiated in the topology. .UNINDENT .sp This chapter focuses on the second item above: how the many values in use in \fIns\-3\fP are organized, documented, and modifiable by \fIns\-3\fP users. The \fIns\-3\fP attribute system is also the underpinning of how traces and statistics are gathered in the simulator. .sp In the course of this chapter we will discuss the various ways to set or modify the values used by \fIns\-3\fP model objects. In increasing order of specificity, these are: .TS center; |l|l|. _ T{ Method T} T{ Scope T} _ T{ Default Attribute values set when Attributes are defined in \fBGetTypeId ()\fP\&. T} T{ Affect all instances of the class. T} _ T{ \fBCommandLine\fP \fBConfig::SetDefault()\fP \fBConfigStore\fP T} T{ Affect all future instances. T} _ T{ \fBObjectFactory\fP T} T{ Affects all instances created with the factory. T} _ T{ Helper methods with (string/ AttributeValue) parameter pairs T} T{ Affects all instances created by the helper. T} _ T{ \fBMyClass::SetX ()\fP \fBObject::SetAttribute ()\fP \fBConfig::Set()\fP T} T{ Alters this particular instance. Generally this is the only form which can be scheduled to alter an instance once the simulation is running. T} _ .TE .sp By “specificity” we mean that methods in later rows in the table override the values set by, and typically affect fewer instances than, earlier methods. .sp Before delving into details of the attribute value system, it will help to review some basic properties of class \fBObject\fP\&. .SS Object Overview .sp \fIns\-3\fP is fundamentally a C++ object\-based system. By this we mean that new C++ classes (types) can be declared, defined, and subclassed as usual. .sp Many \fIns\-3\fP objects inherit from the \fBObject\fP base class. These objects have some additional properties that we exploit for organizing the system and improving the memory management of our objects: .INDENT 0.0 .IP \(bu 2 “Metadata” system that links the class name to a lot of meta\-information about the object, including: .INDENT 2.0 .IP \(bu 2 The base class of the subclass, .IP \(bu 2 The set of accessible constructors in the subclass, .IP \(bu 2 The set of “attributes” of the subclass, .IP \(bu 2 Whether each attribute can be set, or is read\-only, .IP \(bu 2 The allowed range of values for each attribute. .UNINDENT .IP \(bu 2 Reference counting smart pointer implementation, for memory management. .UNINDENT .sp \fIns\-3\fP objects that use the attribute system derive from either \fBObject\fP or \fBObjectBase\fP\&. Most \fIns\-3\fP objects we will discuss derive from \fBObject\fP, but a few that are outside the smart pointer memory management framework derive from \fBObjectBase\fP\&. .sp Let’s review a couple of properties of these objects. .SS Smart Pointers .sp As introduced in the \fIns\-3\fP tutorial, \fIns\-3\fP objects are memory managed by a \fI\%reference counting smart pointer implementation\fP, class \fBPtr\fP\&. .sp Smart pointers are used extensively in the \fIns\-3\fP APIs, to avoid passing references to heap\-allocated objects that may cause memory leaks. For most basic usage (syntax), treat a smart pointer like a regular pointer: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Ptr nd = ...; nd\->CallSomeFunction (); // etc. .ft P .fi .UNINDENT .UNINDENT .sp So how do you get a smart pointer to an object, as in the first line of this example? .SS CreateObject .sp As we discussed above in \fI\%Memory management and class Ptr\fP, at the lowest\-level API, objects of type \fBObject\fP are not instantiated using \fBoperator new\fP as usual but instead by a templated function called \fBCreateObject ()\fP\&. .sp A typical way to create such an object is as follows: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Ptr nd = CreateObject (); .ft P .fi .UNINDENT .UNINDENT .sp You can think of this as being functionally equivalent to: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C WifiNetDevice* nd = new WifiNetDevice (); .ft P .fi .UNINDENT .UNINDENT .sp Objects that derive from \fBObject\fP must be allocated on the heap using \fBCreateObject ()\fP\&. Those deriving from \fBObjectBase\fP, such as \fIns\-3\fP helper functions and packet headers and trailers, can be allocated on the stack. .sp In some scripts, you may not see a lot of \fBCreateObject ()\fP calls in the code; this is because there are some helper objects in effect that are doing the \fBCreateObject ()\fP calls for you. .SS TypeId .sp \fIns\-3\fP classes that derive from class \fBObject\fP can include a metadata class called \fBTypeId\fP that records meta\-information about the class, for use in the object aggregation and component manager systems: .INDENT 0.0 .IP \(bu 2 A unique string identifying the class. .IP \(bu 2 The base class of the subclass, within the metadata system. .IP \(bu 2 The set of accessible constructors in the subclass. .IP \(bu 2 A list of publicly accessible properties (“attributes”) of the class. .UNINDENT .SS Object Summary .sp Putting all of these concepts together, let’s look at a specific example: class \fBNode\fP\&. .sp The public header file \fBnode.h\fP has a declaration that includes a static \fBGetTypeId ()\fP function call: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class Node : public Object { public: static TypeId GetTypeId (void); ... .ft P .fi .UNINDENT .UNINDENT .sp This is defined in the \fBnode.cc\fP file as follows: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C TypeId Node::GetTypeId (void) { static TypeId tid = TypeId ("ns3::Node") .SetParent () .SetGroupName ("Network") .AddConstructor () .AddAttribute ("DeviceList", "The list of devices associated to this Node.", ObjectVectorValue (), MakeObjectVectorAccessor (&Node::m_devices), MakeObjectVectorChecker ()) .AddAttribute ("ApplicationList", "The list of applications associated to this Node.", ObjectVectorValue (), MakeObjectVectorAccessor (&Node::m_applications), MakeObjectVectorChecker ()) .AddAttribute ("Id", "The id (unique integer) of this Node.", TypeId::ATTR_GET, // allow only getting it. UintegerValue (0), MakeUintegerAccessor (&Node::m_id), MakeUintegerChecker ()) ; return tid; } .ft P .fi .UNINDENT .UNINDENT .sp Consider the \fBTypeId\fP of the \fIns\-3\fP \fBObject\fP class as an extended form of run time type information (RTTI). The C++ language includes a simple kind of RTTI in order to support \fBdynamic_cast\fP and \fBtypeid\fP operators. .sp The \fBSetParent ()\fP call in the definition above is used in conjunction with our object aggregation mechanisms to allow safe up\- and down\-casting in inheritance trees during \fBGetObject ()\fP\&. It also enables subclasses to inherit the Attributes of their parent class. .sp The \fBAddConstructor ()\fP call is used in conjunction with our abstract object factory mechanisms to allow us to construct C++ objects without forcing a user to know the concrete class of the object she is building. .sp The three calls to \fBAddAttribute ()\fP associate a given string with a strongly typed value in the class. Notice that you must provide a help string which may be displayed, for example, \fIvia\fP command line processors. Each \fBAttribute\fP is associated with mechanisms for accessing the underlying member variable in the object (for example, \fBMakeUintegerAccessor ()\fP tells the generic \fBAttribute\fP code how to get to the node ID above). There are also “Checker” methods which are used to validate values against range limitations, such as maximum and minimum allowed values. .sp When users want to create Nodes, they will usually call some form of \fBCreateObject ()\fP,: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Ptr n = CreateObject (); .ft P .fi .UNINDENT .UNINDENT .sp or more abstractly, using an object factory, you can create a \fBNode\fP object without even knowing the concrete C++ type: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ObjectFactory factory; const std::string typeId = "ns3::Node\(aq\(aq; factory.SetTypeId (typeId); Ptr node = factory.Create (); .ft P .fi .UNINDENT .UNINDENT .sp Both of these methods result in fully initialized attributes being available in the resulting \fBObject\fP instances. .sp We next discuss how attributes (values associated with member variables or functions of the class) are plumbed into the above \fBTypeId\fP\&. .SS Attributes .sp The goal of the attribute system is to organize the access of internal member objects of a simulation. This goal arises because, typically in simulation, users will cut and paste/modify existing simulation scripts, or will use higher\-level simulation constructs, but often will be interested in studying or tracing particular internal variables. For instance, use cases such as: .INDENT 0.0 .IP \(bu 2 \fI“I want to trace the packets on the wireless interface only on the first access point.”\fP .IP \(bu 2 \fI“I want to trace the value of the TCP congestion window (every time it changes) on a particular TCP socket.”\fP .IP \(bu 2 \fI“I want a dump of all values that were used in my simulation.”\fP .UNINDENT .sp Similarly, users may want fine\-grained access to internal variables in the simulation, or may want to broadly change the initial value used for a particular parameter in all subsequently created objects. Finally, users may wish to know what variables are settable and retrievable in a simulation configuration. This is not just for direct simulation interaction on the command line; consider also a (future) graphical user interface that would like to be able to provide a feature whereby a user might right\-click on an node on the canvas and see a hierarchical, organized list of parameters that are settable on the node and its constituent member objects, and help text and default values for each parameter. .SS Defining Attributes .sp We provide a way for users to access values deep in the system, without having to plumb accessors (pointers) through the system and walk pointer chains to get to them. Consider a class \fBQueueBase\fP that has a member variable \fBm_maxSize\fP controlling the depth of the queue. .sp If we look at the declaration of \fBQueueBase\fP, we see the following: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class QueueBase : public Object { public: static TypeId GetTypeId (void); ... private: ... QueueSize m_maxSize; //!< max queue size ... }; .ft P .fi .UNINDENT .UNINDENT .sp \fBQueueSize\fP is a special type in \fIns\-3\fP that allows size to be represented in different units: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C enum QueueSizeUnit { PACKETS, /**< Use number of packets for queue size */ BYTES, /**< Use number of bytes for queue size */ }; class QueueSize { ... private: ... QueueSizeUnit m_unit; //!< unit uint32_t m_value; //!< queue size [bytes or packets] }; .ft P .fi .UNINDENT .UNINDENT .sp Finally, the class \fBDropTailQueue\fP inherits from this base class and provides the semantics that packets that are submitted to a full queue will be dropped from the back of the queue (“drop tail”). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C /** * \eingroup queue * * \ebrief A FIFO packet queue that drops tail\-end packets on overflow */ template class DropTailQueue : public Queue .ft P .fi .UNINDENT .UNINDENT .sp Let’s consider things that a user may want to do with the value of \fBm_maxSize\fP: .INDENT 0.0 .IP \(bu 2 Set a default value for the system, such that whenever a new \fBDropTailQueue\fP is created, this member is initialized to that default. .IP \(bu 2 Set or get the value on an already instantiated queue. .UNINDENT .sp The above things typically require providing \fBSet ()\fP and \fBGet ()\fP functions, and some type of global default value. .sp In the \fIns\-3\fP attribute system, these value definitions and accessor function registrations are moved into the \fBTypeId\fP class; \fIe.g\fP\&.: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C NS_OBJECT_ENSURE_REGISTERED (QueueBase); TypeId QueueBase::GetTypeId (void) { static TypeId tid = TypeId ("ns3::DropTailQueue") .SetParent () .SetGroupName ("Network") ... .AddAttribute ("MaxSize", "The max queue size", QueueSizeValue (QueueSize ("100p")), MakeQueueSizeAccessor (&QueueBase::SetMaxSize, &QueueBase::GetMaxSize), MakeQueueSizeChecker ()) ... ; return tid; } .ft P .fi .UNINDENT .UNINDENT .sp The \fBAddAttribute ()\fP method is performing a number of things for the \fBm_maxSize\fP value: .INDENT 0.0 .IP \(bu 2 Binding the (usually private) member variable \fBm_maxSize\fP to a public string \fB"MaxSize"\fP\&. .IP \(bu 2 Providing a default value (0 packets). .IP \(bu 2 Providing some help text defining the meaning of the value. .IP \(bu 2 Providing a “Checker” (not used in this example) that can be used to set bounds on the allowable range of values. .UNINDENT .sp The key point is that now the value of this variable and its default value are accessible in the attribute namespace, which is based on strings such as \fB"MaxSize"\fP and \fBTypeId\fP name strings. In the next section, we will provide an example script that shows how users may manipulate these values. .sp Note that initialization of the attribute relies on the macro \fBNS_OBJECT_ENSURE_REGISTERED (QueueBase)\fP being called; if you leave this out of your new class implementation, your attributes will not be initialized correctly. .sp While we have described how to create attributes, we still haven’t described how to access and manage these values. For instance, there is no \fBglobals.h\fP header file where these are stored; attributes are stored with their classes. Questions that naturally arise are how do users easily learn about all of the attributes of their models, and how does a user access these attributes, or document their values as part of the record of their simulation? .sp Detailed documentation of the actual attributes defined for a type, and a global list of all defined attributes, are available in the API documentation. For the rest of this document we are going to demonstrate the various ways of getting and setting attribute values. .SS Setting Default Values .SS Config::SetDefault and CommandLine .sp Let’s look at how a user script might access a specific attribute value. We’re going to use the \fBsrc/point\-to\-point/examples/main\-attribute\-value.cc\fP script for illustration, with some details stripped out. The \fBmain\fP function begins: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C // This is a basic example of how to use the attribute system to // set and get a value in the underlying system; namely, the maximum // size of the FIFO queue in the PointToPointNetDevice // int main (int argc, char *argv[]) { // Queues in ns\-3 are objects that hold items (other objects) in // a queue structure. The C++ implementation uses templates to // allow queues to hold various types of items, but the most // common is a pointer to a packet (Ptr). // // The maximum queue size can either be enforced in bytes (\(aqb\(aq) or // packets (\(aqp\(aq). A special type called the ns3::QueueSize can // hold queue size values in either unit (bytes or packets). The // queue base class ns3::QueueBase has a MaxSize attribute that can // be set to a QueueSize. // By default, the MaxSize attribute has a value of 100 packets (\(aq100p\(aq) // (this default can be observed in the function QueueBase::GetTypeId) // // Here, we set it to 80 packets. We could use one of two value types: // a string\-based value or a QueueSizeValue value Config::SetDefault ("ns3::QueueBase::MaxSize", StringValue ("80p")); // The below function call is redundant Config::SetDefault ("ns3::QueueBase::MaxSize", QueueSizeValue (QueueSize (QueueSizeUnit::PACKETS, 80))); .ft P .fi .UNINDENT .UNINDENT .sp The main thing to notice in the above are the two equivalent calls to \fBConfig::SetDefault ()\fP\&. This is how we set the default value for all subsequently instantiated \fBDropTailQueue\fPs. We illustrate that two types of \fBValue\fP classes, a \fBStringValue\fP and a \fBQueueSizeValue\fP class, can be used to assign the value to the attribute named by “ns3::QueueBase::MaxSize”. .sp It is also possible to manipulate Attributes using the \fBCommandLine\fP; we saw some examples early in the \fIns\-3\fP Tutorial. In particular, it is straightforward to add a shorthand argument name, such as \fB\-\-maxSize\fP, for an Attribute that is particular relevant for your model, in this case \fB"ns3::QueueBase::MaxSize"\fP\&. This has the additional feature that the help string for the Attribute will be printed as part of the usage message for the script. For more information see the \fBCommandLine\fP API documentation. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C // Allow the user to override any of the defaults and the above // SetDefaults() at run\-time, via command\-line arguments // For example, via "\-\-ns3::QueueBase::MaxSize=80p" CommandLine cmd; // This provides yet another way to set the value from the command line: cmd.AddValue ("maxSize", "ns3::QueueBase::MaxSize"); cmd.Parse (argc, argv); .ft P .fi .UNINDENT .UNINDENT .sp Now, we will create a few objects using the low\-level API. Our newly created queues will not have \fBm_maxSize\fP initialized to 0 packets, as defined in the \fBQueueBase::GetTypeId ()\fP function, but to 80 packets, because of what we did above with default values.: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Ptr n0 = CreateObject (); Ptr net0 = CreateObject (); n0\->AddDevice (net0); Ptr > q = CreateObject > (); net0\->AddQueue(q); .ft P .fi .UNINDENT .UNINDENT .sp At this point, we have created a single \fBNode\fP (\fBn0\fP) and a single \fBPointToPointNetDevice\fP (\fBnet0\fP), added a \fBDropTailQueue\fP (\fBq\fP) to \fBnet0\fP, which will be configured with a queue size limit of 80 packets. .sp As a final note, the \fIConfig::Set…()\fP functions will throw an error if the targeted Attribute does not exist at the path given. There are also “fail\-safe” versions, \fIConfig::Set…FailSafe()\fP, if you can’t be sure the Attribute exists. The fail\-safe versions return \fItrue\fP if at least one instance could be set. .SS Constructors, Helpers and ObjectFactory .sp Arbitrary combinations of attributes can be set and fetched from the helper and low\-level APIs; either from the constructors themselves: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Ptr p = CreateObjectWithAttributes ("MinX", DoubleValue (\-100.0), "MinY", DoubleValue (\-100.0), "DeltaX", DoubleValue (5.0), "DeltaY", DoubleValue (20.0), "GridWidth", UintegerValue (20), "LayoutType", StringValue ("RowFirst")); .ft P .fi .UNINDENT .UNINDENT .sp or from the higher\-level helper APIs, such as: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C mobility.SetPositionAllocator ("ns3::GridPositionAllocator", "MinX", DoubleValue (\-100.0), "MinY", DoubleValue (\-100.0), "DeltaX", DoubleValue (5.0), "DeltaY", DoubleValue (20.0), "GridWidth", UintegerValue (20), "LayoutType", StringValue ("RowFirst")); .ft P .fi .UNINDENT .UNINDENT .sp We don’t illustrate it here, but you can also configure an \fBObjectFactory\fP with new values for specific attributes. Instances created by the \fBObjectFactory\fP will have those attributes set during construction. This is very similar to using one of the helper APIs for the class. .sp To review, there are several ways to set values for attributes for class instances \fIto be created in the future:\fP .INDENT 0.0 .IP \(bu 2 \fBConfig::SetDefault ()\fP .IP \(bu 2 \fBCommandLine::AddValue ()\fP .IP \(bu 2 \fBCreateObjectWithAttributes<> ()\fP .IP \(bu 2 Various helper APIs .UNINDENT .sp But what if you’ve already created an instance, and you want to change the value of the attribute? In this example, how can we manipulate the \fBm_maxSize\fP value of the already instantiated \fBDropTailQueue\fP? Here are various ways to do that. .SS Changing Values .SS SmartPointer .sp Assume that a smart pointer (\fBPtr\fP) to a relevant network device is in hand; in the current example, it is the \fBnet0\fP pointer. .sp One way to change the value is to access a pointer to the underlying queue and modify its attribute. .sp First, we observe that we can get a pointer to the (base class) \fBQueue\fP \fIvia\fP the \fBPointToPointNetDevice\fP attributes, where it is called \fB"TxQueue"\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C PointerValue ptr; net0\->GetAttribute ("TxQueue", ptr); Ptr > txQueue = ptr.Get > (); .ft P .fi .UNINDENT .UNINDENT .sp Using the \fBGetObject ()\fP function, we can perform a safe downcast to a \fBDropTailQueue\fP\&. The \fINS_ASSERT\fP checks that the pointer is valid. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Ptr > dtq = txQueue\->GetObject > (); NS_ASSERT (dtq != 0); .ft P .fi .UNINDENT .UNINDENT .sp Next, we can get the value of an attribute on this queue. We have introduced wrapper \fBValue\fP classes for the underlying data types, similar to Java wrappers around these types, since the attribute system stores values serialized to strings, and not disparate types. Here, the attribute value is assigned to a \fBQueueSizeValue\fP, and the \fBGet ()\fP method on this value produces the (unwrapped) \fBQueueSize\fP\&. That is, the variable \fIlimit\fP is written into by the GetAttribute method.: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C QueueSizeValue limit; dtq\->GetAttribute ("MaxSize", limit); NS_LOG_INFO ("1. dtq limit: " << limit.Get ()); .ft P .fi .UNINDENT .UNINDENT .sp Note that the above downcast is not really needed; we could have gotten the attribute value directly from \fBtxQueue\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C txQueue\->GetAttribute ("MaxSize", limit); NS_LOG_INFO ("2. txQueue limit: " << limit.Get ()); .ft P .fi .UNINDENT .UNINDENT .sp Now, let’s set it to another value (60 packets). Let’s also make use of the StringValue shorthand notation to set the size by passing in a string (the string must be a positive integer suffixed by either the \fIp\fP or \fIb\fP character). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C txQueue\->SetAttribute ("MaxSize", StringValue ("60p")); txQueue\->GetAttribute ("MaxSize", limit); NS_LOG_INFO ("3. txQueue limit changed: " << limit.Get ()); .ft P .fi .UNINDENT .UNINDENT .SS Config Namespace Path .sp An alternative way to get at the attribute is to use the configuration namespace. Here, this attribute resides on a known path in this namespace; this approach is useful if one doesn’t have access to the underlying pointers and would like to configure a specific attribute with a single statement. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxSize", StringValue ("25p")); txQueue\->GetAttribute ("MaxSize", limit); NS_LOG_INFO ("4. txQueue limit changed through namespace: " << limit.Get ()); .ft P .fi .UNINDENT .UNINDENT .sp The configuration path often has the form of \fB"...///...//"\fP to refer to a specific instance by index of an object in the container. In this case the first container is the list of all \fBNode\fPs; the second container is the list of all \fBNetDevice\fPs on the chosen \fBNode\fP\&. Finally, the configuration path usually ends with a succession of member attributes, in this case the \fB"MaxSize"\fP attribute of the \fB"TxQueue"\fP of the chosen \fBNetDevice\fP\&. .sp We could have also used wildcards to set this value for all nodes and all net devices (which in this simple example has the same effect as the previous \fBConfig::Set ()\fP): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxSize", StringValue ("15p")); txQueue\->GetAttribute ("MaxSize", limit); NS_LOG_INFO ("5. txQueue limit changed through wildcarded namespace: " << limit.Get ()); .ft P .fi .UNINDENT .UNINDENT .sp If you run this program from the command line, you should see the following output corresponding to the steps we took above: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ ./waf \-\-run main\-attribute\-value 1. dtq limit: 80p 2. txQueue limit: 80p 3. txQueue limit changed: 60p 4. txQueue limit changed through namespace: 25p 5. txQueue limit changed through wildcarded namespace: 15p .ft P .fi .UNINDENT .UNINDENT .SS Object Name Service .sp Another way to get at the attribute is to use the object name service facility. The object name service allows us to add items to the configuration namespace under the \fB"/Names/"\fP path with a user\-defined name string. This approach is useful if one doesn’t have access to the underlying pointers and it is difficult to determine the required concrete configuration namespace path. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Names::Add ("server", n0); Names::Add ("server/eth0", net0); \&... Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25)); .ft P .fi .UNINDENT .UNINDENT .sp Here we’ve added the path elements \fB"server"\fP and \fB"eth0"\fP under the \fB"/Names/"\fP namespace, then used the resulting configuration path to set the attribute. .sp See \fI\%Object names\fP for a fuller treatment of the \fIns\-3\fP configuration namespace. .SS Implementation Details .SS Value Classes .sp Readers will note the \fBTypeValue\fP classes which are subclasses of the \fBAttributeValue\fP base class. These can be thought of as intermediate classes which are used to convert from raw types to the \fBAttributeValue\fPs that are used by the attribute system. Recall that this database is holding objects of many types serialized to strings. Conversions to this type can either be done using an intermediate class (such as \fBIntegerValue\fP, or \fBDoubleValue\fP for floating point numbers) or \fIvia\fP strings. Direct implicit conversion of types to \fBAttributeValue\fP is not really practical. So in the above, users have a choice of using strings or values: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C p\->Set ("cwnd", StringValue ("100")); // string\-based setter p\->Set ("cwnd", IntegerValue (100)); // integer\-based setter .ft P .fi .UNINDENT .UNINDENT .sp The system provides some macros that help users declare and define new AttributeValue subclasses for new types that they want to introduce into the attribute system: .INDENT 0.0 .IP \(bu 2 \fBATTRIBUTE_HELPER_HEADER\fP .IP \(bu 2 \fBATTRIBUTE_HELPER_CPP\fP .UNINDENT .sp See the API documentation for these constructs for more information. .SS Initialization Order .sp Attributes in the system must not depend on the state of any other Attribute in this system. This is because an ordering of Attribute initialization is not specified, nor enforced, by the system. A specific example of this can be seen in automated configuration programs such as \fBConfigStore\fP\&. Although a given model may arrange it so that Attributes are initialized in a particular order, another automatic configurator may decide independently to change Attributes in, for example, alphabetic order. .sp Because of this non\-specific ordering, no Attribute in the system may have any dependence on any other Attribute. As a corollary, Attribute setters must never fail due to the state of another Attribute. No Attribute setter may change (set) any other Attribute value as a result of changing its value. .sp This is a very strong restriction and there are cases where Attributes must set consistently to allow correct operation. To this end we do allow for consistency checking \fIwhen the attribute is used\fP (\fIcf\fP\&. \fBNS_ASSERT_MSG\fP or \fBNS_ABORT_MSG\fP). .sp In general, the attribute code to assign values to the underlying class member variables is executed after an object is constructed. But what if you need the values assigned before the constructor body executes, because you need them in the logic of the constructor? There is a way to do this, used for example in the class \fBConfigStore\fP: call \fBObjectBase::ConstructSelf ()\fP as follows: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ConfigStore::ConfigStore () { ObjectBase::ConstructSelf (AttributeConstructionList ()); // continue on with constructor. } .ft P .fi .UNINDENT .UNINDENT .sp Beware that the object and all its derived classes must also implement a \fBGetInstanceTypeId ()\fP method. Otherwise the \fBObjectBase::ConstructSelf ()\fP will not be able to read the attributes. .SS Adding Attributes .sp The \fIns\-3\fP system will place a number of internal values under the attribute system, but undoubtedly users will want to extend this to pick up ones we have missed, or to add their own classes to the system. .sp There are three typical use cases: .INDENT 0.0 .IP \(bu 2 Making an existing class data member accessible as an Attribute, when it isn’t already. .IP \(bu 2 Making a new class able to expose some data members as Attributes by giving it a TypeId. .IP \(bu 2 Creating an \fBAttributeValue\fP subclass for a new class so that it can be accessed as an Attribute. .UNINDENT .SS Existing Member Variable .sp Consider this variable in \fBTcpSocket\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C uint32_t m_cWnd; // Congestion window .ft P .fi .UNINDENT .UNINDENT .sp Suppose that someone working with TCP wanted to get or set the value of that variable using the metadata system. If it were not already provided by \fIns\-3\fP, the user could declare the following addition in the runtime metadata system (to the \fBGetTypeId()\fP definition for \fBTcpSocket\fP): .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \&.AddAttribute ("Congestion window", "Tcp congestion window (bytes)", UintegerValue (1), MakeUintegerAccessor (&TcpSocket::m_cWnd), MakeUintegerChecker ()) .ft P .fi .UNINDENT .UNINDENT .sp Now, the user with a pointer to a \fBTcpSocket\fP instance can perform operations such as setting and getting the value, without having to add these functions explicitly. Furthermore, access controls can be applied, such as allowing the parameter to be read and not written, or bounds checking on the permissible values can be applied. .SS New Class TypeId .sp Here, we discuss the impact on a user who wants to add a new class to \fIns\-3\fP\&. What additional things must be done to enable it to hold attributes? .sp Let’s assume our new class, called \fBns3::MyMobility\fP, is a type of mobility model. First, the class should inherit from its parent class, \fBns3::MobilityModel\fP\&. In the \fBmy\-mobility.h\fP header file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C namespace ns3 { class MyMobility : public MobilityModel { .ft P .fi .UNINDENT .UNINDENT .sp This requires we declare the \fBGetTypeId ()\fP function. This is a one\-line public function declaration: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C public: /** * Register this type. * \ereturn The object TypeId. */ static TypeId GetTypeId (void); .ft P .fi .UNINDENT .UNINDENT .sp We’ve already introduced what a \fBTypeId\fP definition will look like in the \fBmy\-mobility.cc\fP implementation file: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C NS_OBJECT_ENSURE_REGISTERED (MyMobility); TypeId MyMobility::GetTypeId (void) { static TypeId tid = TypeId ("ns3::MyMobility") .SetParent () .SetGroupName ("Mobility") .AddConstructor () .AddAttribute ("Bounds", "Bounds of the area to cruise.", RectangleValue (Rectangle (0.0, 0.0, 100.0, 100.0)), MakeRectangleAccessor (&MyMobility::m_bounds), MakeRectangleChecker ()) .AddAttribute ("Time", "Change current direction and speed after moving for this delay.", TimeValue (Seconds (1.0)), MakeTimeAccessor (&MyMobility::m_modeTime), MakeTimeChecker ()) // etc (more parameters). ; return tid; } .ft P .fi .UNINDENT .UNINDENT .sp If we don’t want to subclass from an existing class, in the header file we just inherit from \fBns3::Object\fP, and in the object file we set the parent class to \fBns3::Object\fP with \fB\&.SetParent ()\fP\&. .sp Typical mistakes here involve: .INDENT 0.0 .IP \(bu 2 Not calling \fBNS_OBJECT_ENSURE_REGISTERED ()\fP .IP \(bu 2 Not calling the \fBSetParent ()\fP method, or calling it with the wrong type. .IP \(bu 2 Not calling the \fBAddConstructor ()\fP method, or calling it with the wrong type. .IP \(bu 2 Introducing a typographical error in the name of the \fBTypeId\fP in its constructor. .IP \(bu 2 Not using the fully\-qualified C++ typename of the enclosing C++ class as the name of the \fBTypeId\fP\&. Note that \fB"ns3::"\fP is required. .UNINDENT .sp None of these mistakes can be detected by the \fIns\-3\fP codebase, so users are advised to check carefully multiple times that they got these right. .SS New AttributeValue Type .sp From the perspective of the user who writes a new class in the system and wants it to be accessible as an attribute, there is mainly the matter of writing the conversions to/from strings and attribute values. Most of this can be copy/pasted with macro\-ized code. For instance, consider a class declaration for \fBRectangle\fP in the \fBsrc/mobility/model\fP directory: .SS Header File .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C /** * \ebrief a 2d rectangle */ class Rectangle { ... double xMin; double xMax; double yMin; double yMax; }; .ft P .fi .UNINDENT .UNINDENT .sp One macro call and two operators, must be added below the class declaration in order to turn a Rectangle into a value usable by the \fBAttribute\fP system: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C std::ostream &operator << (std::ostream &os, const Rectangle &rectangle); std::istream &operator >> (std::istream &is, Rectangle &rectangle); ATTRIBUTE_HELPER_HEADER (Rectangle); .ft P .fi .UNINDENT .UNINDENT .SS Implementation File .sp In the class definition (\fB\&.cc\fP file), the code looks like this: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C ATTRIBUTE_HELPER_CPP (Rectangle); std::ostream & operator << (std::ostream &os, const Rectangle &rectangle) { os << rectangle.xMin << "|" << rectangle.xMax << "|" << rectangle.yMin << "|" << rectangle.yMax; return os; } std::istream & operator >> (std::istream &is, Rectangle &rectangle) { char c1, c2, c3; is >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >> c3 >> rectangle.yMax; if (c1 != \(aq|\(aq || c2 != \(aq|\(aq || c3 != \(aq|\(aq) { is.setstate (std::ios_base::failbit); } return is; } .ft P .fi .UNINDENT .UNINDENT .sp These stream operators simply convert from a string representation of the Rectangle (\fB"xMin|xMax|yMin|yMax"\fP) to the underlying Rectangle. The modeler must specify these operators and the string syntactical representation of an instance of the new class. .SS ConfigStore .sp Values for \fIns\-3\fP attributes can be stored in an ASCII or XML text file and loaded into a future simulation run. This feature is known as the \fIns\-3\fP ConfigStore. The \fBConfigStore\fP is a specialized database for attribute values and default values. .sp Although it is a separately maintained module in the \fBsrc/config\-store/\fP directory, we document it here because of its sole dependency on \fIns\-3\fP core module and attributes. .sp We can explore this system by using an example from \fBsrc/config\-store/examples/config\-store\-save.cc\fP\&. .sp First, all users of the \fBConfigStore\fP must include the following statement: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C #include "ns3/config\-store\-module.h" .ft P .fi .UNINDENT .UNINDENT .sp Next, this program adds a sample object \fBConfigExample\fP to show how the system is extended: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C class ConfigExample : public Object { public: static TypeId GetTypeId (void) { static TypeId tid = TypeId ("ns3::A") .SetParent () .AddAttribute ("TestInt16", "help text", IntegerValue (\-2), MakeIntegerAccessor (&A::m_int16), MakeIntegerChecker ()) ; return tid; } int16_t m_int16; }; NS_OBJECT_ENSURE_REGISTERED (ConfigExample); .ft P .fi .UNINDENT .UNINDENT .sp Next, we use the Config subsystem to override the defaults in a couple of ways: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Config::SetDefault ("ns3::ConfigExample::TestInt16", IntegerValue (\-5)); Ptr a_obj = CreateObject (); NS_ABORT_MSG_UNLESS (a_obj\->m_int16 == \-5, "Cannot set ConfigExample\(aqs integer attribute via Config::SetDefault"); Ptr a2_obj = CreateObject (); a2_obj\->SetAttribute ("TestInt16", IntegerValue (\-3)); IntegerValue iv; a2_obj\->GetAttribute ("TestInt16", iv); NS_ABORT_MSG_UNLESS (iv.Get () == \-3, "Cannot set ConfigExample\(aqs integer attribute via SetAttribute"); .ft P .fi .UNINDENT .UNINDENT .sp The next statement is necessary to make sure that (one of) the objects created is rooted in the configuration namespace as an object instance. This normally happens when you aggregate objects to a \fBns3::Node\fP or \fBns3::Channel\fP instance, but here, since we are working at the core level, we need to create a new root namespace object: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Config::RegisterRootNamespaceObject (a2_obj); .ft P .fi .UNINDENT .UNINDENT .SS Writing .sp Next, we want to output the configuration store. The examples show how to do it in two formats, XML and raw text. In practice, one should perform this step just before calling \fBSimulator::Run ()\fP to save the final configuration just before running the simulation. .sp There are three Attributes that govern the behavior of the ConfigStore: \fB"Mode"\fP, \fB"Filename"\fP, and \fB"FileFormat"\fP\&. The Mode (default \fB"None"\fP) configures whether \fIns\-3\fP should load configuration from a previously saved file (specify \fB"Mode=Load"\fP) or save it to a file (specify \fB"Mode=Save"\fP). The Filename (default \fB""\fP) is where the ConfigStore should read or write its data. The FileFormat (default \fB"RawText"\fP) governs whether the ConfigStore format is plain text or Xml (\fB"FileFormat=Xml"\fP) .sp The example shows: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output\-attributes.xml")); Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml")); Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save")); ConfigStore outputConfig; outputConfig.ConfigureDefaults (); outputConfig.ConfigureAttributes (); // Output config store to txt format Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output\-attributes.txt")); Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText")); Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save")); ConfigStore outputConfig2; outputConfig2.ConfigureDefaults (); outputConfig2.ConfigureAttributes (); Simulator::Run (); Simulator::Destroy (); .ft P .fi .UNINDENT .UNINDENT .sp Note the placement of these statements just prior to the \fBSimulator::Run ()\fP statement. This output logs all of the values in place just prior to starting the simulation (\fIi.e\fP\&. after all of the configuration has taken place). .sp After running, you can open the \fBoutput\-attributes.txt\fP file and see: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C \&... default ns3::ErrorModel::IsEnabled "true" default ns3::RateErrorModel::ErrorUnit "ERROR_UNIT_BYTE" default ns3::RateErrorModel::ErrorRate "0" default ns3::RateErrorModel::RanVar "ns3::UniformRandomVariable[Min=0.0|Max=1.0]" default ns3::BurstErrorModel::ErrorRate "0" default ns3::BurstErrorModel::BurstStart "ns3::UniformRandomVariable[Min=0.0|Max=1.0]" default ns3::BurstErrorModel::BurstSize "ns3::UniformRandomVariable[Min=1|Max=4]" default ns3::PacketSocket::RcvBufSize "131072" default ns3::PcapFileWrapper::CaptureSize "65535" default ns3::PcapFileWrapper::NanosecMode "false" default ns3::SimpleNetDevice::PointToPointMode "false" default ns3::SimpleNetDevice::TxQueue "ns3::DropTailQueue" default ns3::SimpleNetDevice::DataRate "0bps" default ns3::PacketSocketClient::MaxPackets "100" default ns3::PacketSocketClient::Interval "+1000000000.0ns" default ns3::PacketSocketClient::PacketSize "1024" default ns3::PacketSocketClient::Priority "0" default ns3::ConfigStore::Mode "Save" default ns3::ConfigStore::Filename "output\-attributes.txt" default ns3::ConfigStore::FileFormat "RawText" default ns3::ConfigExample::TestInt16 "\-5" global SimulatorImplementationType "ns3::DefaultSimulatorImpl" global SchedulerType "ns3::MapScheduler" global RngSeed "1" global RngRun "1" global ChecksumEnabled "false" value /$ns3::ConfigExample/TestInt16 "\-3" .ft P .fi .UNINDENT .UNINDENT .sp In the above, several of the default values for attributes for the core and network modules are shown. Then, all the values for the \fIns\-3\fP global values are recorded. Finally, the value of the instance of \fBConfigExample\fP that was rooted in the configuration namespace is shown. In a real \fIns\-3\fP program, many more models, attributes, and defaults would be shown. .sp An XML version also exists in \fBoutput\-attributes.xml\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C .ft P .fi .UNINDENT .UNINDENT .sp This file can be archived with your simulation script and output data. .SS Reading .sp Next, we discuss configuring simulations \fIvia\fP a stored input configuration file. There are a couple of key differences compared to writing the final simulation configuration. First, we need to place statements such as these at the beginning of the program, before simulation configuration statements are written (so the values are registered before being used in object construction). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input\-defaults.xml")); Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load")); Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml")); ConfigStore inputConfig; inputConfig.ConfigureDefaults (); .ft P .fi .UNINDENT .UNINDENT .sp Next, note that loading of input configuration data is limited to Attribute default (\fIi.e\fP\&. not instance) values, and global values. Attribute instance values are not supported because at this stage of the simulation, before any objects are constructed, there are no such object instances around. (Note, future enhancements to the config store may change this behavior). .sp Second, while the output of \fBConfigStore\fP state will list everything in the database, the input file need only contain the specific values to be overridden. So, one way to use this class for input file configuration is to generate an initial configuration using the output (\fB"Save"\fP) \fB"Mode"\fP described above, extract from that configuration file only the elements one wishes to change, and move these minimal elements to a new configuration file which can then safely be edited and loaded in a subsequent simulation run. .sp When the \fBConfigStore\fP object is instantiated, its attributes \fB"Filename"\fP, \fB"Mode"\fP, and \fB"FileFormat"\fP must be set, either \fIvia\fP command\-line or \fIvia\fP program statements. .SS Reading/Writing Example .sp As a more complicated example, let’s assume that we want to read in a configuration of defaults from an input file named \fBinput\-defaults.xml\fP, and write out the resulting attributes to a separate file called \fBoutput\-attributes.xml\fP\&.: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C #include "ns3/config\-store\-module.h" \&... int main (...) { Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input\-defaults.xml")); Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load")); Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml")); ConfigStore inputConfig; inputConfig.ConfigureDefaults (); // // Allow the user to override any of the defaults and the above Bind () at // run\-time, viacommand\-line arguments // CommandLine cmd; cmd.Parse (argc, argv); // setup topology ... // Invoke just before entering Simulator::Run () Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output\-attributes.xml")); Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save")); ConfigStore outputConfig; outputConfig.ConfigureAttributes (); Simulator::Run (); } .ft P .fi .UNINDENT .UNINDENT .SS ConfigStore use cases (pre\- and post\-simulation) .sp It is worth stressing that ConfigStore can be used for different purposes, and this is reflected in where in the script ConfigStore is invoked. .sp The typical use\-cases are: .INDENT 0.0 .IP \(bu 2 Change an Object default attributes .IP \(bu 2 Inspect/change a \fIspecific\fP Object attributes .IP \(bu 2 Inspect the simulation Objects and their attributes .UNINDENT .sp As a matter of fact, some Objects might be created when the simulation starts. Hence, ConfigStore will not “report” their attributes if invoked earlier in the code. .sp A typical workflow might involve running the simulation, calling ConfigStore at the end of the simulation (after \fBSimulator::Run ()\fP and before \fBSimulator::Destroy ()\fP) This will show all the attributes in the Objects, both those with default values, and those with values changed during the simulation execution. .sp To change these values, you’ll need to either change the default (class\-wide) attribute values (in this case call ConfigStore before the Object creation), or specific object attribute (in this case call ConfigStore after the Object creation, typically just before \fBSimulator::Run ()\fP\&. .SS ConfigStore GUI .sp There is a GTK\-based front end for the ConfigStore. This allows users to use a GUI to access and change variables. .sp Some screenshots are presented here. They are the result of using GtkConfig on \fBsrc/lte/examples/lena\-dual\-stripe.cc\fP after \fBSimulator::Run ()\fP\&. .INDENT 0.0 .INDENT 2.5 [image] .UNINDENT .UNINDENT .INDENT 0.0 .INDENT 2.5 [image] .UNINDENT .UNINDENT .sp To use this feature, one must install \fBlibgtk\-3\-dev\fP; an example Ubuntu installation command is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ sudo apt\-get install libgtk\-3\-dev .ft P .fi .UNINDENT .UNINDENT .sp On a MacOS it is possible to install GTK\-3 using \fI\%Homebrew\fP\&. The installation command is: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ brew install gtk+3 adwaita\-icon\-theme .ft P .fi .UNINDENT .UNINDENT .sp To check whether it is configured or not, check the output of the step: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ ./waf configure \-\-enable\-examples \-\-enable\-tests \-\-\-\- Summary of optional NS\-3 features: Python Bindings : enabled Python API Scanning Support : enabled NS\-3 Click Integration : enabled GtkConfigStore : not enabled (library \(aqgtk+\-3.0 >= 3.0\(aq not found) .ft P .fi .UNINDENT .UNINDENT .sp In the above example, it was not enabled, so it cannot be used until a suitable version is installed and: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ ./waf configure \-\-enable\-examples \-\-enable\-tests $ ./waf .ft P .fi .UNINDENT .UNINDENT .sp is rerun. .sp Usage is almost the same as the non\-GTK\-based version, but there are no \fBConfigStore\fP attributes involved: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C // Invoke just before entering Simulator::Run () GtkConfigStore config; config.ConfigureDefaults (); config.ConfigureAttributes (); .ft P .fi .UNINDENT .UNINDENT .sp Now, when you run the script, a GUI should pop up, allowing you to open menus of attributes on different nodes/objects, and then launch the simulation execution when you are done. .sp Note that “launch the simulation” means to proceed with the simulation script. If GtkConfigStore has been called after \fBSimulator::Run ()\fP the simulation will not be started again \- it will just end. .SH OBJECT NAMES .sp \fIPlaceholder chapter\fP .SH LOGGING .sp The \fIns\-3\fP logging facility can be used to monitor or debug the progress of simulation programs. Logging output can be enabled by program statements in your \fBmain()\fP program or by the use of the \fBNS_LOG\fP environment variable. .sp Logging statements are not compiled into optimized builds of \fIns\-3\fP\&. To use logging, one must build the (default) debug build of \fIns\-3\fP\&. .sp The project makes no guarantee about whether logging output will remain the same over time. Users are cautioned against building simulation output frameworks on top of logging code, as the output and the way the output is enabled may change over time. .SS Overview .sp \fIns\-3\fP logging statements are typically used to log various program execution events, such as the occurrence of simulation events or the use of a particular function. .sp For example, this code snippet is from \fBIpv4L3Protocol::IsDestinationAddress()\fP: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C if (address == iaddr.GetBroadcast ()) { NS_LOG_LOGIC ("For me (interface broadcast address)"); return true; } .ft P .fi .UNINDENT .UNINDENT .sp If logging has been enabled for the \fBIpv4L3Protocol\fP component at a severity of \fBLOGIC\fP or above (see below about log severity), the statement will be printed out; otherwise, it will be suppressed. .SS Enabling Output .sp There are two ways that users typically control log output. The first is by setting the \fBNS_LOG\fP environment variable; e.g.: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ NS_LOG="*" ./waf \-\-run first .ft P .fi .UNINDENT .UNINDENT .sp will run the \fBfirst\fP tutorial program with all logging output. (The specifics of the \fBNS_LOG\fP format will be discussed below.) .sp This can be made more granular by selecting individual components: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ NS_LOG="Ipv4L3Protocol" ./waf \-\-run first .ft P .fi .UNINDENT .UNINDENT .sp The output can be further tailored with prefix options. .sp The second way to enable logging is to use explicit statements in your program, such as in the \fBfirst\fP tutorial program: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C int main (int argc, char *argv[]) { LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO); LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO); ... .ft P .fi .UNINDENT .UNINDENT .sp (The meaning of \fBLOG_LEVEL_INFO\fP, and other possible values, will be discussed below.) .SS \fBNS_LOG\fP Syntax .sp The \fBNS_LOG\fP environment variable contains a list of log components and options. Log components are separated by \(ga:’ characters: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ NS_LOG=":..." .ft P .fi .UNINDENT .UNINDENT .sp Options for each log component are given as flags after each log component: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ NS_LOG="=