Google Style Guide in C ++. Part 10

Part 1. Introduction
...
Part 9. Comments
Part 10. Formatting
...


This article is a translation of part of the Google style guide in C ++ into Russian.
Original article (fork on github), updated translation .

Formatting


The coding and formatting style is arbitrary, but the project is much easier to manage if everyone follows the same style. Although someone may disagree with all the rules (or use what they are used to), it is very important that everyone follows the same rules in order to easily read and understand someone else's code.
For correct formatting, we created a settings file for emacs .

Line length


It is advisable to limit the length of lines of code to 80 characters.
This rule is a bit controversial, but the bulk of existing code adheres to this principle, and we also support it.

For
adherents of the rule, they say that longer lines are not needed, and constantly adjusting the size of the windows is tiresome. In addition, some place windows with code next to each other and cannot arbitrarily increase the width of the windows. At the same time, a width of 80 characters is a historical standard, why change it? ..

Against The

other side claims that long lines can improve code readability. 80 characters is a relic of the 1960s mainframe. Modern screens may well show longer lines.

A verdict of

80 characters is the maximum.

A string can exceed a limit of 80 characters if:

  • . , URL-, 80 .
  • /, 80 . , .
  • include.
  • using

-ASCII


Non-ASCII characters should be used as rarely as possible, the encoding should be UTF-8.
You do not have to hardcode strings to show to the user (even English), so Non-ASCII characters should be rare. However, in some cases it is permissible to include such words in the code. For example, if the code parses data files (with non-English encoding), it is possible to include national delimiter words in the code. In a more general case, unit test code may contain national strings. In these cases, UTF-8 encoding should be used, as it is understood by most utilities (which understand not only ASCII).

Hex is also valid, especially if it improves readability. For example, "\ xEF \ xBB \ xBF" or u8 "\ uFEFF"- An inextricable space of zero length in Unicode, and which should not be displayed in the correct UTF-8 text.

Use the u8 prefix so that literals like \ uXXXX are encoded in UTF-8. Do not use it for lines containing non-ASCII characters already encoded in UTF-8 - you can get clumsy text if the compiler does not recognize the source code as UTF-8.

Avoid using C ++ 11 char16_t and char32_t characters since they are needed for non-UTF-8 lines. For the same reasons, do not use wchar_t (except when working with the Windows API using wchar_t ).

Spaces vs. Tabs


Use only spaces for indentation. 2 spaces for one indent.
We use spaces for indentation. Do not use tabs in your code - configure your editor to insert spaces when you press Tab.

Function declarations and definitions


Try to place the type of the return value, the name of the function and its parameters on one line (if everything fits). Break a list of parameters too long into lines, just like arguments in a function call.

An example of the correct function design:

ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
  DoSomething();
  ...
}

If one line is not enough:

ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2,
                                             Type par_name3) {
  DoSomething();
  ...
}

or, if the first parameter also does not fit:

ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
    Type par_name1,  //  4 
    Type par_name2,
    Type par_name3) {
  DoSomething();  //  2 
  ...
}

A few notes:

  • Choose good names for options.
  • You can omit the parameter name if it is not used in the function definition.
  • , , . .
  • .
  • .
  • .
  • . .
  • , , .
  • .
  • .
  • β€” 2 .
  • When transferring parameters to another line, indent 4 spaces.

You can omit the name of unused parameters if this is obvious from the context:

class Foo {
 public:
  Foo(const Foo&) = delete;
  Foo& operator=(const Foo&) = delete;
};

Unused parameters with non-obvious context should be commented out in the function definition:

class Shape {
 public:
  virtual void Rotate(double radians) = 0;
};

class Circle : public Shape {
 public:
  void Rotate(double radians) override;
};

void Circle::Rotate(double /*radians*/) {}

//   -  -     ,
//    .
void Circle::Rotate(double) {}

Try to use attributes and macros at the beginning of an advertisement or function definition,
up to the type of the return value:
ABSL_MUST_USE_RESULT bool IsOk();

Lambdas


Format the parameters and body of the expression in the same way as a regular function, the list of captured variables is like a normal list.

To capture variables by reference, do not put a space between the ampersand (&) and the variable name.

int x = 0;
auto x_plus_n = [&x](int n) -> int { return x + n; }

Short lambdas can be used directly as an argument to a function.

std::set<int> blacklist = {7, 8, 9};
std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1};
digits.erase(std::remove_if(digits.begin(), digits.end(), [&blacklist](int i) {
               return blacklist.find(i) != blacklist.end();
             }),
             digits.end());

Floating point numbers


