style.md 41.2 KB
Newer Older
B
BayoNet 已提交
1 2 3 4
# Как писать код на C++

## Общее

5 6 7 8 9 10 11
**1.** Этот текст носит рекомендательный характер.

**2.** Если вы редактируете код, то имеет смысл писать так, как уже написано.

**3.** Стиль нужен для единообразия. Единообразие нужно, чтобы было проще (удобнее) читать код. А также, чтобы было легче осуществлять поиск по коду.

**4.** Многие правила продиктованы не какими либо разумными соображениями, а сложившейся практикой.
B
BayoNet 已提交
12 13 14

## Форматирование

15
**1.** Большую часть форматирования сделает автоматически `clang-format`.
B
BayoNet 已提交
16

17
**2.** Отступы — 4 пробела. Настройте среду разработки так, чтобы таб добавлял четыре пробела.
B
BayoNet 已提交
18

19
**3.** Открывающая и закрывающие фигурные скобки на отдельной строке.
B
BayoNet 已提交
20

21 22 23 24 25 26 27 28
```cpp
inline void readBoolText(bool & x, ReadBuffer & buf)
{
    char tmp = '0';
    readChar(tmp, buf);
    x = tmp != '0';
}
```
B
BayoNet 已提交
29

30
**4.** Если всё тело функции — один `statement`, то его можно разместить на одной строке. При этом, вокруг фигурных скобок ставятся пробелы (кроме пробела на конце строки).
B
BayoNet 已提交
31

32 33 34 35
```cpp
inline size_t mask() const                { return buf_size() - 1; }
inline size_t place(HashValue x) const    { return x & mask(); }
```
B
BayoNet 已提交
36

37
**5.** Для функций. Пробелы вокруг скобок не ставятся.
B
BayoNet 已提交
38

39 40 41
```cpp
void reinsert(const Value & x)
```
B
BayoNet 已提交
42

43 44 45
```cpp
memcpy(&buf[place_value], &x, sizeof(x));
```
B
BayoNet 已提交
46

47
**6.**  В выражениях `if`, `for`, `while` и т.д. перед открывающей скобкой ставится пробел (в отличие от вызовов функций).
48

49 50 51
```cpp
for (size_t i = 0; i < rows; i += storage.index_granularity)
```
52

53
**7.**  Вокруг бинарных операторов (`+`, `-`, `*`, `/`, `%`, ...), а также тернарного оператора `?:` ставятся пробелы.
54

55 56 57 58 59
```cpp
UInt16 year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0');
UInt8 month = (s[5] - '0') * 10 + (s[6] - '0');
UInt8 day = (s[8] - '0') * 10 + (s[9] - '0');
```
60

61
**8.**  Если ставится перенос строки, то оператор пишется на новой строке, и перед ним увеличивается отступ.
62

63 64 65 66 67 68
```cpp
if (elapsed_ns)
    message << " ("
        << rows_read_on_server * 1000000000 / elapsed_ns << " rows/s., "
        << bytes_read_on_server * 1000.0 / elapsed_ns << " MB/s.) ";
```
69

70
**9.**  Внутри строки можно, выполнять выравнивание с помощью пробелов.
71

72 73 74 75 76
```cpp
dst.ClickLogID         = click.LogID;
dst.ClickEventID       = click.EventID;
dst.ClickGoodEvent     = click.GoodEvent;
```
B
BayoNet 已提交
77

78
**10.** Вокруг операторов `.`, `->` не ставятся пробелы.
A
Alexey Milovidov 已提交
79

80
При необходимости, оператор может быть перенесён на новую строку. В этом случае, перед ним увеличивается отступ.
B
BayoNet 已提交
81

82
**11.** Унарные операторы `--`, `++`, `*`, `&`, ... не отделяются от аргумента пробелом.
83

84
**12.** После запятой ставится пробел, а перед — нет. Аналогично для точки с запятой внутри выражения `for`.
85

86
**13.** Оператор `[]` не отделяется пробелами.
87

88
**14.** В выражении `template <...>`, между `template` и `<` ставится пробел, а после `<` и до `>`  не ставится.
A
Alexey Milovidov 已提交
89

90 91 92 93 94
```cpp
template <typename TKey, typename TValue>
struct AggregatedStatElement
{}
```
95

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
**15.** В классах и структурах, `public`, `private`, `protected` пишется на том же уровне, что и `class/struct`, а остальной код с отступом.

```cpp
template <typename T>
class MultiVersion
{
public:
    /// Version of object for usage. shared_ptr manage lifetime of version.
    using Version = std::shared_ptr<const T>;
    ...
}
```

