escargot/docs/Coding_Style_Guide.md
Hyukwoo Park 2b145ef54c
Update coding style guide (#296)
* reformat all single logical expression based on the new rule

Signed-off-by: HyukWoo Park <hyukwoo.park@samsung.com>
2019-07-03 15:42:41 +09:00

367 lines
10 KiB
Markdown

# C++ Style Guide
The goal of this document is to describe our C++ style convention. Our
coding style generally follows
[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html).
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__` .
```cpp
#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.
```cpp
if (condition) {
...
}
if (condition) {
a = b;
}
```
Write the condition of `if` scope with explicit expressions. For details, please refer to [Code readability](#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.
```cpp
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](#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.
```cpp
if (condition1 ||
(condition2 && condition3)) {
...
}
```
Write the conditions among binary operators with explicit expressions. For details, please refer to [Code readability](#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.
```cpp
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).
```cpp
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.
```cpp
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.
```cpp
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.
```cpp
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.
```cpp
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.
```cpp
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.
```cpp
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.
```cpp
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.
```cpp
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
```cpp
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.
```cpp
// `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
```cpp
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.
```cpp
A::releaseMemeber() {
// free(m_pointerMemeber); if you needs
m_pointerMemeber = nullptr;
}
```
### Supported assertions macro list in Escargot
```cpp
...
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.
```diff
++ // 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())
```