node/CPP_STYLE_GUIDE.md
Refael Ackermann 0f8eaa4712 doc,meta: add references to outside C++ guides
with explicit priorities

PR-URL: https://github.com/nodejs/node/pull/23317
Reviewed-By: Anna Henningsen <anna@addaleax.net>
2018-10-13 18:07:55 -04:00

329 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# C++ Style Guide
## Table of Contents
* [Guides and References](#guides-and-references)
* [Formatting](#formatting)
* [Left-leaning (C++ style) asterisks for pointer declarations](#left-leaning-c-style-asterisks-for-pointer-declarations)
* [C++ style comments](#c-style-comments)
* [2 spaces of indentation for blocks or bodies of conditionals](#2-spaces-of-indentation-for-blocks-or-bodies-of-conditionals)
* [4 spaces of indentation for statement continuations](#4-spaces-of-indentation-for-statement-continuations)
* [Align function arguments vertically](#align-function-arguments-vertically)
* [Initialization lists](#initialization-lists)
* [CamelCase for methods, functions, and classes](#camelcase-for-methods-functions-and-classes)
* [snake\_case for local variables and parameters](#snake_case-for-local-variables-and-parameters)
* [snake\_case\_ for private class fields](#snake_case_-for-private-class-fields)
* [snake\_case\_ for C-like structs](#snake_case_-for-c-like-structs)
* [Space after `template`](#space-after-template)
* [Memory Management](#memory-management)
* [Memory allocation](#memory-allocation)
* [Use `nullptr` instead of `NULL` or `0`](#use-nullptr-instead-of-null-or-0)
* [Ownership and Smart Pointers](#ownership-and-smart-pointers)
* [Others](#others)
* [Type casting](#type-casting)
* [Using `auto`](#using-auto)
* [Do not include `*.h` if `*-inl.h` has already been included](#do-not-include-h-if--inlh-has-already-been-included)
* [Avoid throwing JavaScript errors in C++ methods](#avoid-throwing-javascript-errors-in-c)
* [Avoid throwing JavaScript errors in nested C++ methods](#avoid-throwing-javascript-errors-in-nested-c-methods)
## Guides and References
The Node.js C++ codebase strives to be consistent in its use of language
features and idioms, as well as have some specific guidelines for the use of
runtime features.
Coding guidelines are based on the following guides (highest priority first):
1. This document
2. The [Google C++ Style Guide][]
3. The ISO [C++ Core Guidelines][]
In general code should follow the C++ Core Guidelines, unless overridden by the
Google C++ Style Guide or this document. At the moment these guidelines are
checked manually by reviewers, with the goal to validate this with automatic
tools.
## Formatting
Unfortunately, the C++ linter (based on [Googles `cpplint`][]), which can be
run explicitly via `make lint-cpp`, does not currently catch a lot of rules that
are specific to the Node.js C++ code base. This document explains the most
common of these rules:
### Left-leaning (C++ style) asterisks for pointer declarations
`char* buffer;` instead of `char *buffer;`
### C++ style comments
Use C++ style comments (`//`) for both single-line and multi-line comments.
Comments should also start with uppercase and finish with a dot.
Examples:
```c++
// A single-line comment.
// Multi-line comments
// should also use C++
// style comments.
```
The codebase may contain old C style comments (`/* */`) from before this was the
preferred style. Feel free to update old comments to the preferred style when
working on code in the immediate vicinity or when changing/improving those
comments.
### 2 spaces of indentation for blocks or bodies of conditionals
```c++
if (foo)
bar();
```
or
```c++
if (foo) {
bar();
baz();
}
```
Braces are optional if the statement body only has one line.
`namespace`s receive no indentation on their own.
### 4 spaces of indentation for statement continuations
```c++
VeryLongTypeName very_long_result = SomeValueWithAVeryLongName +
SomeOtherValueWithAVeryLongName;
```
Operators are before the line break in these cases.
### Align function arguments vertically
```c++
void FunctionWithAVeryLongName(int parameter_with_a_very_long_name,
double other_parameter_with_a_very_long_name,
...);
```
If that doesnt work, break after the `(` and use 4 spaces of indentation:
```c++
void FunctionWithAReallyReallyReallyLongNameSeriouslyStopIt(
int okay_there_is_no_space_left_in_the_previous_line,
...);
```
### Initialization lists
Long initialization lists are formatted like this:
```c++
HandleWrap::HandleWrap(Environment* env,
Local<Object> object,
uv_handle_t* handle,
AsyncWrap::ProviderType provider)
: AsyncWrap(env, object, provider),
state_(kInitialized),
handle_(handle) {
```
### CamelCase for methods, functions, and classes
Exceptions are simple getters/setters, which are named `property_name()` and
`set_property_name()`, respectively.
```c++
class FooBar {
public:
void DoSomething();
static void DoSomethingButItsStaticInstead();
void set_foo_flag(int flag_value);
int foo_flag() const; // Use const-correctness whenever possible.
};
```
### snake\_case for local variables and parameters
```c++
int FunctionThatDoesSomething(const char* important_string) {
const char* pointer_into_string = important_string;
}
```
### snake\_case\_ for private class fields
```c++
class Foo {
private:
int counter_ = 0;
};
```
### snake\_case\_ for C-like structs
For plain C-like structs snake_case can be used.
```c++
struct foo_bar {
int name;
}
```
### Space after `template`
```c++
template <typename T>
class FancyContainer {
...
}
```
## Memory Management
### Memory allocation
- `Malloc()`, `Calloc()`, etc. from `util.h` abort in Out-of-Memory situations
- `UncheckedMalloc()`, etc. return `nullptr` in OOM situations
### Use `nullptr` instead of `NULL` or `0`
What it says in the title.
### Ownership and Smart Pointers
"Smart" pointers are classes that act like pointers, e.g.
by overloading the `*` and `->` operators. Some smart pointer types can be
used to automate ownership bookkeeping, to ensure these responsibilities are
met. `std::unique_ptr` is a smart pointer type introduced in C++11, which
expresses exclusive ownership of a dynamically allocated object; the object
is deleted when the `std::unique_ptr` goes out of scope. It cannot be
copied, but can be moved to represent ownership transfer.
`std::shared_ptr` is a smart pointer type that expresses shared ownership of a
dynamically allocated object. `std::shared_ptr`s can be copied; ownership
of the object is shared among all copies, and the object
is deleted when the last `std::shared_ptr` is destroyed.
Prefer to use `std::unique_ptr` to make ownership
transfer explicit. For example:
```cpp
std::unique_ptr<Foo> FooFactory();
void FooConsumer(std::unique_ptr<Foo> ptr);
```
Never use `std::auto_ptr`. Instead, use `std::unique_ptr`.
## Others
### Type casting
- Always avoid C-style casts (`(type)value`)
- `dynamic_cast` does not work because RTTI is not enabled
- Use `static_cast` for casting whenever it works
- `reinterpret_cast` is okay if `static_cast` is not appropriate
### Using `auto`
Being explicit about types is usually preferred over using `auto`.
Use `auto` to avoid type names that are noisy, obvious, or unimportant. When
doing so, keep in mind that explicit types often help with readability and
verifying the correctness of code.
```cpp
for (const auto& item : some_map) {
const KeyType& key = item.first;
const ValType& value = item.second;
// The rest of the loop can now just refer to key and value,
// a reader can see the types in question, and we've avoided
// the too-common case of extra copies in this iteration.
}
```
### Do not include `*.h` if `*-inl.h` has already been included
Do
```cpp
#include "util-inl.h" // already includes util.h
```
instead of
```cpp
#include "util.h"
#include "util-inl.h"
```
### Avoid throwing JavaScript errors in C++
When there is a need to throw errors from a C++ binding method, try to
return the data necessary for constructing the errors to JavaScript,
then construct and throw the errors [using `lib/internal/errors.js`][errors].
Note that in general, type-checks on arguments should be done in JavaScript
before the arguments are passed into C++. Then in the C++ binding, simply using
`CHECK` assertions to guard against invalid arguments should be enough.
If the return value of the binding cannot be used to signal failures or return
the necessary data for constructing errors in JavaScript, pass a context object
to the binding and put the necessary data inside in C++. For example:
```cpp
void Foo(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
// Let the JavaScript handle the actual type-checking,
// only assertions are placed in C++
CHECK_EQ(args.Length(), 2);
CHECK(args[0]->IsString());
CHECK(args[1]->IsObject());
int err = DoSomethingWith(args[0].As<String>());
if (err) {
// Put the data inside the error context
Local<Object> ctx = args[1].As<Object>();
Local<String> key = FIXED_ONE_BYTE_STRING(env->isolate(), "code");
ctx->Set(env->context(), key, err).FromJust();
} else {
args.GetReturnValue().Set(something_to_return);
}
}
// In the initialize function
env->SetMethod(target, "foo", Foo);
```
```js
exports.foo = function(str) {
// Prefer doing the type-checks in JavaScript
if (typeof str !== 'string') {
throw new errors.codes.ERR_INVALID_ARG_TYPE('str', 'string');
}
const ctx = {};
const result = binding.foo(str, ctx);
if (ctx.code !== undefined) {
throw new errors.codes.ERR_ERROR_NAME(ctx.code);
}
return result;
};
```
#### Avoid throwing JavaScript errors in nested C++ methods
When you have to throw the errors from C++, try to do it at the top level and
not inside of nested calls.
Using C++ `throw` is not allowed.
[C++ Core Guidelines]: http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
[Google C++ Style Guide]: https://google.github.io/styleguide/cppguide.html
[Googles `cpplint`]: https://github.com/google/styleguide
[errors]: https://github.com/nodejs/node/blob/master/doc/guides/using-internal-errors.md