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

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.

Function arguments and local variables

CamelCase with first letter in lower case.

Examples:

int counter;
Plugin* icqPlugin;

Commenting

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

The rest

Additional pointers:

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