Code Quality Checking
-
Commercial tools.
-
Scripts for code management and testing.
-
Programming hints.
1. Commercial tools
The GEANT4 prototype code has been filtered by the most commonly used code
inspectors packages currently in the market. Some of them have been used
just for a limited period of time for evaluation.
2. Scripts for code management and testing
Scripts have been developed to inspect the code in order to find common
mistakes:
-
g4correct finds examples of common mistakes and inelegant usage
in C++ source files, namely:
-
a=a+2; instead of a+=2;
-
same for -=, *= and /=
-
a+=1; instead of a++;
-
(*ptr_to_class).member instead of ptr_to_class->member
The script can correct the source or present its change proposals (diff
format, or diff -e format).
It works only in 95% of the cases (only a parser based solution would
be perfect). The results should always be checked since it may even corrupt
correct code !
-
g4findcast finds old style C casts like (G4Int)1.2. In C++
this should look G4Int(1.2)
Another script has been developed for testing the code: g4test.
View the instructions for installing
these scripts and other useful tools.
3. Programming hints
Here is a list of hints for development in Geant4 to avoid common programming mistakes.
It is far from being exaustive or complete.
-
Initialize all data members of a class in each constructor (pointers,
G4ints, etc.)
Bugs related to this problem are extremely difficult to track down!
-
Always initialize to zero (0) pointers
to data which have been dynamically deallocated with delete and which
are going to be reused
The following program will print the string on screen on most systems:
Foo* p = new Foo();
delete p;
if (p) cout << "Hello! p is NOT zero!" << endl;
Prefer the usage of 0 instead of NULL to initialize a null
pointer
NULL is system dependent and, although now
it is (or will be) included in the C++ ANSI standard, most of the C++
compilers do not have it defined and they fetch it from stdio.h.
Always declare virtual the destructor of a base class
... and remove all virtual qualifiers from methods of derived
classes which are not supposed to act as base class for others.
Do not insert items allocated on the heap into value-based collections
(Or, at least, preserve the pointer for deletion)
Properly destruct objects
This was only a problem in "singleton" classes; it did not affect the
code but made memory usage checking much more difficult. In particular,
remember to delete items of a pointer based collection which were allocated
on the heap.
Inline short and frequently used member functions
Define inlined functions in the .icc file and check that the
compiler is effectively inlining them (using the proper compiler options).
Usage of C++ operators
(see above, at correct.sh)
Use as few arithmetic functions as possible
especially pow() and sqrt() are time consuming. In particular:
- a*a or sqr(a) is better than pow(a, 2.0)
- and
tmp= a;
for (i=0; i<10; i++) {
array[i]= tmp;
tmp*= base;
}
is better than
for (i=0; i<10; i++) {
array[i]= a*pow(base, i);
}
A member function doing allocations on the heap may leak memory if the
user invokes the function several times
Check if allocation was done already.
Avoid:
if (a<1) b= 0;
if (a<2) b= 1;
if (a<34) b= 2;
if ... else if ... is faster.
Access of elements of the RWTPtrOrderedVector collections
As of Tools.h++ V7.0.1:
operator() fetches an element and does no bounds checking of the index.
operator[] fetches an element and does bounds check.
The library is only aware of insertions by insert() (and related
functions).
Therefore:
-
filling the array by insert() and reading it with [] works;
-
filling the array by () and reading it by () works;
-
filling by [] does NOT work any more.
Use operator() for accessing elements in Rogue Wave collections
It's well known that bounds checking considerably slows down performance.
The use of () operator will also help on spotting those common bounds'
errors.
The return type of function main()
use the declaration int main() instead of void main()
or main() (this "implicite int" will be excluded from the C++ standard
soon). Programs should return:
-
0 or EXIT_SUCCESS on success;
-
nonzero or EXIT_FAILURE on error.
Deletion of elements in pointer based collections
Items stored in pointer collections e.g. RWTPtrOrderedVector
can be deleted by the method clearAndDestroy().
Problems with classes that cannot be copied/assigned to/constructed
etc.
It is best to make the corresponding member functions private.
Use const declarations whenever possible instead of numbers in the code
Avoid multiple return points in a method where possible
Methods with multiple return points cannot be inlined and some compilers
(e.g. HP-CC) complain about this.
Avoid doing assignments like:
if (cond) a= true; else a= false;
use a= cond; instead;
Use G4Exception to print an error message and exit the program
Use costants and units defined in PhysicalConstants.h and
SystemOfUnits.h
Look inside those files in CLHEP/Units before defining local
constants or putting hard-coded numbers in the code. They may be already
defined there!
Do not code bit-wise operations within a word
Consider a word as atomic!
Note
Some tools for run-time memory usage checking are not able to cope with
the Rogue Wave RWCString class. This class does not store a pointer
to the beginning of the heap block it allocates, it stores just one to
the beginning+4 (the first 4 bytes store some bookeeping information),
thus they falsely report every undeleted RWCString as a possible
memory leak.
Mon 29 Mar 1999 GC
& PU