General Style and Design Guidelines.
More...
General Style and Design Guidelines.
Formatting Style:
- Generally follows the FreeBSD kernel style. See "man style" on a FreeBSD host, or search the web.
- Example link: http://www.freebsd.org/cgi/man.cgi?query=style&sektion=9
- FreeBSD style means 8-character tabs.
- Optimized for easy merges and diffs (cvs diff etc.) (this means function definitions and other lists are often split into a single line per argument or entry, etc).
Header Files:
- Header files are there for the user of a class or API, not the developer of the class or API.
- As such, header files must be terse (or silent!) on implementation.
- Header files must be easy to read and understand from the user's point of view. That is, they must be formatted and commented so that someone perusing the header can quickly understand everything. You should not rely on tools or generated documentation for users to understand the objects and methods declared in the header.
- Header files must explain clearly what the class/API is for and how it should be used. That is, the design and logical concepts as well as the technical details.
- Header files form a strict wall between the complexity of implementation (.cpp file) and what should be the simple intended purpose (exposed API). A messy header file indicates that complexity can leak out into other code, which is nonmaintainable.
General Implementation Notes:
- Aggressively check inputs. Assert or throw as necessary.
- Throw if you think there may be legitimate reasons for an error condition to occur (out of disk space, bad user input, etc.). Only assert if the error appears to be a coding issue. Imagine that the code will be linked into a long-running server daemon that is never supposed to fail. Throwing is safest if you aren't sure.
- Any errors must be accompanied with a rich error message. Imagine that someone is trying to code against your API using only the error messages to guide them. It should be possible for them to do that. (Asserting on a null pointer is okay. But more intricate checks must have clear descriptions for both callers and the developers who come into the code later).
- Code for long-term maintainability by multiple strangers. This means code and comments must be clear and readable.
- Within a .cpp file, I usually put static helper methods at the top of the file, and the public API at the bottom. This is to avoid needing separate function declarations and definitions. Usually, the only function declarations are in header files, and functions private to a file (static) are defined before any references to them occur, so no separate declaration is needed.
A note on Class implementation:
- If a class is meant to be used on the stack, then of course the header file must include all of the private data etc.
- If a class is meant to be allocated on the heap, then private data should not be in the header. Typically this means that the header contains only an interface declaration, and the .cpp file contains a class that implements the interface. You'll have to provide a factory method on the interface, since the implementation class is hidden. Why do this?
- Header file stays clean (interface only, so a user of the class isn't distracted by implementation details).
- Code that uses the class doesn't have to recompile every time the implementation changes.
- If for some reason you have a class allocated on the heap but you can't pay the cost of virtual function calls, then of course you can put the private data in the header. But that is probably an indication that something isn't factored correctly.
- If your class interface is so big that you can't reasonably put all of the class implementation in a single .cpp file, that is also an indication that your class is too complex and needs to be refactored.
- Even within a .cpp file, the class declaration must be terse, so a developer can get a feel for the class in only 1-2 screens. A large implementation class is probably a sign that additional functionality could be broken out into separately reusable components.
- Within a class declaration, keep things grouped. For instance, all public methods should be declared in one place, all private data in one place, etc. Don't mix data and functions or public + private. If you start needing a lot of sections with coupled code + data, that's a sign you need to break up the class.
- Don't mix data with behaviors. If a class is mostly about its data, then keep any operations fairly terse, and rich behaviors on the data can be contained in separate visitor objects.
- Don't couple concepts. For instance, a rich data object should not know how to render itself--that should be done by a separate object. So the rich data object is not dependent on an arbitrary rendering library.