About coding habits
Goal
The goal is to be able to work as a team. Some coders are completly able to work on a function made of hundreds of lines. That's not the issue.
The problem is that one day a coworker will have to use or to alter that function. And there's no way he can quickly 100% be sure how to use it or where to add his new code. He's most probably going to create new bugs, but that's the fault of the original author. Also it will cost times.
General:
- Bad: early abstraction, before we actually know if, how and why it will have to be abstracted.
- Some think modern code must use STL and Boost ; other hates these libs. That's not the subject of this page, do as ordered to and as required by the platform. On the other hand I strongly advice using STL in all offline tools.
- Don't abuse polymorphism and heritage. Often a class with pointers to composants will be more generic and cleaner. Example: "shall BreakableSlidingDoor class derivate from BreakableDoor or from SlidingDoor?" vs an Entity class containing pointers to several composants, which will be NULL if the component doesn't exist for a particular entity (movement, life points and stats, AI, physics, rendering...).
- "Premature optimization is the root of all evil" (by Sir Tony Hoare) is so wrong, as the true quote by Sir Tony Hoare (other page with the full quote) does prove:
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.
Early function-local optimization (like asm) is wrong. Early algorithm/application architecture optimization is good and it is a must do/have to be a good programmer. Doing it too late in the life of the project means it will be harder or impossible to do, plus it will slow down the project development. - Extreme-programming: bad name for grouping ideas that already existed previously. What creating another marketing word and why using the so negative "extreme" word?
- Peer-programming: very good for specs.
Readable code
- No too long functions (max ~ one screen / 40 lines).
- No too generic meaningless variable names: dirPlayerToTarget instead of dir.
- Don't copy paste big blocs of code. Instead factorize the common code in a function.
- Always use size_t when casting a pointer to an integer (for comparison or offsets for example).
- Don't reuse a local variable in a different way, always create a new one with an explicit name. This will also tell the compiler the previous value is not useful anymore and can be discarded ; thus it will optimize more.
- Read only once member variables, and store them in local variables if needed: this will tell the compiler there's no memory aliasing, thus it will optimize more.
- Add a comment on each #else and #endif telling which #if it corresponds.
- Exit a function only at the very beginning in some inputs checking, or at the very end.
- No repeated accesses to global variables or singletons: keep a local reference or copy. It will help both in readability of the code and in optimisation (less memory aliasing, fewer function calls that often cannot be otpimized by the compiler...)
Often the same for member variables: if a loop is changing a member, it's often better to create a local copy, to have the loop update the local copy, then to copy it back in the member variable. - No magic numbers: no constants in hard in the code. Sometimes a "*2" might be acceptable. But here's an example of what is completely wrong: a coworker wrote a streaming system of levels in a game ; the system was relying on a feature of the console hardware, which could read only blocs of 512 bytes, and to a 512 bytes aligned memory address. Example of his code before I cleaned it:
- sizeBloc = (size>>9)<<9;
- assert( (destAddress & 511) == 0 );
Example after cleanup:- sizeBloc = size & ~(HARDWARE_BLOCK_SIZE-1); // Yes, the API did have a defined named constant. What's more, that "&" is more readable and also faster to execute.
- assert( (destAddress & (HARDWARE_BLOCK_SIZE-1) == 0 );
More readable and a bit faster code, and it will still work if the architecture is updated with different streaming bloc size.
No "C" prefix in class names. This habits comes from Microsoft to make a clear distinction between their classes and classes from other programmers. Unfortunately other programmers started using the same naming convention, making it both useless and counter-productive.Pointers vs References: using a reference will tell the NULL value is not allowed (but it doesn't mean one should not test this case (either through a if or an assert) ; using a pointer, one can tag it as restricted, allowing the compiler to optimize more aggressively.Use standardized coding guidelines in your team, that will help sharing code. A few examples (but you'll meet lots of other conventions as each company or even each project has its own rules):- I like the Pascal/Java notation for class and methods/functions names, and the Camel notation for variables: no '_' in the middle of the name, caps on each word of the name, except the first one in variable names: float speed, int GetSpeed().
- Member variable names should be obious. Either using a prefix (m or m_ or something else), or a suffix. Don't use '_' prefix or suffix as C/C++ standard forbid that. Small advantage of suffixes: faster to (not) type with auto-completion ; but harder to read/catch for some people.
- Hungarian Notation: I like a very simplified notation stating only statics, globals, C arrays, and C string. No pointer/integer/float/... prefix. But each team you'll meat will have different habits, one is to changes his habits with every new job.