Skip to content

AGS Coding Conventions (Cpp)

Ivan Mogilko edited this page Jan 18, 2019 · 3 revisions

The following are the coding conventions for C++ parts of AGS (this includes engine, script compiler, and limited parts of the editor which are still written in C++ as of 2019).

These definitions are not set in place as absolute rule, but as guidelines that should be followed to promote consistency and uniformity throughout the source code of the AGS run-time engine. They may be reviewed and reconsidered over time, but changes should have a good rationale behind them. In regards to coding style please remember that it primarily aims to make the code consistent and convenient to work with for any average person rather than match someone's personal preference or achieve ultimate perfection.

Formatting

First of all, runtime engine of AGS is an old program and consists of multiple historical "layers" of code built on top of each other. For that reason you may meet a lot of different code styles in there. When writing completely new code unit or doing major refactoring we'd ask you to follow our formatting convention. When doing smaller changes and fixes in the existing code please try to keep formatting consistent with the surrounding code (unless it's too bad) even if it does not match our convention: this is again to make it easier to review and work with the code.

Indentation and whitespace

We use spaces for indentation. Each level of indentation should increase by 4 spaces. There are specific cases where whitespace helps improve readability of code, beyond where it is required. Use common sense and discretion, but the following are some examples of proper whitespace usage.

Horizontal Whitespace

Horizontal whitespace should be used appropriately for indenting lines, and spacing out the code on a line. You should never end a line with trailing whitespace. Whitespace should also be used in the following ways:

  • Surrounding conventional operators, class inheritance, and ternary operators:
 int a = 42;
 a *= c + d;

 class Derived : public Base
 {
 };

 a = (true ? b : c);
  • To separate a C++ reserved keyword from an opening parenthesis:
 if (true)
 {
 }
 
 while (false)
 {
 }
  • After a comma:
 int a, b, c;
 
 function(a, b, c);
  • After a semicolon, if there is more on that line (including comments):
 for (int i = 0; i < 42; ++i)
 {
 }
 
 int id; // used to store the identification number

Vertical Whitespace

Vertical whitespace should be used sparingly to separate functions or other large blocks of code, to help maintain readability. You should not have several blank lines at the start or end of a function, or in-between statements, declarations, and so forth. Use discretion as necessary, but bear in mind that the goal is to make the code readable.

Each file should end with a blank line as some compilers require it or may warn if they do not.

Comments

Generally, line comments (//) should be preferred over block level comments (/* */) as block comments cannot be nested.

Braces

"Curly braces", { and }, should occupy a line by themselves (trailing comments are acceptable also). They should be at one level of indentation lower than the code they contain. This helps as a vertical guide (in addition to the indentation) in finding where a particular block of code starts or ends.

 { // opening brace
     // this code is inside the braces
 } // closing brace

Curly braces may be omitted after conditional or loop statements if there is only one line of code in the following block. In case of combined if/else statement only omit curvy braces if each of the blocks consist of one line.

 if (a == 0)
     DoSomething();

 if (a == 0)
     DoSomething();
 else
     DoSomethingElse();

 for (int i = 0; i < 10; ++i)
     NextIteration(i);

 // but...
 if (a == 0)
 {
     DoOnlyOneStatement();
 }
 else
 {
     DoMultiplePart1();
     DoMultiplePart2();
 }

In case of nested conditions or loops we also permit omitting braces but only if there are no more than two levels of nesting.

 if (do_processing)
     for (int i = 0; i < 10; ++i)
         Process(i);

Please use discretion when omitting curvy braces and try to not overdo, especially if surrounded by complex code with lots of nesting.

Naming Conventions

The user types: classes, structs, enums and typedefs, - as well as global entities such as namespaces, global constants and global variables should be named using upper CamelCase notation. It is also suggested to begin global variables with 'g_' or 'gl_' prefix.

class AnimatedButton;
struct Rect;

typedef std::unordered_map<String, Object> MapOfObjects;

Bitmap *gl_VirtualScreen;

When creating aliases (typedefs) for standard containers and smart pointers you may add some kind of simple prefix indicating the aliased type, for example:

typedef std::shared_ptr<Bitmap> PBitmap;

typedef std::vector<int> VInts;

Enum's constants (not enum type itself) while being in CamelCase should be prefixed with letter 'k'. Constant names are also allowed to have two or more parts separated by underscores ('_'). Constants within same enum might share a common first word indicating their relation, for example:

enum TypeOfAction
{
    kAction_Default,
    kAction_Special,
    kAction_Other
};

Class methods and public data fields should also follow CamelCase notation. Private class fields should be named using lower camelCase and prefixed with an underscore ('_') to distinguish single-worded members from local variables (see further):

class MyClass
{
public:
    MyClass();
    ~MyClass();
    void PublicMethod();

    int PublicField;

private:
    void PrivateMethod();

    int _privateField;
}

Local variables and function arguments should be named in all lowercase with underscores separating parts of name when necessary (but not as a prefix).

void SomeFunction(int func_arg1, int func_arg2)
{
    int local_var = 0;
}

Names should be as descriptive as possible, without being overly verbose. Functions particularly should indicate what they actually do, so a name such as "GetID()" would be preferred over simply naming the function "ID()".