Version 20 (modified by 11 years ago) ( diff ) | ,
---|
Coding style
Coding rules might sound overdone, but they are essential to keep a code clean and tidy and even more important consistent. At the end this will help everybody to read code of somebody else, because it is structured at least very similar everywhere and the optical layout reflects somewhat its structure. These rules include a lot of experience and are adapted to modern screen (which have more than 25 lines). The order is random and has not yet any meaning. They are the basis for the development of FACT++ and MARS. Future projects might use different rules, but the more consistent the existing code basis is, the easier it is for everybody to understand all existing code.
- In general lines should not be longer than 80 characters.
- Use const qualifiers where ever possible. This includes functions, function arguments and every variable declaration. This has several advantages: Improved checking by the compiler, better optimization by the compiler, and faster understanding (You are only interested in a loop at the end of a function and an important variable is declared at the beginning, you would have to check all code in between carefully to know its value, and even worse, ever function call which might take the non-const variable as a reference and alter it).
- Declare variables as late as possible. To understand code, it makes sense to have all you need as close as possible which avoids scrolling forth and back.
- The indentation is 4. No tabs allowed.
- Curly braces are *always* on a single empty line (this gives a clear structure to the code), although in *switch* statements. No comments on the line with the opening brace. Put them above (e.g. the if-clause) or below in the next line.
- *goto* is a no-go, it breaks the structure of the code (check google for details)
- In switch statements the *case* label is on the same level as the *switch*. The corresponding code or brackets are indented.
- There is only one command per line. In if conditions the command to be executed is always on a new line, the *else* is always on it's own empty line.
- Nested if's like "if () else if () else if () else ..." are written like a staircase following the previous rules. Every other formatting hides the structure!
- Variables definitions in class headers are at the beginning of the class definition. Usually (not always reasonable) the order of modifiers is: private, protected, public. After the variables, put the functions, in the same order.
- Data members always start with an f (for field), static constant members with a g (for global) and non-const static members with fg (for global field). We use capitalization for variable names, e.g. fThisIsMyDataMember. class names and function names start with a capital. Strongly Mars related classes start with a capital M. Classes which can compile without Mars, might start with a different capital. Some exceptions are very very low level classes (e.g. zfits) which are written closer to the system coding style.
- File names should generally have the same name as the class name.
- Each header has to be encapsulated in an #ifndef MARS_ClassName, #define MARS_ClassName, [...code...], #endif block to avoid double inclusion.
- *using namespace* is never be used globally in a header.
- Try to use empty lines to separate blocks (e.g. everything which belongs to a loop plus the loop from the rest)
- Use '\n' to finish a line not *endl* except you want to flush a line to the console (e.g. to make sure it is available if after that a crash happens). For example: You output many lines successively, use '\n'. For the last one before your code returns use *endl*
- Do not use curly braces in if-statements to bracket a single line.
- Use a white space after the semicolon in a for-statement, but not before. Keep the three blocks without white-spaces. This simplifies to see what belongs to which part.
- In argument lists, use a white space after the comma, not before.
- If you use the ?:-operator, put white spaces around the ? and the colon. Try to keep the rest without white spaces.
- Avoid to assign variables just to give something a different name, use comments. Generally, do not invent variables for something which is only used once, except it helps for example to keep a function call much shorter.
- Use white spaces around logical operators (&&, ..) in conditionals (==, <= are conditional operators, not logical operators). This mimics the priority of the operators and makes the condition easier to understand. Try to keep the calculations compact. Avoid unnecessary parenthesis. If you are not sure, check operator priorities. This is a language issue nothing random from the compiler!
- Avoid using line long variable names. They should not be too short either. *timv* doesn't tel us much, but *myVar* tells us as much as *thisIsMyVariable* but makes code more compact thus better readble. Don't use variable names to explain their contents, use comments!
- Whenever possible propagate const-references instead of copying the argument. This is an optimization issue.
- Use gRandom and do not define your own TRandom object. The global scope will take care of that. A major advantage of simulations is to be able to set a global(!) seed.
- For proper documentation, please have a look at other classes. Note that FACT++ and Mars style is slightly different. Mars is using THtml with some Mars specific setup, FACT++ is using doxygen.
- Never define constants as preprocessor directives. Generally avoid preprocessor directives. If you really need them use upper case only.
- omit else in constructions like *if (...) return x; [else] ....*
- Source files have the extension cc, header files the extension h, C source files just c.
- Operators +=, -=, etc. are preferable over constructions like x=x+, x=x-, ...
- If you omit intentionally a break in a switch statement (*fallthru*) add a comment (only necessary *after* code between two case-statements, not necessary if there is nothing at all between two case statements)
- Sometimes including system headers is not identical for all operating systems, comments might help
- Try to avoid using dynamic allocation whenever possible: "int i=5; /* do something with i*/; /* i runs out of scope */" is preferred over "int *i = new int; *i=5; /* do something with i */; delete i;" Although this example sounds ridiculous, the same is true for every class instance you allocate.
- Initialization of all data members using their constructor (see example) is preferred over using assignments (optimization reasons)
- Keep your code linear! Avoid using a lot of if-else and blocks in loops. Use *continue* and *break* instead (easier to read, human brains have difficulties to handle to many levels, better fits on the screen, easier to optimize)
- avoid using c-style comments (/* ... */) if you comment a whole line
- Use forward declaration whenever possible instead of includes (compile time)
- Do never use global variables, except you have discussed that with everybody else before.
- Try to avoid static variables (only use them if it is really unavoidable -- think twice)
- directory names should be lower case only (to easy distinguishable from source code files)
- typedefs should end with a _t like UInt_t
- Try to avoid code repetitions. Invent a new function instead.
- Move code to their own function, If otherwise the structure gets too complicated. A typical example is
"MyClass *ptr = new MyClass; CallFunction(ptr); delete ptr;"
is much easier than"MyClass *ptr = new MyClass; if (...) { delete ptr; return; } if (...) { delete ptr; return; } delete ptr;"
In a similar case"int i = Find(a);"
with something like"int Func(int a) { for (int i=0; i<n; i++) { if (x[i]==a) return i; if (x[i]==a+1) return a+1; ... } return -1; }"
then if you try to construct the same with breaks and variables declared outside of the for-scope.
Exception from these rules are of course acceptable but should stay an exception. Exceptions are only useful if using a different style, reveals the structure of the code (what the code is doing) much better.
Example
#ifndef MARS_Complex #define MARS_Complex #include <math.h> // needed for sqrt on Ubuntu 10.14 class Complex { private: double fRe; double fIm; public: Complex(double re, double im) : fRe(re), fIm(im) { } double Modulus() const { return sqrt(fRE * fRe + fIm * fIm); } bool Foo(int a, int b) { for (int i=0; i<b-a; i++) { if (i<b) bar(i); else { bar(i); bar(b); } } const int c = a + b; switch (c) { case 0: // Code goes here return false; case 1: case 2: // Code goes here return false; default: // Code goes here return true; } return true; } }; #endif