Coding style
This document describes the preferred coding and documentation style for Licq and its plugins.
Who should read this?
This document is written for people who do development on Licq. Everyone who modifies parts of the source is a potential developer and should read on.
Why should i code and document my source in a special way?
Many people develop on the same code and everyone wants to easily read and understand it. You have your own style of coding of course and we respect this fact. But when you work on Licq sources you should respect our coding style as well and anyways it's a great improvement when we all agree to a common style so that everyone can easily read understand the code.
General
Indentation
Indentation is done with two spaces, not tabs. Every editor worth its name can be configured to map the tab key to two spaces. Use this! We repeat: two spaces, no tabs.
Rationale for spaces instead of tabs: None, it's just the way it is. Accept it!
switch statements
Indent case labels and clearly comment when you don't have a break at the end of a case branch.
// Do like this switch (clientVersion) { case ICQ_VERSION_9: useXtraz = true; // Fall through case ICQ_VERSION_8: port = DEFAULT_ICQ_PORT; break; } // Not like this switch (clientVersion) { case ICQ_VERSION_9: useXtraz = true; case ICQ_VERSION_8: port = DEFAULT_ICQ_PORT; break; }
Braces
Braces (opening and closing) should be placed on a line for themselves. The only exception to this rule is class (and struct) definitions where a ; should be appended after the closing brace. Example:
if (isActive() || force) { // do this } else { // do that }
If the statement body is a simple oneliner, you may skip the braces. But you wont be shot for using braces there as well. Use your own judgement and choose what you think makes the code the most readable for others.
Parens () with keywords and functions
- Do not put parens next to keywords. Put a space between.
- Do put parens next to function names.
- Do not use parens in return statements when it's not necessary.
Rationale: Keywords are not functions. By putting parens next to keywords keywords and function names are made to look alike.
Example:
if (true) { const int max = std::max(1, 2); return max; }
Breaking long lines
Try to break lines so that they don't exceed 80 characters. Rationale: It makes it possible to have two code windows next to each other, which is better than having one wide window. Use your judgment when breaking lines and indent the resulting line with 4 spaces (double indention).
Example:
while (plugin->isTheNameOfThisFunctionLongEnough() || plugin->maybeThisNameIsBetter()) { // Do something } std::string& pluginName = plugin->thisFunctionNameIsWayTooLongButIllustratesHowToBreakAssignmentLines();
Naming
First: remember that code is written once, but read numerous times. This is probably the most repeated sentence in coding style documents, but for good reasons. Code is read more times than it's written! Keep this in mind when reading the rest of this section.
Choosing a good name is a science in itself. But keep the above in mind (you know, code is read more than it's written), and choose descriptive names. Don't choose overly long names, but not too short either. Generally, the length should be related to the scope that the entity is visible in. E.g. member variables should have a longer name than the counter in a tight for-loop.
Take the time to come up with a good name. Remember, code is written once...
Don't use Hungarian notation. Rationale: In a strongly type language such as C++, the programmer is assisted by the compiler in making sure that two variables of different types can't be assigned to each other (unless implicit casting is in effect, but that's a different issue). But more importantly, Hungarian notation makes the code harder to read.
Example:
bool isActive; // Good name bool bActive; // Bad name, don't use Hungarian notation
Classes, structs and typedefs
Classes, structs and typedefs should be named using the CamelCase style with a upper cased first letter.
Examples:
class PluginObserver; struct LinkedList; typedef std::list<Plugin> PluginList;
The declaration for e.g. a class named PluginObserver should be in the file pluginobserver.h and the definition (or implementation) in pluginobserver.cpp. Except in some special cases, the rule is one class per file.
Methods and functions
Methods (member functions) and regular functions should also use CamelCase naming, but with a lower cased first letter.
Examples:
int Plugin::getId() const; void reset();
Member variables (attributes)
Historically, member variables have been prefixed with 'm' or 'm_' to show that they're member and not local variables. The problem with this (as with all Hungarian notation) is that it makes the code harder to read. But being able to separate member and local variables is important.
In Licq we prepend all member variables with 'my' to show that they are "owned" by the class and then use CamelCase for the rest of the name, e.g. myMemberVariable.
- Setters are easily created: void setMyVar(int var) { myVar = var; }
- You don't get stuck on a 'm' or 'm_' when reading code (my is a word with meaning), but you're still able to tell member and local variables apart.
Function arguments and local variables
CamelCase with first letter in lower case.
Examples:
int counter; Plugin* icqPlugin;
Commenting
- Use Doxygen tags to document classes, methods and functions.
- Document intent and stuff that isn't obvious from reading the code.
- Don't add useless comments. "Add 1 on i" is not a good comment.
- When fixing a bug, add a reference to the bug report if it makes sense. But don't use it as a way to avoid writing a good, descriptive comment.
- Comments should in almost all cases be put on a separate row before the code in question.
Doxygen
Doxygen comments should exist for (at least) all public classes and methods, and it should be in the header files. Before committing, generate the doxygen documentation and check it to make sure it's sane.
An example class follows.
/** * @class ExampleClient * Exemplifies the usage of Doxygen comments. * Here follows an longer comment that gives a more in-depth * overview of this class. */ class ExampleClient { private: /// Greeting to send when connecting (since this is a one-liner, we use ///) int myGreeting; public: /** * Construct a new client. * * @param[in] greeting String to send when connecting. */ ExampleClient(const std::string& greeting); /** * Connect to the example server. * After a connection is established, greeting messages * are exchanged and the server's response is returned. * * @param[in] server Server to connect to. * @param[out] ident String returned from the server as identification. * @return True if the connection was established; otherwisefalse. */ bool connect(const std::string& server, std::string* ident); };
Variables and Data Types
- Use const on local variables and arguments as a way to communicate that the variable is not supposed to be changed.
- Keep '*' and '&' close to the type, not the name: "Plugin* plugin;" not "Plugin *plugin".
- Use smart pointers where it makes sense.
- Never use global variables. Use the Singleton pattern instead.
- Unless the integer size matters, use int or unsigned int. Avoid using short, long, etc. unless it's really necessary.
- When integer size matters, for example in protocol handling, include stdint.h and use uint16_t, int64_t, etc. Do not use long as it has different size on 32 bit and 64 bit systems.
- Don't assume the integer type for defined data types, for example size_t and time_t, as they may be defined differently on another systems. Use "size_t len", not "unsigned int len".
The rest
Additional pointers:
- Don't write acronyms or abbrevations in all upper case. It's getUrl() not getURL().
- Prepend getters and setters with get and set respectively. Example: getVar() and setVar(int var).
- If a method has an empty body, put a comment there to tell the reader that it was left empty intentionally.
Plugin::~Plugin() { // Empty }
- One-liners:
// Prefer this if (user == NULL) return false; // to this if (user == NULL) return false;
- Don't compare boolean values. Use if (isActive()), not if (isActive() == true).
- When comparing integers and pointers, compare against a value.
// Do like this if (size != 0 && pointer == NULL) // Not like this if (size && !pointer) // However, when just checking a flag it's ok to omit the " != 0 " part if (userMode & VisibleFlag)
- All code should compile without warnings.
- Use named constants (e.g. ICQ_VERSION_8, LIST_SIZE), don't use magic numbers.
Post scriptum
Two spaces, not tabs. Braces on a line for themselves, with the same indention level as the statement (if, for etc) it belongs to. Use common sense and ask on the licq-dev MailingList if you additional questions after reading this.
For emacs users, you can use some stuff from Erik's .emacs file.
Recommended reading
- Code Complete, 2nd edition - Steve McConnell
- C++ Coding Standards: 101 Rules, Guidelines, and Best Practices - Herb Sutter, Andrei Alexandrescu