style.md 41.4 KB
Newer Older
1
# Как писать код на C++ {#kak-pisat-kod-na-c}
B
BayoNet 已提交
2

3
## Общее {#obshchee}
B
BayoNet 已提交
4

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

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

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

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

13
## Форматирование {#formatirovanie}
B
BayoNet 已提交
14

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

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

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

21
``` cpp
22 23 24 25 26 27 28
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
``` cpp
33 34 35
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
``` cpp
40 41
void reinsert(const Value & x)
```
B
BayoNet 已提交
42

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

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

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

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

55
``` cpp
56 57 58 59
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
``` cpp
64 65 66 67 68
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
``` cpp
73 74 75 76
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
``` cpp
91 92 93 94
template <typename TKey, typename TValue>
struct AggregatedStatElement
{}
```
95

96 97
**15.** В классах и структурах, `public`, `private`, `protected` пишется на том же уровне, что и `class/struct`, а остальной код с отступом.

98
``` cpp
99 100 101 102 103 104 105 106 107 108 109 110
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` не нужен.

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

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

115
``` cpp
116 117 118 119 120 121 122 123 124 125 126
/// Finish write.
for (auto & stream : streams)
    stream.second->finalize();
```

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

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

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

127
``` cpp
128 129 130 131 132 133
<< ", " << (timer.elapsed() / chunks_stats.hits) << " μsec/hit.";
```

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

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

135 136 137 138
**23.** Функции, классы, и т. п. отделяются друг от друга одной или двумя пустыми строками.

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

139
``` cpp
140 141 142 143 144 145 146 147 148
//correct
const char * pos
const std::string & s
//incorrect
char const * pos
```

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

149
``` cpp
150 151 152 153 154 155 156 157 158 159 160 161 162
//correct
const char * pos
//incorrect
const char* pos
const char *pos
```

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

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

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

163
``` cpp
164 165 166 167 168 169 170 171 172
//correct
using FileStreams = std::map<std::string, std::shared_ptr<Stream>>;
FileStreams streams;
//incorrect
std::map<std::string, std::shared_ptr<Stream>> streams;
```

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

173
``` cpp
174 175 176 177 178 179
//incorrect
int x, *y;
```

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

180
``` cpp
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
//incorrect
std::cerr << (int)c <<; std::endl;
//correct
std::cerr << static_cast<int>(c) << std::endl;
```

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

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

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

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

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

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

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

203
## Комментарии {#kommentarii}
B
BayoNet 已提交
204

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

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

209
``` cpp
210 211 212 213 214 215
/** 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
``` cpp
222 223 224 225 226 227 228 229 230 231
/** 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
``` cpp
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
/*
* 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
Пример взят с ресурса http://home.tamk.fi/~jaalto/course/coding-style/doc/unmaintainable-code/.
261

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

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

S
Sergei Bocharov 已提交
266
Замечание: такие комментарии могут использоваться для генерации документации с помощью Doxygen. Но, фактически, Doxygen не используется, так как для навигации по коду гораздо удобнее использовать возможности IDE.
267 268 269

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

270
**10.** Для закомментированных кусков кода, используются обычные, не «документирующие» комментарии.
271 272 273 274 275 276 277

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

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

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

278
``` cpp
279 280 281 282 283
/// WHAT THE FAIL???
```

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

284
``` cpp
285 286 287 288 289
///******************************************************
```

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

290
``` cpp
291 292 293 294 295
/// Why did you do this stuff?
```

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

296
``` cpp
297 298
/// for
```
B
BayoNet 已提交
299

300
## Имена {#imena}
B
BayoNet 已提交
301

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

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

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

310
``` cpp
311 312 313
std::string getName() const override { return "Memory"; }
```

314
**3.** Имена классов (структур) - CamelCase с большой буквы. Префиксы кроме I для интерфейсов - не используются.
B
BayoNet 已提交
315

316
``` cpp
317 318
class StorageMemory : public IStorage
```
B
BayoNet 已提交
319

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

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

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

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

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

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

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

340
``` cpp
341 342
class IBlockInputStream
```
343

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

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

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

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

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

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

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

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

364 365
**11.** Если имя содержит сокращение, то:

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

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

371
``` cpp
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
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 已提交
387

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

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

394
``` cpp
395 396 397 398 399 400 401
enum class CompressionMethod
{
    QuickLZ = 0,
    LZ4     = 1,
};
```

S
Sergei Bocharov 已提交
402
**15.** Все имена - по-английски. Транслит с русского использовать нельзя.
403

404
``` text
405 406 407 408 409
не Stroka
```

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

410
``` text
411 412 413 414 415 416 417 418 419 420
`AST`, `SQL`.

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

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

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

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

422
## Как писать код {#kak-pisat-kod}
B
BayoNet 已提交
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
``` cpp
453 454 455 456 457 458 459 460
/// Если вычислений ещё не было - вычислим первый блок синхронно
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
``` cpp
469 470 471
//Not correct
catch (...) {}
```
B
BayoNet 已提交
472

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

475
``` cpp
476 477 478 479 480 481 482 483
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
``` cpp
488 489 490
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
``` cpp
514
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
``` cpp
586
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
``` cpp
618
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

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

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

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

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

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

S
Sergei Bocharov 已提交
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

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

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

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

В логе используется кодировка 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` для функций, если в этом нет необходимости.

672
``` cpp
673 674 675 676 677
[auto f() -&gt; void;]{.strike}
```

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

678
``` cpp
679 680 681 682 683 684 685 686 687
//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
## Неиспользуемые возможности языка C++ {#neispolzuemye-vozmozhnosti-iazyka-c}
690

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

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

695
## Платформа {#platforma}
696

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

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

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

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

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

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

709
**5.** Код пишется под процессор с архитектурой x86\_64.
710

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

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

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

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

719
## Инструментарий {#instrumentarii}
720

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

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

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

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

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

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

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

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

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

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

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

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

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

747
## Библиотеки {#biblioteki}
748

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

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

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

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

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

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

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

763
## Общее {#obshchee-1}
764

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

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

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

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

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

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

777
## Дополнительно {#dopolnitelno}
778

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

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

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

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

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

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

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

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

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

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

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

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

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

805
``` cpp
806 807 808 809
function(
  T1 x1,
  T2 x2)
```
810

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

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

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

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

I
Ivan Blinkov 已提交
838
[Оригинальная статья](https://clickhouse.tech/docs/ru/development/style/) <!--hide-->