Skip to content

Coding standard

Meow edited this page Apr 5, 2022 · 3 revisions

Coding standard guide

(wip)

This guide aims to explain the coding standards that I as the lead developer expect. Consistent coding style is important to ensure that your code is readable by anyone in the project, and makes maintenance of the code far easier, as well as making errors easier to track down.

First and foremost...

...use clang-format with its default settings. It typically does a great job at formatting your code to comply with almost all that this document presents.

You may download clang-format and other LLVM clang tools here

clang-format -i filename.c

Indentation

Indent your code with two spaces ( ). Do not use the tab character (\t).

Good

int main() {
  if (something()) {
    do_something_useful();
  }

  return 0;
}

Bad

int main() {
	if (something()) {
		do_something_useful();
	}

	return 0;
}

Comments

Use C-style comments (/* comment */), do not use C++ comments (// comment)

Good

/* Some comment */
...

Bad

// Some comment
...

Comment must be on its own line. Do not put the comment on a line with code.

Good

/* Comment */
some_code();

Bad

some_code(); /* Comment */

Documentation

We use doxygen for docs. Put the documentation in the header files, not the source files. The precise style of documentation is as follows:

/**
 * @brief A short sentence describing what the function does, ending with a full-stop (dot).
 * 
 * A more detailed description. May span multiple
 * lines or sentences. This is optional but highly
 * recommended.
 *
 * ```
 * example of usage here
 * ```
 *
 * @param p Description of the parameter
 * @return Description of the return value
 */
int func(int p);

Example

/**
 * @brief Checks whether or not the game should exit.
 *
 * ```
 * int main() {
 *   GameState *state = (Gamestate *)malloc(sizeof(GameState));
 *
 *   while (!game_should_exit(state)) {
 *     game_logic(state);
 *   }
 *
 *   free(state);
 *
 *   return 0;
 * }
 * ```
 *
 * @param state Current game state
 * @return uint8_t Whether or not the game should exit.
 */
uint8_t game_should_exit(GameState *state);

Naming convention

Variable names

Always name variables something useful. Ideally the variable should be a brief description of what it's supposed to do.

Good

float velocity = 1.0f;
float mass = 10.0f;
float momentum = velocity * mass;

Bad

float a = 1.0f;
float b = 10.0f;
float c = a * b;

Variable name style

The goal of a consistent variable name style is to always be able to tell what kind of a thing each variable or identifier (name) is simply by looking at its name. This significantly speeds up the development process as you no longer need to rely on your IDE to tell you what you're looking at. The style we'll be using is inspired by the style of the Ruby programming language.

Please always use snake_case for local (scoped) variable names and function argument names. Use SCREAMING_SNAKE_CASE for globals and #define'd constants.

Good

#define PLAYER_FRICTION 0.99f;
...
float calculate_momentum(float player_velocity, float player_mass) {
  float momentum = player_velocity * player_mass;

  return momentum * PLAYER_FRICTION;
}

Bad

#define PlayerFriction = 0.99f;

float calculateMomentum(float PlayerVelocity, float playerMass) {
  float MOMENTUM = PlayerVelocity * playerMass;

  return MOMENTUM * PlayerFriction;
}

Function name style

Function names should always use snake_case.

Good

int function_name() { }
int do_something_useful() { }
int player_kill(Player *player) { }

Bad

int functionName() { }
int DoSomethingUseful() { }
int PLAYERKILL(Player *player) { }

Struct and class name style

Structs and classes should always use CamelCase.

Good

typedef struct Player {
  ...
} Player;
class PlayerData {
  ...
};

Bad

typedef struct player {
  ...
} player;
class playerData {
  ...
};

Classes

Everything should be private by default. Only use public if you really need to have a method/variable externally-available.

Good

class Foo {
public:
  Vector3 velocity;

  void do_thing();

private:
  int buffer_position;
  float internal_float;

  void determine_how_to_do_thing();
};

Bad

class Foo {
public:
  Vector3 velocity;
  int buffer_position;
  float internal_float;

  void do_thing();
  void determine_how_to_do_thing();
};

Newlines

Try to separate the "logical" blocks of your program by using newlines. They make it much easier to read your code. The logical blocks are variables or function calls that vaguely share the same overall "theme" or goal.

Here's when to put a newline:

  • After a block of variable declarations
  • After closing bracket (})
    • UNLESS the next line only contains a closing bracket
  • After a block of function calls
  • Before if, for, while and other logical blocks
  • Before case label
  • Before return statement
  • Before and after #ifdef / #ifndef
  • Before and after #endif
  • After a block of #define statements
  • After a block of #include statements
  • At the very end of the file. Your files should end with a single empty line.

Here's when NOT to put a newline

  • At the start of the file
  • After two consequent newlines (avoid having 2 empty lines next to each other)
  • Directly after block opening (after {)

Good

#include <iostream>

#ifdef _WIN32

typedef WideChar char16_t

#endif

int main() {
  const int a = 123;
  const float b = 1.0f;

  const GameState *state = game_new_state();

  if (game_is_paused(state)) {
    switch (state->status) {
    case Status::WIN:
      game_win(state);
      
      break;

    case Status::LOSS:
      game_loss(state);

      break;

    default:
      game_loss(state);
    }

    return 0;
  } else {
    if (state->tick > a)
      return 1;

    game_tick(state);
  }

  return 0;
}

Bad

#include <iostream>
#ifdef _WIN32
typedef WideChar char16_t
#endif
int main() {
  const int a = 123;
  const float b = 1.0f;
  const GameState *state = game_new_state();
  if (game_is_paused(state)) {
    switch (state->status) {
    case Status::WIN:
      game_win(state);
      break;
    case Status::LOSS:
      game_loss(state);
      break;
    default:
      game_loss(state);
    }
    return 0;
  } else {
    if (state->tick > a)
      return 1;
    game_tick(state);
  }
  return 0;
}

Brackets and spaces

In general, no spaces after opening brackets, and before closing brackets. The only exception are curly brackets ({ }). You should put spaces before opening round brackets (() and after closing round brackets ()). You should put a space after closing square bracket ([).

You should also surround basic binary operators (+ - / * == || && and others) by spaces on both sides.

When defining a pointer, put a space before *, but not after.

Unary operators should not have any spacing

Good

int a[8] = { ... };
int *ptr;

if (a[0] == 10 && !a[1]) {
  ...
}

Bad

int a[ 8 ] = {...};
int* ptr;

if( a[ 0 ]==10&& ! a [ 1 ]){
  ...
}

Variable const-ness

Try to use const by default. Only use non-const if you absolutely cannot think of a way to use it.

Good

/* Game state is only read, so it can be const */
const GameState *game_state = ...;
...
if (game_is_paused(game_state)) {
  ...
}

Bad

/* Game state is only read, but is mutable here. This could have consequences if you're not careful! */
GameState *game_state = ...;
...
if (game_is_paused(game_state)) {
  ...
}

Avoid reinventing the wheel

Standard libraries typically provide many useful functions. Avoid reinventing the wheel and try to find out if C11 / C++11 already have something that does what you want to do.

Game Design, Ressources & Protocols

Clone this wiki locally