Skip to content

Latest commit

 

History

History
367 lines (292 loc) · 10.3 KB

Coding_Style_Guide.md

File metadata and controls

367 lines (292 loc) · 10.3 KB

C++ Style Guide

The goal of this document is to describe our C++ style convention. Our coding style generally follows Google C++ Style Guide. This document highlights core coding style.

Header Files

#define Guard

Use #define directive in all header files to prevent multiple inclusion of header files. The format of the identifier name should be __[Project Name][HeaderFileName]__. For example, for MyVeryCoolClass.h in Escargot project, the guard should be __EscargotMyVeryCoolClass__ .

#ifndef __EscargotMyVeryCoolClass__
#define __EscargotMyVeryCoolClass__
...
#endif

Use #include only in .cpp files

To prevent possible loops in header file inclusion, try to include header files only in .cpp files. In this case, the order of header file inclusion is important.

'including headers in a header' is allowed for the followings. otherwise, try to use forward declarations.

  • Class Inheritance
  • Class member as an instance

Formatting

Indentation

Use spaces only, and use 4 spaces at a time. Tabs should not be used.

Empty Lines between Functions

Add one empty line between function implementations in .cpp files. Add zero lines between function declarations (or implementations) in .h files to explicitly group related functions. Add one empty line to separate between these groups.

if Statements

Add a space before the opening parenthesis, and between closing parenthesis and opening curly brace. Always enclose statements with a pair of braces even for a single line statement.

if (condition) {
    ...
}

if (condition) {
    a = b;
}

Write the condition of if scope with explicit expressions. For details, please refer to Code readability

for, while, do-while Statements

Add a space before the opening parenthesis, and between closing parenthesis and opening curly brace. Always have statements with a pair of braces even for a single line statement.

while (condition) {
    ...
}

while (condition) {
    a = b;
}

Write the condition of for, while, and do-while scopes with explicit expressions. For details, please refer to Code readability

Binary Operators

When binary operators cannot fit in the same line, split operands after the binary operators, and align operands with the first operand. Try to use parentheses as much as possible even if the correct precedence can be derived without using parentheses. This often helps readers to understand the code better.

if (condition1 ||
   (condition2 && condition3)) {
    ...
}

Write the conditions among binary operators with explicit expressions. For details, please refer to Code readability

Classes

Constructors

Initialization of the member variables should be done in the initializer as much as possible. When initializing member variables in a constructor, always split each initializer on a separate line, and align the commas with the colon.

Dog::Dog(String name, Breed breed)
    : Animal()
    , m_name(name)
    , m_breed(breed)
{
...
}

Calling other constructors inside a constructor should be avoided. This is in preparation for an environment where we cannot use c++11 features (such as embedded devices).

Dog::Dog()
    , m_isAnimal(true)
{
}

Dog::Dog(String name, Breed breed)
    : Dog()  //<--- Not allowed
    , m_name(name)
    , m_breed(breed)
{
...
}

Write it one by one.

Dog::Dog()
    , m_isAnimal(true)
    , m_name(String::emptyString)
    , m_breed(Breed::emptyBreed)
{
}

Dog::Dog(String name, Breed breed)
    : m_isAnimal(true)
    , m_name(name)
    , m_breed(breed)
{
...
}

Class Modifiers

Do not indent class modifiers (i.e., public, protected, and private) in a class declaration.

class Dog {
public:
    ...
private:
    ...
}

Functions

Function Names

Use camelcases when naming a function.

Function Calls

Write a function call all in the same line if it fits. If not, split the function call into multiple lines by either adding a newline after the assignment operator or between function parameters. When splitting between parameters, align them with the first parameter. In addition, do not add spaces before and after the first and last function parameter, respectively.

int val = foo(arg1, arg2, arg3);

int val =
    foo(arg1, arg2, arg3);

int val = foo(arg1, arg2
              arg3);

Function Declaration and Definition

Use named parameters in function declarations.

returnType functionName(int, int); // Not allowed
returnType functionName(int arg1, int arg2);  // Use named parameters

Return type should be on the same line as the function declaration (and definition) if it fits. If not, split function parameters into multiple lines, and align them with the first parameter.

returnType functionName(int arg1,
                        int arg2);

If the first parameter does not fit in a line, write parameters in the next line with 4 space indent.

returnType functionName(
    int arg1, int arg2);

The opening curly brace should be on its own in the next line. A Closing curly brace should be on the next line as the opening brace.

returnType functionName()
{
    ...
}

Short and Empty Functions

Do not write a function all in one line even for a very short function that can be fit in one line. One exception is an empty, inline function that is declared in a header file.