**16.** Если на весь файл один `namespace` и кроме него ничего существенного нет, то отступ внутри `namespace` не нужен.

**17.** Если блок для выражения `if`, `for`, `while`, ... состоит из одного `statement`, то фигурные скобки не обязательны. Вместо этого поместите `statement` на отдельную строку. Это правило справедливо и для вложенных `if`, `for`, `while`, ...

Если внутренний `statement` содержит фигурные скобки или `else`, то внешний блок следует писать в фигурных скобках.

```cpp
/// Finish write.
for (auto & stream : streams)
    stream.second->finalize();
```

**18.** Не должно быть пробелов на концах строк.

**19.** Исходники в кодировке UTF-8.

**20.** В строковых литералах можно использовать не-ASCII.

```cpp
<< ", " << (timer.elapsed() / chunks_stats.hits) << " μsec/hit.";
```

**21.** Не пишите несколько выражений в одной строке.

**22.** Внутри функций группируйте блоки кода, отделяя их не более, чем одной пустой строкой.
B
BayoNet 已提交
134

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
**23.** Функции, классы, и т. п. отделяются друг от друга одной или двумя пустыми строками.

**24.** `const` (относящийся к значению) пишется до имени типа.

```cpp
//correct
const char * pos
const std::string & s
//incorrect
char const * pos
```

**25.** При объявлении указателя или ссылки, символы `*` и `&` отделяются пробелами с обеих сторон.

```cpp
//correct
const char * pos
//incorrect
const char* pos
const char *pos
```

**26.** При использовании шаблонных типов, пишите `using` (кроме, возможно, простейших случаев).

То есть, параметры шаблона указываются только в `using` и затем не повторяются в коде.

`using` может быть объявлен локально, например, внутри функции.

```cpp
//correct
using FileStreams = std::map<std::string, std::shared_ptr<Stream>>;
FileStreams streams;
//incorrect
std::map<std::string, std::shared_ptr<Stream>> streams;
```

**27.** Нельзя объявлять несколько переменных разных типов в одном выражении.

```cpp
//incorrect
int x, *y;
```

**28.** C-style cast не используется.

```cpp
//incorrect
std::cerr << (int)c <<; std::endl;
//correct
std::cerr << static_cast<int>(c) << std::endl;
```

**29.** В классах и структурах, группируйте отдельно методы и отдельно члены, внутри каждой области видимости.

**30.** Для не очень большого класса/структуры, можно не отделять объявления методов от реализации.

Аналогично для маленьких методов в любых классах/структурах.

Для шаблонных классов/структур, лучше не отделять объявления методов от реализации (так как иначе они всё равно должны быть определены в той же единице трансляции).

**31.** Не обязательно умещать код по ширине в 80 символов. Можно в 140.

**32.** Всегда используйте префиксный инкремент/декремент, если постфиксный не нужен.

```cpp
for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it)
```
B
BayoNet 已提交
202 203 204

## Комментарии

205
**1.** Необходимо обязательно писать комментарии во всех нетривиальных местах.
B
BayoNet 已提交
206

207
Это очень важно. При написании комментария, можно успеть понять, что код не нужен вообще, или что всё сделано неверно.
B
BayoNet 已提交
208

209 210 211 212 213 214 215
```cpp
/** Part of piece of memory, that can be used.
  * For example, if internal_buffer is 1MB, and there was only 10 bytes loaded to buffer from file for reading,
  * then working_buffer will have size of only 10 bytes
  * (working_buffer.end() will point to position right after those 10 bytes available for read).
  */
```
B
BayoNet 已提交
216

217
**2.** Комментарии могут быть сколь угодно подробными.
B
BayoNet 已提交
218

219
**3.** Комментарии пишутся до соответствующего кода. В редких случаях после, на той же строке.
B
BayoNet 已提交
220

221 222 223 224 225 226 227 228 229 230 231
```cpp
/** Parses and executes the query.
*/
void executeQuery(
    ReadBuffer & istr, /// Where to read the query from (and data for INSERT, if applicable)
    WriteBuffer & ostr, /// Where to write the result
    Context & context, /// DB, tables, data types, engines, functions, aggregate functions...
    BlockInputStreamPtr & query_plan, /// Here could be written the description on how query was executed
    QueryProcessingStage::Enum stage = QueryProcessingStage::Complete /// Up to which stage process the SELECT query
    )
```
B
BayoNet 已提交
232

233
**4.** Комментарии следует писать только на английском языке.
B
BayoNet 已提交
234