Floating-point numbers should always be with a decimal point and numbers on either side of it (even in the case of exponential notation). This approach will improve readability: all floating-point numbers will be in the same format, you will not confuse it with an integer, and the characters E e of exponential notation cannot be taken for hexadecimal digits. Remember that a number in exponential notation is not an integer.

float f = 1.f;
long double ld = -.5L;
double d = 1248e6;

float f = 1.0f;
float f2 = 1;   //  
long double ld = -0.5L;
double d = 1248.0e6;

Function call


Either write the entire function call in one line, or place arguments on a new line. And the indent can be either on the first argument, or 4 spaces. Try to minimize the number of lines, place a few arguments on each line.

Function call format:

bool result = DoSomething(argument1, argument2, argument3);

If the arguments do not fit on one line, then we divide them into several lines and each subsequent line is aligned with the first argument. Do not add spaces between parentheses and arguments:

bool result = DoSomething(averyveryveryverylongargument1,
                          argument2, argument3);


It is allowed to place arguments on several lines with an indent of 4 spaces:
if (...) {
  ...
  ...
  if (...) {
    bool result = DoSomething(
        argument1, argument2,  //  4 
        argument3, argument4);
    ...
  }

Try to place several arguments per line, reducing the number of lines per function call (if this does not impair readability). Some people think that formatting strictly on one argument per line is more readable and makes editing arguments easier. However, we focus primarily on code readers (not editing), so we offer a number of approaches to improve readability.

If several arguments on the same line degrade readability (due to the complexity or complexity of the expressions), try creating β€œtalking” variables for the arguments:

int my_heuristic = scores[x] * y + bases[x];
bool result = DoSomething(my_heuristic, x, y, z);

Or place the complex argument on a separate line and add an explanatory comment:

bool result = DoSomething(scores[x] * y + bases[x],  //  
                          x, y, z);

If the function call still has arguments that it is desirable to place on a separate line - place it. A solution should be based on improving code readability.

Arguments sometimes form a structure. In this case, format the arguments according to the required structure:

//     3x3
my_widget.Transform(x1, x2, x3,
                    y1, y2, y3,
                    z1, z2, z3);


Formatting an Initialization List


Format the initialization list in the same way as a function call.

If the list in brackets follows the name (for example, the name of a type or variable), format {} as if it were a function call with that name. Even if there is no name, consider that it is, only empty.

//      .
return {foo, bar};
functioncall({foo, bar});
std::pair<int, int> p{foo, bar};

//     .
SomeFunction(
    {"assume a zero-length name before {"},
    some_other_function_parameter);
SomeType variable{
    some, other, values,
    {"assume a zero-length name before {"},
    SomeOtherType{
        "Very long string requiring the surrounding breaks.",
        some, other values},
    SomeOtherType{"Slightly shorter string",
                  some, other, values}};
SomeType variable{
    "This is too long to fit all in one line"};
MyType m = {  // Here, you could also break before {.
    superlongvariablename1,
    superlongvariablename2,
    {short, interior, list},
    {interiorwrappinglist,
     interiorwrappinglist2}};

Conditions


Try not to insert spaces on the inside of the brackets. Place if and else on different lines.

There are two approaches to formatting conditions. One allows spaces between brackets and a condition, the other does not.

The preferred option without spaces. Another option is also valid, but be consistent . If you modify existing code, use the format that is already in the code. If you are writing new code, use the format like the files located in the same directory or use the project format. If unsure, do not add spaces.

if (condition) {  //    
  ...  //  2 
} else if (...) {  // 'else'      
  ...
} else {
  ...
}

If using a space format:

if ( condition ) {  //   
  ...  //  2 
} else {  // 'else'      
  ...
}

Note that in any case there must be a space between if and the opening bracket. You also need a space between the closing bracket and the curly bracket (if there is one).

if(condition) {   //  -    'if'
if (condition){   //  -    {
if(condition){    //  

if (condition) {  //   -     'if'   {

Short conditions can be written on one line if this improves readability. Use this option only if the line is short and the condition does not contain an else section .

if (x == kFoo) return new Foo();
if (x == kBar) return new Bar();

Do not use the shortened version if there is an else section :

//  -    ,   'else'
if (x) DoThis();
else DoThat();

Usually braces are not required for a short condition, but they are acceptable. Complicated conditions or code are also better read with curly braces. It is often required that any if be with parentheses.

if (condition)
  DoSomething();  //  2 

if (condition) {
  DoSomething();  //  2 
}

And if one part of the condition uses curly braces, also issue the second with them:

//  -    'if',  'else' - 
if (condition) {
  foo;
} else
  bar;

//  -    'else',  'if' - 
if (condition)
  foo;
else {
  bar;
}


//  -     'if'   'else'
if (condition) {
  foo;
} else {
  bar;
}

Loops and switches


The switch construct can use parentheses for blocks. Describe non-trivial transitions between options. Brackets are optional for single expression loops. An empty loop should use either an empty body in brackets or continue .

The case blocks in the switch can either be with curly braces, or without them (of your choice). If parentheses are used, use the format described below.

It is recommended to switch to the default section in switch . This is not necessary when using an enumeration, and the compiler may issue a warning if not all values ​​are processed. If the default section should not be executed, then configure it as an error. For instance:

switch (var) {
  case 0: {  //  2 
    ...      //  4 
    break;
  }
  case 1: {
    ...
    break;
  }
  default: {
    assert(false);
  }
}

The transition from one label to the next should be marked with the macro ABSL_FALLTHROUGH_INTENDED; (defined in absl / base / macros.h ).
Place ABSL_FALLTHROUGH_INTENDED; at the point where the transition will be. An exception to this rule is consecutive labels without a code, in this case nothing needs to be marked.

switch (x) {
  case 41:  //  
  case 43:
    if (dont_be_picky) {
      //    ( )    
      ABSL_FALLTHROUGH_INTENDED;
    } else {
      CloseButNoCigar();
      break;
    }
  case 42:
    DoSomethingSpecial();
    ABSL_FALLTHROUGH_INTENDED;
  default:
    DoSomethingGeneric();
    break;
}

Brackets are optional for single-operation loops.

for (int i = 0; i < kSomeNumber; ++i)
  printf("I love you\n");

for (int i = 0; i < kSomeNumber; ++i) {
  printf("I take it back\n");
}

An empty loop should be styled either as a pair of brackets, or as continue without brackets. Do not use a single semicolon.

while (condition) {
  //    false
}
for (int i = 0; i < kSomeNumber; ++i) {}  // .      -   
while (condition) continue;  //  - continue     

while (condition);  //  -     do/while

Pointers and links


Around '.' and '->' do not put spaces. The dereferencing or capturing operator must be without spaces.

The following are examples of proper formatting of expressions with pointers and links:

x = *p;
p = &x;
x = r.y;
x = r->y;

Note:

  • '.' and '->' are used without spaces.
  • The * or & operators are not separated by spaces.

When declaring a variable or argument, you can place '*' both on the type and on the name:

// ,   *, &
char *c;
const std::string &str;

// ,   *, &
char* c;
const std::string& str;

Try to use a single style in the code file; when modifying an existing file, use the formatting used.

It is allowed to declare several variables in one expression. However, do not use multiple declarations with pointers or links - this may be misunderstood.

//  - 
int x, y;

int x, *y;  //  -      &  *
char * c;  //  -     *
const std::string & str;  //  -     &

Logical expressions


If the logical expression is very long (exceeds the typical value), use a single approach to breaking the expression into lines.

For example, here when wrapping the AND operator is located at the end of the line:

if (this_one_thing > this_other_thing &&
    a_third_thing == a_fourth_thing &&
    yet_another && last_one) {
  ...
}

Note that the code is split (according to the example) so that && and the AND operator complete the line. This style is more often used with Google code, although the location of the operators at the beginning of the line is also acceptable. Also, you can add additional brackets to improve readability. Note that using operators in the form of punctuation (such as && and ~ ) is preferable to using operators in the form of the words and and compl .

Return values


Do not enclose simple return statements in parentheses.

Use parentheses in return expr; only if you used them in an expression of the form x = expr; .

return result;                  //   -  
//  - .    
return (some_long_condition &&
        another_condition);

return (value);                // . ,      var = (value);
return(result);                // . return -   !

Initializing Variables and Arrays


What to use: = , () or
{} is your choice.

You can choose between the options = ,
() and {} . The following code examples are correct:

int x = 3;
int x(3);
int x{3};
std::string name = "Some Name";
std::string name("Some Name");
std::string name{"Some Name"};

Be careful when using the initialization list {...} for a type that has a constructor with std :: initializer_list .

The compiler will prefer to use the std :: initializer_list constructor when there is a list in braces . Note that empty curly braces {} are a special case and the default constructor will be called (if available). To explicitly use a constructor without std :: initializer_list, use parentheses instead of curly braces.

std::vector<int> v(100, 1);  //    
std::vector<int> v{100, 1};  //   2- : 100  1


Also, construction with curly brackets forbids a series of transformations of integer types (transformations with decreasing accuracy). And you can get compilation errors.

int pi(3.14);  // : pi == 3
int pi{3.14};  //  : "" 

Preprocessor directives


Sign # (sign of the preprocessor directive) should be at the beginning of the line.

Even if the preprocessor directive refers to the embedded code, directives are written from the beginning of the line.

//  -    
  if (lopsided_score) {
#if DISASTER_PENDING      //  -    
    DropEverything();
# if NOTIFY               //   # - ,   
    NotifyClient();
# endif
#endif
    BackToNormal();
  }

//  -   
  if (lopsided_score) {
    #if DISASTER_PENDING  // ! "#if"     
    DropEverything();
    #endif                // !     "#endif"
    BackToNormal();
  }

Class formatting


Arrange the sections in the following order: public , protected and private . Indentation is one space.

The basic format for the class is described below (with the exception of comments, see the Commenting on the class description):

class MyClass : public OtherClass {
 public:      //  1 
  MyClass();  //  2-  
  explicit MyClass(int var);
  ~MyClass() {}

  void SomeFunction();
  void SomeFunctionThatDoesNothing() {
  }

  void set_some_var(int var) { some_var_ = var; }
  int some_var() const { return some_var_; }

 private:
  bool SomeInternalFunction();

  int some_var_;
  int some_other_var_;
};

Remarks:

  • The name of the base class is written on the same line as the name of the inherited class (of course, taking into account the limit of 80 characters).
  • Key words the public: , protected,: , and private: should be indented one space.
  • Each of these keywords must be preceded by a blank line (with the exception of the first mention). Also, in small classes, blank lines can be omitted.
  • Do not add a blank line after these keywords.
  • The public section should be the first, behind it protected and at the end the private section .
  • See Declaration Procedure for building declarations in each of these sections.

Constructor Initialization Lists


Constructor initialization lists can be on one line or on several lines with 4 space indentation.

The following are the correct formats for initialization lists:

//    
MyClass::MyClass(int var) : some_var_(var) {
  DoSomething();
}

//          ,
//           
MyClass::MyClass(int var)
    : some_var_(var), some_other_var_(var + 1) {
  DoSomething();
}

//     ,     
//     
MyClass::MyClass(int var)
    : some_var_(var),             //  4 
      some_other_var_(var + 1) {  //   
  DoSomething();
}

//     ,       
MyClass::MyClass(int var)
    : some_var_(var) {}

Formatting Namespaces


Namespace content is indented.

The namespace does not add padding. For instance:

namespace {

void foo() {  // .   
  ...
}

}  // namespace

Do not indent in namespace:

namespace {

  // .   ,   
  void foo() {
    ...
  }

}  // namespace

When declaring nested namespaces, place each declaration on a separate line.

namespace foo {
namespace bar {

Horizontal breakdown


Use horizontal breakdowns as appropriate. Never add spaces to the end of a line.

General principles


void f(bool b) {  //       
  ...
int i = 0;  //       
//            .
//    ,      
int x[] = { 0 };
int x[] = {0};

//        
class Foo : public Bar {
 public:
  //  inline-  
  //     (  )
  Foo(int b) : Bar(), baz_(b) {}  //    
  void Reset() { baz_ = 0; }  //      
  ...

Adding delimiter spaces may interfere with code merging. Therefore: Do not add separation spaces to existing code. You can remove spaces if you have already modified this line. Or do it as a separate operation (it is preferable that no one works with this code).

Cycles and conditions


if (b) {          //        
} else {          //   else
}
while (test) {}   //       
switch (i) {
for (int i = 0; i < 5; ++i) {
//         .   .
//   ,  
switch ( i ) {
if ( test ) {
for ( int i = 0; i < 5; ++i ) {
//         
//          ,   
for ( ; i < 5 ; ++i) {
  ...

//           
for (auto x : counts) {
  ...
}
switch (i) {
  case 1:         //    case  
    ...
  case 2: break;  //    ,   (   )  

Operators


//     
x = 0;

//      ,
//   /   .
//          
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);

//       
x = -5;
++x;
if (x && !y)
  ...

Patterns and casts


//       (<  >),
//  <,  >(  
std::vector<std::string> x;
y = static_cast<char*>(x);

//        .       
std::vector<char *> x;

Vertical breakdown


Minimize vertical splitting.

This is more a principle than a rule: do not add blank lines without special need. In particular, put no more than 1-2 empty lines between functions, do not start the function with an empty line, do not end the function with an empty line, and try to use empty lines less. An empty line in a code block should work like a paragraph in a novel: visually separate two ideas.

The basic principle: the more code fits on one screen, the easier it is to understand and track the sequence of execution. Use the empty string solely to visually separate this sequence.

A few helpful notes about blank lines:

  • An empty line at the beginning or at the end of a function will not improve readability.
  • Empty lines in the if-else blockchain can improve readability.
  • An empty line in front of the comment line usually helps the readability of the code - a new comment usually involves the completion of an old thought and the beginning of a new idea. And the empty line is clearly hinting at it.

All Articles