void f() { } // Allowed only in a header file

int g()
{
    return 0;
}

C++ Features

Run-Time Type Information (RTTI)

Do not use Run Time Type Information.

C++11

Use of C++11 features are encouraged. In addition, use of C++11 compatible style formatting is encouraged, e.g., use A<B<int>> instead of A<B<int> >

Use of try-catch statements

Do not use try-catch statements except throwing an Exception.

Assertions and nullptr

Basic principle

  • When using a pointer type variable, be sure to add the Assertions statement if you do not want to consider the situation where the value is nullptr, if you do not want to add assertions, be sure to write your defense code.
  • In our strategy, Escargot will be terminated along with an error message when GC memory allocation fails.
  • If you use c-style allocator like malloc/free, you should check allocation fail.
  • While you don't use GC allocator, check before dereferencing with ASSERT or if, depends on the expected behavior what you want to achieve.

Add an assertion in the following situations.

  • If the function argument is a pointer type
void A::functionA(B* arg1, int arg2) {
    ASSERT(arg1 != nullptr);
}

Handling a nullable pointer

There are the following choices where you handle a pointer which can be nullable.

  • Use NULLABLE macro

Add NULLABLE definition before the type of pointers. NULLABLE is nothing but a notation which explicitly shows whether or not the given pointer can be nullable.

// `NULLABLE` is predefined in Escargot as below.
#define NULLABLE

...

NULLABLE Object* A::functionA(NULLABLE ObjectB* b, ObjectC* c) {

    // NOTE: If NULLABLE isn't prepended on the type of the given parameter,
    // ASSERTION is preferred as below.
    ASSERT(c != nullptr);

    ...

    // Access violation should be considered for a nullable pointer.
    if ((b != nullptr) && b->isLoaded())
    {
        ...
    }

    // Use `NULLABLE` when calling a function which can return a nullable pointer.
    NULLABLE Object* obj = c->getObject(...);
    // NULLABLE auto obj = c->getObject(...);

    if (cnd) {
        return new Object;
    } else {
        return nullptr;
    }
}
  • Use Nullable template only for javascript binding interfaces
Nullable<Object> A::functionA() {
    if (cnd) {
        return valueOfObjectClass;
    } else {
        // return nullptr;
        return Nullable<Object>();
    }
}
  • Use reference instead of pointer

Be sure to explicitly assign nullptr if you need to release it.

A::releaseMemeber() {
    // free(m_pointerMemeber); if you needs
    m_pointerMemeber = nullptr;
}

Supported assertions macro list in Escargot

...
ASSERT()
ASSERT_NOT_REACHED()
RELEASE_ASSERT_SHOULD_NOT_BE_HERE()
// ETC, See StarfishBase.h for more information
...

Comments

Comment Style

Both // and /* */ style comments can be used, although // style is much preferred.

Add comment for newly function

  • Having a clear function name and parameters will solve many readability problems.
  • We are not aiming to generate an API doc. We think it is unneeded.
  • We don't need to write comment for every function. We prefer to write comments at a place where function definition in cpp file
  • What we want to write are (unusual, important, or pre/post conditions of) function behaviors that are difficult to deliver to readers by code. Some examples include, "This function should be called after finishing xxx, or it will give you yyy."
  • Since writing comments is optional, we do not want to have rigid formats. (Also, not updating comments after updating actual code is bad). We are thinking of having simple comments starting with // in the header file above the function we want to add comments. (Again this is more like informal comment rule.)
  • Which function to write the comment is more like up to developers. Each developer needs to decide what to write comments (or not)

Code readability

Make sure your code is obvious and readable with the following conventions.

++ // NOTE: the statements in green are preferred.

// Use a more explicit expression for `nullptr` checking.
- if (ptr)
+ if (ptr != nullptr)

- ASSERT(ptr)
+ ASSERT(ptr != nullptr)

// Using the obvious meaning is preferred. ((e.g) the following usage for `strlen`)
- if (verbose && strlen(verbose))
+ if ((verbose != nullptr) && (strlen(verbose) > 0))

// Regarding readability, The primary rule is to use explicit expression consists of left and right operand
// and not to use single operand logical operator such as (`!`).
// You can use single operand logical expression only if the operand has boolean type.
// Other cases are not recommended to omit the right operand.

bool isLoaded();
bool sunnyToday();
int howMuchLoaded();

- if (!isLoaded() && howMuchLoaded() && ptr)
+ if (!isLoaded() && howMuchLoaded() != 0 && ptr != nullptr)
+ if (sunnyToday())