Overview
The Node.js C++ codebase follows specific style guidelines to maintain consistency and quality. This guide covers formatting, naming conventions, memory management, and best practices.
References
Coding guidelines are based on (in priority order):
This document
Google C++ Style Guide
C++ Core Guidelines
Pointer and Reference Declarations
Use left-leaning (C++ style) asterisks:
// Good
char * buffer;
const std ::string & name;
// Bad
char * buffer;
const std ::string & name;
Use C++ style comments (//) for both single-line and multi-line:
// A single-line comment.
// Multi-line comments
// should also use C++
// style comments.
Comments should:
Start with uppercase
End with a period
Indentation
2 Spaces for Blocks
if (foo)
bar ();
// Or with braces
if (foo) {
bar ();
baz ();
}
Braces are optional for single-statement bodies.
Namespaces receive no indentation.
4 Spaces for Continuations
VeryLongTypeName very_long_result = SomeValueWithAVeryLongName +
SomeOtherValueWithAVeryLongName;
Operators come before the line break.
Function Arguments
Align arguments vertically:
void FunctionWithAVeryLongName ( int parameter_with_a_very_long_name ,
double other_parameter_with_a_very_long_name ,
...);
If that doesn’t work, break after ( with 4 spaces:
void FunctionWithAReallyReallyReallyLongNameSeriouslyStopIt (
int okay_there_is_no_space_left_in_the_previous_line ,
...);
Initialization Lists
HandleWrap :: HandleWrap (Environment * env,
Local < Object > object,
uv_handle_t * handle,
AsyncWrap ::ProviderType provider)
: AsyncWrap (env, object, provider),
state_ (kInitialized),
handle_ (handle) {
Template Declarations
Add space after template:
template < typename T >
class FancyContainer {
...
};
Naming Conventions
PascalCase for Methods, Functions, and Classes
class FooBar {
public:
void DoSomething ();
static void DoSomethingButItsStaticInstead ();
void set_foo_flag ( int flag_value );
int foo_flag () const ; // Use const-correctness
};
Exception : Simple getters/setters use snake_case:
Getter: property_name()
Setter: set_property_name()
snake_case for Variables and Parameters
int FunctionThatDoesSomething ( const char* important_string ) {
const char * pointer_into_string = important_string;
}
snake_case_ for Private Class Fields
class Foo {
private:
int counter_ = 0 ;
};
Note the trailing underscore.
snake_case for C-like Structs
struct foo_bar {
int name;
};
Memory Management
Memory Allocation
Node.js provides allocation wrappers:
// Abort on Out-of-Memory
Malloc (size)
Calloc (count, size)
// Return nullptr on OOM
UncheckedMalloc (size)
UncheckedCalloc (count, size)
Use nullptr
Always use nullptr instead of NULL or 0:
// Good
if (ptr == nullptr )
return nullptr ;
// Bad
if (ptr == NULL )
return 0 ;
See C++ Core Guidelines ES.47 .
Explicit Pointer Comparisons
Use explicit comparisons with nullptr:
// Good
if (foo == nullptr )
if (foo != nullptr )
// Bad
if (foo)
if ( ! foo)
Smart Pointers
Use smart pointers to represent ownership:
// Prefer unique_ptr over shared_ptr
std :: unique_ptr < Foo > FooFactory ();
void FooConsumer ( std :: unique_ptr < Foo > ptr );
// Only use shared_ptr when sharing is necessary
std :: shared_ptr < Foo > SharedFooFactory ();
Never use std::auto_ptr - it’s deprecated.
See C++ Core Guidelines R.20 and R.21 .
Avoid Non-const References
Non-const references obscure which values are modified:
class ExampleClass {
public:
explicit ExampleClass ( OtherClass * other_ptr )
: pointer_to_other_ (other_ptr) {}
void SomeMethod ( const std :: string & input_param ,
std :: string * in_out_param ); // Pointer, not reference
const std :: string & get_foo () const { return foo_string_; }
void set_foo ( const std :: string & new_value ) { foo_string_ = new_value; }
void ReplaceCharacterInFoo ( char from , char to ) {
// Non-const reference OK here - method name indicates mutation
for ( char & character : foo_string_) {
if (character == from)
character = to;
}
}
private:
std ::string foo_string_;
OtherClass * pointer_to_other_; // Or std::unique_ptr<OtherClass>
};
AliasedBuffers for TypedArrays
When manipulating TypedArrays from C++:
// Create an AliasedBuffer
AliasedBuffer < uint32_t , v8 ::Uint32Array > data;
// Modify through natural operator semantics
data [ 0 ] = 12345 ;
See aliased_buffer.h for details.
Type Casting
Prefer Named Casts
// Good
static_cast < double > (value)
reinterpret_cast < uv_handle_t *> (handle)
// Bad
( double )value
Cast guidelines:
Use static_cast<T> when valid
Use reinterpret_cast only when necessary
Avoid C-style casts
dynamic_cast doesn’t work (Node.js built without RTTI)
See C++ Core Guidelines ES.48 and ES.49 .
Using auto
Be explicit about types when it aids readability:
// Good use of auto
for ( const auto & item : some_map) {
const KeyType & key = item . first ;
const ValType & value = item . second ;
// Clear types, avoids copies
}
// Prefer explicit types for clarity
const std ::string name = GetName ();
Use auto to avoid:
Noisy type names
Obvious types
Unimportant types
Include Directives
Don’t include *.h if *-inl.h is already included:
// Good
#include "util-inl.h" // Already includes util.h
// Bad
#include "util.h"
#include "util-inl.h"
Error Handling
Avoid Throwing JavaScript Errors in C++
Return error data to JavaScript and construct errors there:
void Foo ( const FunctionCallbackInfo < Value > & args ) {
Environment * env = Environment :: GetCurrent (args);
// Type-checking in JavaScript, assertions 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 error data in context object
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);
}
}
env -> SetMethod (target, "foo" , Foo);
JavaScript wrapper:
exports . foo = function ( str ) {
// Type-checking 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 in Nested C++ Methods
Throw JavaScript exceptions (isolate()->ThrowException()) as close to the return to JavaScript as possible.
Node.js is built without C++ exception handling. Code using throw, try, or catch will break.
Code Examples
Complete Class Example
class HttpParser : public AsyncWrap {
public:
HttpParser ( Environment * env , Local < Object > wrap );
~HttpParser () override ;
void Parse ( const char* data , size_t len );
size_t bytes_read () const { return bytes_read_; }
void set_max_header_size ( size_t size ) { max_header_size_ = size; }
private:
void OnHeadersComplete ();
void OnMessageComplete ();
size_t bytes_read_ = 0 ;
size_t max_header_size_ = 8192 ;
llhttp_t parser_;
};
Function Example
void AfterWrite ( uv_write_t * req , int status ) {
WriteWrap * req_wrap = WriteWrap :: from_req (req);
CHECK_NOT_NULL (req_wrap);
HandleScope scope ( req_wrap -> env ()-> isolate ());
Context ::Scope context_scope ( req_wrap -> env ()-> context ());
if (status == 0 ) {
req_wrap -> Done ( 0 );
} else {
req_wrap -> Done (status);
}
}
Linting
Run the C++ linter:
The linter is based on Google’s cpplint but doesn’t catch all Node.js-specific rules. Manual review is still necessary.
Building Guide Build Node.js from source
Testing Guide Write and run tests
C++ Codebase README C++ idioms and patterns
Google C++ Guide Google C++ Style Guide
Quick Reference
// Pointers and references
char * ptr; // Left-leaning asterisk
const Type & ref; // Left-leaning ampersand
// Naming
class MyClass {}; // PascalCase
void DoSomething (); // PascalCase
int my_variable; // snake_case
int private_field_; // snake_case with trailing underscore
// Memory
if (ptr == nullptr ) // Explicit nullptr comparison
auto ptr = std :: make_unique < T >(); // Smart pointers
// Comments
// C++ style comments only.
// Start with uppercase, end with period.
// Spacing
template < typename T > // Space after template
if (condition) { // No space before {
statement; // 2-space indent
}