Home   Project Page   Download   Manual   API Reference   Neddoc Reference   Support  

basic network

OMNeT++ Coding Conventions / Style Guide

(last updated: Sept 17 1999)

Here's a bunch of advice on how to write OMNeT++ models. Some of them are "rules of thumb", saying if you program like that, you're likely to have less trouble; other conventions are aimed at making OMNeT++ models higher quality and more consistent.

Source files

1. Put the NED description, the C++ class declaration and the implementation of a simple module into three separate files. Don't put two or more modules' code into the same file unless they are build upon one another - do not be afraid of small files! Thus, for a simple module called Foobar, you should have Foobar.ned, Foobar.h and Foobar.cc. This reduces coupling of module sources and makes your code more reusable.

C++ coding style

2. Write legible C++ code. Choose your favourite indentation style and keep to it consistently. (I would prefer the style in which the OMNeT++ sources are written.) Don't put more than one statement on the same line. Always use blank lines to break the code into not-very-long logical blocks; above each block, put a comment of few-words what that block does. Always put at least two blank lines between two (member) functions. Remember: the structure of your code should be obvious at the first glance!

3. Identifiers: Begin module type names with a capital letter, and capitalize the beginning of each word, like in TokenRingMAC. Do not use underscore `_` in module names. Use the C++-style naming on member functions: beginning of each word is capitalized (except for the first one) and no underscores: sendUnnumberedFrame(). On parameter names, use C++-style naming, e.g. windowSize.

Organizing simple modules

4. Put state variables into the class declaration as data members instead of local variables to activity(). This enables referencing them in finish() as well as splitting up activity().

5. Objects like cQueues, cOutVectors, cStdDevs also go into the class declaration. You can assign names to them with setName() and otherwise initialize them at the top of the activity() function. This way you can avoid having to write the simple module constructor explicitly.

6. Avoid global variables (and what's the same, static class members). They are not reset to their initial value (zero) when you run the simulation, stop it and rebuild the network. This can cause several problems when you use Cmdenv to execute several runs one after another, or in Tkenv when you rebuild the network from the menu.

7. Query the values of parameters into state variables (-->class members) of the same name at the top of the activity() function. If you know the value of a parameter is a random value (like uniform 0..10) or it can change during simulation, then to avoid having to look it up by name each time (like d=par("delay")) you may query its pointer into a cPar* state variable with the same name prepended with 'p' (like pDelay=&par("delay")).

Constructors, destructors, initialize() and finish()

8. Never put code that's supposed to do simulation-related things into the simple module destructor; put them into finish() instead. In fact, you almost never need to write a destructor since OMNeT++ keeps track of objects you create (dynamically, as local variables or as class members) and disposes of them automatically. However it cannot track non-OMNeT++ objects so they may need to be deleted manually from the destructor.

9. You need to write the simple module constructor exlicitly if you have dynamic data structures in the class that need to be freed by the destructor. Even in this case, make the constructor as simple as possible (e.g. simply NULL out the data structure pointers) and leave real work to the activity() function.

10.Especially, do not put simulation-related code into the simple module constructor. For example, modules often need to investigate their surroundings (maybe the whole network) at the beginning of the simulation and save the collected info into internal tables. Code like that cannot be placed into the constructor since the network is still being set up when the constructor is called.

Make use of what C++ offers

11.Do not hesitate to split up the activity() function to several member functions, as this generally improves code readability. For example, if you're implementing a state machine, you can put the general logic into activity() and the code implementing the actual states into separate member functions. As a rule of thumb, one member function shouldn't be more than one screen page (about 25 lines) unless really necessary.

12.Make the functions virtual. Maybe someone who reuses your code will need a different behavior than what you thought of.

13.Use inheritance if you're writing a very complex simple module: create a basic simple module class and build upon it deriving new module classes. This will make your code more readable and easier to manage/reuse. Unfortunately, inheritance is not supported in NED so you actually have to make distinct NED descriptions for each simple module class. Even if you have an abstract classes, prepare a NED desctiption for it: it is useful as a reference to others who might derive a different simple module class from your abstract class. Inheritance in NED is planned in later releases of OMNeT++.

For better performance

14.Consider using subclassing cMessage to add parameters to it instead of using cPar parameters. Simulations using message subclassing are reported to have 5..10 times (!) performance advantage to those using cPar message parameters.

15.Reuse messages wherever possible, as this can result in a tremendous gain in execution speed! For example, if you implement timers, you can create a message once and then schedule the same message again and again each time the timer is restarted. Look at the Fifo example about how to do it.

Prepare for debugging

16.Use ev.printf() and ev<<... generously to print out information on what the module is doing. Doing so will pay out several times when it comes to debugging. Use a parameter and a state variable called debug. Surround your debugging output (ev<<... and ev.printf() calls) with if(debug). You may introduce more specific debug switches (like debug_queuing etc.)

17.Put a WATCH() on the state variables (including debug) at the top of the activity function. They cause no run-time overhead, consume little memory and can be invaluable during debugging.