235
**5.** При написании библиотеки, разместите подробный комментарий о том, что это такое, в самом главном заголовочном файле.
B
BayoNet 已提交
236

237
**6.** Нельзя писать комментарии, которые не дают дополнительной информации. В частности, нельзя писать пустые комментарии вроде этого:
B
BayoNet 已提交
238

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
```cpp
/*
* Procedure Name:
* Original procedure name:
* Author:
* Date of creation:
* Dates of modification:
* Modification authors:
* Original file name:
* Purpose:
* Intent:
* Designation:
* Classes used:
* Constants:
* Local variables:
* Parameters:
* Date of creation:
* Purpose:
*/
```
B
BayoNet 已提交
259

260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
Пример взят с ресурса  [http://home.tamk.fi/~jaalto/course/coding-style/doc/unmaintainable-code/](http://home.tamk.fi/~jaalto/course/coding-style/doc/unmaintainable-code/).

**7.** Нельзя писать мусорные комментарии (автор, дата создания...) в начале каждого файла.

**8.** Однострочные комментарии начинаются с трёх слешей: `///` , многострочные с `/**`. Такие комментарии считаются «документирующими».

Замечание: такие комментарии могут использоваться для генерации документации с помощью Doxygen. Но, фактически, Doxygen не используется, так как для навигации по коду гораздо удобне использовать возможности IDE.

**9.** В начале и конце многострочного комментария, не должно быть пустых строк (кроме строки, на которой закрывается многострочный комментарий).

**10.** Для закомментированных кусков кода, используются обычные, не "документирующие" комментарии.

**11.** Удаляйте закомментированные куски кода перед коммитом.

**12.** Не нужно писать нецензурную брань в комментариях или коде.

**13.** Не пишите прописными буквами. Не используйте излишнее количество знаков препинания.

```cpp
/// WHAT THE FAIL???
```

**14.** Не составляйте из комментариев строки-разделители.

```cpp
///******************************************************
```

**15.** Не нужно писать в комментарии диалог (лучше сказать устно).

```cpp
/// Why did you do this stuff?
```

**16.** Не нужно писать комментарий в конце блока о том, что представлял собой этот блок.

```cpp
/// for
```
B
BayoNet 已提交
299 300 301

## Имена

302
**1.** В именах переменных и членов класса используйте маленькие буквами с подчёркиванием.
B
BayoNet 已提交
303

304 305 306 307 308
```cpp
size_t max_block_size;
```

**2.** Имена функций (методов)  camelCase с маленькой буквы.
B
BayoNet 已提交
309 310 311 312

    ```cpp
    std::string getName() const override { return "Memory"; }
    ```
313
**3.** Имена классов (структур) - CamelCase с большой буквы. Префиксы кроме I для интерфейсов - не используются.
B
BayoNet 已提交
314 315 316 317 318

    ```cpp
    class StorageMemory : public IStorage
    ```

319
**4.** `using` называются также, как классы, либо с `_t` на конце.
B
BayoNet 已提交
320

321
**5.** Имена типов — параметров шаблонов: в простых случаях - `T`; `T`, `U`; `T1`, `T2`.
B
BayoNet 已提交
322

323
В более сложных случаях - либо также, как имена классов, либо можно добавить в начало букву `T`.
B
BayoNet 已提交
324

325 326 327 328
```cpp
template <typename TKey, typename TValue>
struct AggregatedStatElement
```
B
BayoNet 已提交
329

330
**6.** Имена констант — параметров шаблонов: либо также, как имена переменных, либо `N` в простом случае.
B
BayoNet 已提交
331

332 333 334 335
```cpp
template <bool without_www>
struct ExtractDomain
```
B
BayoNet 已提交
336

337
**7.** Для абстрактных классов (интерфейсов) можно добавить в начало имени букву `I`.
B
BayoNet 已提交
338

339 340 341
```cpp
class IBlockInputStream
```
342

343
**8.** Если переменная используется достаточно локально, то можно использовать короткое имя.
344

345
В остальных случаях используйте имя, описывающее смысл.
346

347 348 349
```cpp
bool info_successfully_loaded = false;
```
350

351
**9.**  В именах `define` и глобальных констант используется ALL_CAPS с подчёркиванием.
B
BayoNet 已提交
352

353 354 355
```cpp
#define MAX_SRC_TABLE_NAMES_TO_STORE 1000
```
B
BayoNet 已提交
356

357
**10.** Имена файлов с кодом называйте по стилю соответственно тому, что в них находится.
B
BayoNet 已提交
358

359
Если в файле находится один класс, назовите файл, как класс (CamelCase).
360

361
Если в файле находится одна функция, назовите файл, как функцию (camelCase).
A
Alexey Milovidov 已提交
362

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
**11.** Если имя содержит сокращение, то:

- для имён переменных, всё сокращение пишется маленькими буквами `mysql_connection` (не `mySQL_connection`).
- для имён классов и функций, сохраняются большие буквы в сокращении `MySQLConnection` (не `MySqlConnection`).

**12.** Параметры конструктора, использующиеся сразу же для инициализации соответствующих членов класса, следует назвать также, как и члены класса, добавив подчёркивание в конец.

```cpp
FileQueueProcessor(
    const std::string & path_,
    const std::string & prefix_,
    std::shared_ptr<FileHandler> handler_)
    : path(path_),
    prefix(prefix_),
    handler(handler_),
    log(&Logger::get("FileQueueProcessor"))
{
}
```

Также можно называть параметры конструктора так же, как и члены класса (не добавлять подчёркивание), но только если этот параметр не используется в теле конструктора.

**13.** Именование локальных переменных и членов класса никак не отличается (никакие префиксы не нужны).
B
BayoNet 已提交
386

387 388 389
```cpp
timer (not m_timer)
```
B
BayoNet 已提交
390

391
**14.** Константы в `enum` — CamelCase с большой буквы. Также допустим ALL_CAPS. Если `enum` не локален, то используйте `enum class`.
A
Alexey Milovidov 已提交
392

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
```cpp
enum class CompressionMethod
{
    QuickLZ = 0,
    LZ4     = 1,
};
```

**15.** Все имена - по английски. Транслит с русского использовать нельзя.

```
не Stroka
```

**16.** Сокращения (из нескольких букв разных слов) в именах можно использовать только если они являются общепринятыми (если для сокращения можно найти расшифровку в английской википедии или сделав поисковый запрос).

```
`AST`, `SQL`.

Не `NVDH` (что-то неведомое)
```

Сокращения в виде обрезанного слова можно использовать, только если такое сокращение является широко используемым.

Впрочем, сокращения также можно использовать, если расшифровка находится рядом в комментарии.

**17.** Имена файлов с исходниками на C++ должны иметь расширение только `.cpp`. Заголовочные файлы - только `.h`.
A
Alexey Milovidov 已提交
420

B
BayoNet 已提交
421 422 423

## Как писать код

424
**1.** Управление памятью.
B
BayoNet 已提交
425

426
Ручное освобождение памяти (`delete`) можно использовать только в библиотечном коде.
B
BayoNet 已提交
427

428
В свою очередь, в библиотечном коде, оператор `delete` можно использовать только в деструкторах.
B
BayoNet 已提交
429

430
В прикладном коде следует делать так, что память освобождается каким-либо объектом, который владеет ей.
B
BayoNet 已提交
431

432
Примеры:
A
Alexey Milovidov 已提交
433

434 435 436
-   проще всего разместить объект на стеке, или сделать его членом другого класса.
-   для большого количества маленьких объектов используйте контейнеры.
-   для автоматического освобождения маленького количества объектов, выделенных на куче, используйте `shared_ptr/unique_ptr`.
B
BayoNet 已提交
437

438
**2.** Управление ресурсами.
B
BayoNet 已提交
439

440
Используйте `RAII` и см. пункт выше.
B
BayoNet 已提交
441

442
**3.** Обработка ошибок.
A
Alexey Milovidov 已提交
443

444
Используйте исключения. В большинстве случаев, нужно только кидать исключения, а ловить - не нужно (потому что `RAII`).
A
Alexey Milovidov 已提交
445

446
В программах офлайн обработки данных, зачастую, можно не ловить исключения.
A
Alexey Milovidov 已提交
447

448
В серверах, обрабатывающих пользовательские запросы, как правило, достаточно ловить исключения на самом верху обработчика соединения.
B
BayoNet 已提交
449

450
В функциях потока, следует ловить и запоминать все исключения, чтобы выкинуть их в основном потоке после `join`.
B
BayoNet 已提交
451

452 453 454 455 456 457 458 459 460
```cpp
/// Если вычислений ещё не было - вычислим первый блок синхронно
if (!started)
{
    calculate();
    started = true;
}
else    /// Если вычисления уже идут - подождём результата
    pool.wait();
A
Alexey Milovidov 已提交
461

462 463 464
if (exception)
    exception->rethrow();
```
A
Alexey Milovidov 已提交
465

466
Ни в коем случае не «проглатывайте» исключения без разбора. Ни в коем случае, не превращайте все исключения без разбора в сообщения в логе.
B
BayoNet 已提交
467

468 469 470 471
```cpp
//Not correct
catch (...) {}
```
B
BayoNet 已提交
472

473
Если вам нужно проигнорировать какие-то исключения, то игнорируйте только конкретные, а остальные кидайте обратно.
B
BayoNet 已提交
474

475 476 477 478 479 480 481 482 483
```cpp
catch (const DB::Exception & e)
{
    if (e.code() == ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION)
        return nullptr;
    else
        throw;
}
```
B
BayoNet 已提交
484

485
При использовании функций, использующих коды возврата или `errno`, проверяйте результат и кидайте исключение.
B
BayoNet 已提交
486

487 488 489 490
```cpp
if (0 != close(fd))
    throwFromErrno("Cannot close file " + file_name, ErrorCodes::CANNOT_CLOSE_FILE);
```
B
BayoNet 已提交
491

492
`assert` не используются.
B
BayoNet 已提交
493

494
**4.** Типы исключений.
A
Alexey Milovidov 已提交
495

496
В прикладном коде не требуется использовать сложную иерархию исключений. Желательно, чтобы текст исключения был понятен системному администратору.
A
Alexey Milovidov 已提交
497

498
**5.** Исключения, вылетающие из деструкторов.
B
BayoNet 已提交
499

500
Использовать не рекомендуется, но допустимо.
B
BayoNet 已提交
501

502
Используйте следующие варианты:
B
BayoNet 已提交
503

504 505 506 507
- Сделайте функцию (`done()` или `finalize()`), которая позволяет заранее выполнить всю работу, в процессе которой может возникнуть исключение. Если эта функция была вызвана, то затем в деструкторе не должно возникать исключений.
- Слишком сложную работу (например, отправку данных по сети) можно вообще не делать в деструкторе, рассчитывая, что пользователь заранее позовёт метод для завершения работы.
- Если в деструкторе возникло исключение, желательно не "проглатывать" его, а вывести информацию в лог (если в этом месте доступен логгер).
- В простых программах, если соответствующие исключения не ловятся, и приводят к завершению работы с записью информации в лог, можно не беспокоиться об исключениях, вылетающих из деструкторов, так как вызов `std::terminate` (в случае `noexcept` по умолчанию в C++11), является приемлимым способом обработки исключения.
B
BayoNet 已提交
508

509
**6.** Отдельные блоки кода.
B
BayoNet 已提交
510

511
Внутри одной функции, можно создать отдельный блок кода, для того, чтобы сделать некоторые переменные локальными в нём, и для того, чтобы соответствующие деструкторы были вызваны при выходе из блока.
B
BayoNet 已提交
512

513 514
```cpp
Block block = data.in->read();
A
Alexey Milovidov 已提交
515

516 517 518 519 520
{
    std::lock_guard<std::mutex> lock(mutex);
    data.ready = true;
    data.block = block;
}
A
Alexey Milovidov 已提交
521

522 523
ready_any.set();
```
B
BayoNet 已提交
524

525
**7.** Многопоточность.
A
Alexey Milovidov 已提交
526

527
В программах офлайн обработки данных:
A
Alexey Milovidov 已提交
528

529
- cначала добейтесь более-менее максимальной производительности на одном процессорном ядре, потом можно распараллеливать код, но только если есть необходимость.
A
Alexey Milovidov 已提交
530

531
В программах - серверах:
A
Alexey Milovidov 已提交
532

533
- используйте пул потоков для обработки запросов. На данный момент, у нас не было задач, в которых была бы необходимость использовать userspace context switching.
B
BayoNet 已提交
534

535
Fork для распараллеливания не используется.
B
BayoNet 已提交
536

537
**8.** Синхронизация потоков.
A
Alexey Milovidov 已提交
538

539
Часто можно сделать так, чтобы отдельные потоки писали данные в разные ячейки памяти (лучше в разные кэш-линии), и не использовать синхронизацию потоков (кроме `joinAll`).
A
Alexey Milovidov 已提交
540

541
Если синхронизация нужна, то в большинстве случаев, достаточно использовать mutex под `lock_guard`.
B
BayoNet 已提交
542

543
В остальных случаях, используйте системные примитивы синхронизации. Не используйте busy wait.
B
BayoNet 已提交
544

545
Атомарные операции можно использовать только в простейших случаях.
A
Alexey Milovidov 已提交
546

547
Не нужно писать самостоятельно lock-free структуры данных, если вы не являетесь экспертом.
B
BayoNet 已提交
548

549
**9.** Ссылки и указатели.
A
Alexey Milovidov 已提交
550

551
В большинстве случаев, предпочитайте ссылки.
B
BayoNet 已提交
552

553
**10.** const.
A
Alexey Milovidov 已提交
554

555
Используйте константные ссылки, указатели на константу, `const_iterator`, константные методы.
A
Alexey Milovidov 已提交
556

557
Считайте, что `const` — вариант написания «по умолчанию», а отсутствие `const` только при необходимости.
B
BayoNet 已提交
558

559
Для переменных, передающихся по значению, использовать `const` обычно не имеет смысла.
B
BayoNet 已提交
560

561
**11.** unsigned.
A
Alexey Milovidov 已提交
562

563
Используйте `unsigned`, если нужно.
A
Alexey Milovidov 已提交
564

565
**12.** Числовые типы.
A
Alexey Milovidov 已提交
566

567
Используйте типы `UInt8`, `UInt16`, `UInt32`, `UInt64`, `Int8`, `Int16`, `Int32`, `Int64`, а также `size_t`, `ssize_t`, `ptrdiff_t`.
A
Alexey Milovidov 已提交
568

569
Не используйте для чисел типы `signed/unsigned long`, `long long`, `short`, `signed/unsigned char`, `char`.
A
Alexey Milovidov 已提交
570

571
**13.** Передача аргументов.
B
BayoNet 已提交
572

573
Сложные значения передавайте по ссылке (включая `std::string`).
B
BayoNet 已提交
574

575
Если функция захватывает владение объектом, созданным на куче, то сделайте типом аргумента `shared_ptr` или `unique_ptr`.
A
Alexey Milovidov 已提交
576

577
**14.** Возврат значений.
B
BayoNet 已提交
578

579
В большинстве случаев, просто возвращайте значение с помощью `return`. Не пишите `[return std::move(res)]{.strike}`.
B
BayoNet 已提交
580

581
Если внутри функции создаётся объект на куче и отдаётся наружу, то возвращайте `shared_ptr` или `unique_ptr`.
B
BayoNet 已提交
582

583
В некоторых редких случаях, может потребоваться возвращать значение через аргумент функции. В этом случае, аргументом будет ссылка.
B
BayoNet 已提交
584

585 586
```cpp
using AggregateFunctionPtr = std::shared_ptr<IAggregateFunction>;
B
BayoNet 已提交
587

588 589 590 591 592 593 594 595
/** Позволяет создать агрегатную функцию по её имени.
  */
class AggregateFunctionFactory
{
public:
    AggregateFunctionFactory();
    AggregateFunctionPtr get(const String & name, const DataTypes & argument_types) const;
```
A
Alexey Milovidov 已提交
596

597
**15.** namespace.
A
Alexey Milovidov 已提交
598

599
Для прикладного кода отдельный `namespace` использовать не нужно.
A
Alexey Milovidov 已提交
600

601
Для маленьких библиотек - не требуется.
A
Alexey Milovidov 已提交
602

603
Для не совсем маленьких библиотек - поместите всё в `namespace`.
A
Alexey Milovidov 已提交
604

605
Внутри библиотеки в `.h` файле можно использовать `namespace detail` для деталей реализации, не нужных прикладному коду.
A
Alexey Milovidov 已提交
606

607
В `.cpp` файле можно использовать `static` или анонимный namespace для скрытия символов.
A
Alexey Milovidov 已提交
608

609
Также, `namespace` можно использовать для `enum`, чтобы соответствующие имена не попали во внешний `namespace` (но лучше использовать `enum class`).
A
Alexey Milovidov 已提交
610

611
**16.** Отложенная инициализация.
B
BayoNet 已提交
612

613
Обычно, если для инициализации требуются аргументы, то не пишите конструктор по умолчанию.
A
Alexey Milovidov 已提交
614

615
Если потом вам потребовалась отложенная инициализация, то вы можете дописать конструктор по умолчанию (который создаст объект с некорректным состоянием). Или, для небольшого количества объектов, можно использовать `shared_ptr/unique_ptr`.
B
BayoNet 已提交
616

617 618
```cpp
Loader(DB::Connection * connection_, const std::string & query, size_t max_block_size_);
B
BayoNet 已提交
619

620 621 622
/// Для отложенной инициализации
Loader() {}
```
B
BayoNet 已提交
623

624
**17.** Виртуальные функции.
B
BayoNet 已提交
625

626
Если класс не предназначен для полиморфного использования, то не нужно делать функции виртуальными зря. Это относится и к деструктору.
B
BayoNet 已提交
627

628
**18.** Кодировки.
B
BayoNet 已提交
629

630
Везде используется UTF-8. Используется `std::string`, `char *`. Не используется `std::wstring`, `wchar_t`.
A
Alexey Milovidov 已提交
631

632
**19.** Логгирование.
B
BayoNet 已提交
633

634
См. примеры везде в коде.
B
BayoNet 已提交
635

636
Перед коммитом, удалите всё бессмысленное и отладочное логгирование, и другие виды отладочного вывода.
B
BayoNet 已提交
637

638
Не должно быть логгирования на каждую итерацию внутреннего цикла, даже уровня Trace.
B
BayoNet 已提交
639

640
При любом уровне логгирования, логи должно быть возможно читать.
A
Alexey Milovidov 已提交
641

642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
Логгирование следует использовать, в основном, только в прикладном коде.

Сообщения в логе должны быть написаны на английском языке.

Желательно, чтобы лог был понятен системному администратору.

Не нужно писать ругательства в лог.

В логе используется кодировка UTF-8. Изредка можно использовать в логе не-ASCII символы.

**20.** Ввод-вывод.

Во внутренних циклах (в критичных по производительности участках программы) нельзя использовать `iostreams` (в том числе, ни в коем случае не используйте `stringstream`).

Вместо этого используйте библиотеку `DB/IO`.

**21.** Дата и время.

См. библиотеку `DateLUT`.

**22.** include.

В заголовочном файле используется только `#pragma once`, а include guards писать не нужно.

**23.** using.

`using namespace` не используется. Можно использовать `using` что-то конкретное. Лучше локально, внутри класса или функции.

**24.** Не нужно использовать `trailing return type` для функций, если в этом нет необходимости.

```cpp
[auto f() -&gt; void;]{.strike}
```

**25.** Объявление и инициализация переменных.

```cpp
//right way
std::string s = "Hello";
std::string s{"Hello"};

//wrong way
auto s = std::string{"Hello"};
```

**26.** Для виртуальных функций, пишите `virtual` в базовом классе, а в классах-наследниках, пишите `override` и не пишите `virtual`.
A
Alexey Milovidov 已提交
688

689

B
BayoNet 已提交
690
## Неиспользуемые возможности языка C++
691

692 693 694 695
**1.** Виртуальное наследование не используется.

**2.** Спецификаторы исключений из C++03 не используются.

B
BayoNet 已提交
696
## Платформа
697

698
**1.** Мы пишем код под конкретную платформу.
699

700
Хотя, при прочих равных условиях, предпочитается более-менее кроссплатформенный или легко портируемый код.
701

702
**2.** Язык - C++17.
703

704
**3.** Компилятор - `gcc`. На данный момент (декабрь 2017), код собирается версией 7.2. (Также код может быть собран `clang 5`)
705

706
Используется стандартная библиотека (реализация `libstdc++` или `libc++`).
707

708
**4.** ОС - Linux Ubuntu, не более старая, чем Precise.
709

710
**5.** Код пишется под процессор с архитектурой x86_64.
711

712
Набор инструкций минимальный из поддержаных нашими серверами. Сейчас это - SSE4.2.
713

714
**6.** Используются флаги компиляции `-Wall -Wextra -Werror`.
715

716
**7.** Используется статическая линковка со всеми библиотеками кроме тех, которые трудно подключить статически (см. вывод команды `ldd`).
717

718
**8.** Код разрабатывается и отлаживается с релизными параметрами сборки.
719

B
BayoNet 已提交
720
## Инструментарий
721

722 723 724 725 726 727 728 729 730
**1.** Хорошая среда разработки - KDevelop.

**2.** Для отладки используется `gdb`, `valgrind` (`memcheck`), `strace`, `-fsanitize=...`, `tcmalloc_minimal_debug`.

**3.** Для профилирования используется `Linux Perf`, `valgrind` (`callgrind`), `strace -cf`.

**4.** Исходники в Git.

**5.** Сборка с помощью `CMake`.
731

732
**6.** Программы выкладываются с помощью `deb` пакетов.
733

734 735 736 737 738 739 740 741 742 743 744 745 746
**7.** Коммиты в master не должны ломать сборку проекта.

А работоспособность собранных программ гарантируется только для отдельных ревизий.

**8.** Коммитьте как можно чаще, в том числе и нерабочий код.

Для этого следует использовать бранчи.

Если ваш код в ветке `master` ещё не собирается, исключите его из сборки перед `push`, также вы будете должны его доработать или удалить в течение нескольких дней.

**9.** Для нетривиальных изменений, используются бранчи. Следует загружать бранчи на сервер.

**10.** Ненужный код удаляется из исходников.
A
Alexey Milovidov 已提交
747

748

B
BayoNet 已提交
749
## Библиотеки
750

751
**1.** Используются стандартная библиотека C++14 (допустимо использовать экспериментальные расширения) а также фреймворки `boost`, `Poco`.
752

753
**2.** При необходимости, можно использовать любые известные библиотеки, доступные в ОС из пакетов.
754

755
Если есть хорошее готовое решение, то оно используется, даже если для этого придётся установить ещё одну библиотеку.
756

757 758 759 760 761 762 763
(Но будьте готовы к тому, что иногда вам придётся выкидывать плохие библиотеки из кода.)

**3.** Если в пакетах нет нужной библиотеки, или её версия достаточно старая, или если она собрана не так, как нужно, то можно использовать библиотеку, устанавливаемую не из пакетов.

**4.** Если библиотека достаточно маленькая и у неё нет своей системы сборки, то следует включить её файлы в проект, в директорию `contrib`.

**5.** Предпочтение всегда отдаётся уже использующимся библиотекам.
764

B
BayoNet 已提交
765
## Общее
766

767 768 769 770 771 772 773 774 775 776 777
**1.** Пишите как можно меньше кода.

**2.** Пробуйте самое простое решение.

**3.** Не нужно писать код, если вы ещё не знаете, что будет делать ваша программа, и как будет работать её внутренний цикл.

**4.** В простейших случаях, используйте `using` вместо классов/структур.

**5.** Если есть возможность - не пишите конструкторы копирования, операторы присваивания, деструктор (кроме виртуального, если класс содержит хотя бы одну виртуальную функцию), move-конструкторы и move-присваивания. То есть, чтобы соответствущие функции, генерируемые компилятором, работали правильно. Можно использовать `default`.

**6.** Приветствуется упрощение и уменьшение объёма кода.
778

B
BayoNet 已提交
779
## Дополнительно
780

781
**1.** Явное указание `std::` для типов из `stddef.h`.
782

783
Рекомендуется не указывать. То есть, рекомендуется писать `size_t` вместо `std::size_t`, это короче.
A
Alexey Milovidov 已提交
784

785
При желании, можно дописать `std::`, этот вариант допустим.
786

787
**2.** Явное указание `std::` для функций из стандартной библиотеки C.
788

789
Не рекомендуется. То есть, пишите `memcpy` вместо `std::memcpy`.
A
Alexey Milovidov 已提交
790

791
Причина - существуют похожие нестандартные функции, например, `memmem`. Мы можем использовать и изредка используем эти функции. Эти функции отсутствуют в `namespace std`.
A
Alexey Milovidov 已提交
792

793
Если вы везде напишете `std::memcpy` вместо `memcpy`, то будет неудобно смотреться `memmem` без `std::`.
A
Alexey Milovidov 已提交
794

795
Тем не менее, указывать `std::` тоже допустимо, если так больше нравится.
796

797
**3.** Использование функций из C при наличии аналогов в стандартной библиотеке C++.
798

799
Допустимо, если это использование эффективнее.
800

801
Для примера, для копирования длинных кусков памяти, используйте `memcpy` вместо `std::copy`.
802

803
**4.** Перенос длинных аргументов функций.
804

805
Допустимо использовать любой стиль переноса, похожий на приведённые ниже:
806

807 808 809 810 811
```cpp
function(
  T1 x1,
  T2 x2)
```
812

813 814 815 816 817 818
```cpp
function(
  size_t left, size_t right,
  const & RangesInDataParts ranges,
  size_t limit)
```
819

820 821 822 823 824
```cpp
function(size_t left, size_t right,
  const & RangesInDataParts ranges,
  size_t limit)
```
825

826 827 828 829 830
```cpp
function(size_t left, size_t right,
      const & RangesInDataParts ranges,
      size_t limit)
```
A
Alexey Milovidov 已提交
831

832 833 834 835 836 837 838
```cpp
function(
      size_t left,
      size_t right,
      const & RangesInDataParts ranges,
      size_t limit)
```
I
Ivan Blinkov 已提交
839 840

[Оригинальная статья](https://clickhouse.yandex/docs/ru/development/style/) <!--hide-->