ClickHouse/docs/ru/development/style.md
2018-05-26 02:55:18 +03:00

839 lines
41 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# Как писать код на C++
## Общее
**1.** Этот текст носит рекомендательный характер.
**2.** Если вы редактируете код, то имеет смысл писать так, как уже написано.
**3.** Стиль нужен для единообразия. Единообразие нужно, чтобы было проще (удобнее) читать код. А также, чтобы было легче осуществлять поиск по коду.
**4.** Многие правила продиктованы не какими либо разумными соображениями, а сложившейся практикой.
## Форматирование
**1.** Большую часть форматирования сделает автоматически `clang-format`.
**2.** Отступы — 4 пробела. Настройте среду разработки так, чтобы таб добавлял четыре пробела.
**3.** Открывающая и закрывающие фигурные скобки на отдельной строке.
```cpp
inline void readBoolText(bool & x, ReadBuffer & buf)
{
char tmp = '0';
readChar(tmp, buf);
x = tmp != '0';
}
```
**4.** Если всё тело функции — один `statement`, то его можно разместить на одной строке. При этом, вокруг фигурных скобок ставятся пробелы (кроме пробела на конце строки).
```cpp
inline size_t mask() const { return buf_size() - 1; }
inline size_t place(HashValue x) const { return x & mask(); }
```
**5.** Для функций. Пробелы вокруг скобок не ставятся.
```cpp
void reinsert(const Value & x)
```
```cpp
memcpy(&buf[place_value], &x, sizeof(x));
```
**6.** В выражениях `if`, `for`, `while` и т.д. перед открывающей скобкой ставится пробел (в отличие от вызовов функций).
```cpp
for (size_t i = 0; i < rows; i += storage.index_granularity)
```
**7.** Вокруг бинарных операторов (`+`, `-`, `*`, `/`, `%`, ...), а также тернарного оператора `?:` ставятся пробелы.
```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');
```
**8.** Если ставится перенос строки, то оператор пишется на новой строке, и перед ним увеличивается отступ.
```cpp
if (elapsed_ns)
message << " ("
<< rows_read_on_server * 1000000000 / elapsed_ns << " rows/s., "
<< bytes_read_on_server * 1000.0 / elapsed_ns << " MB/s.) ";
```
**9.** Внутри строки можно, выполнять выравнивание с помощью пробелов.
```cpp
dst.ClickLogID = click.LogID;
dst.ClickEventID = click.EventID;
dst.ClickGoodEvent = click.GoodEvent;
```
**10.** Вокруг операторов `.`, `->` не ставятся пробелы.
При необходимости, оператор может быть перенесён на новую строку. В этом случае, перед ним увеличивается отступ.
**11.** Унарные операторы `--`, `++`, `*`, `&`, ... не отделяются от аргумента пробелом.
**12.** После запятой ставится пробел, а перед — нет. Аналогично для точки с запятой внутри выражения `for`.
**13.** Оператор `[]` не отделяется пробелами.
**14.** В выражении `template <...>`, между `template` и `<` ставится пробел, а после `<` и до `>` не ставится.
```cpp
template <typename TKey, typename TValue>
struct AggregatedStatElement
{}
```
**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.** Внутри функций группируйте блоки кода, отделяя их не более, чем одной пустой строкой.
**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)
```
## Комментарии
**1.** Необходимо обязательно писать комментарии во всех нетривиальных местах.
Это очень важно. При написании комментария, можно успеть понять, что код не нужен вообще, или что всё сделано неверно.
```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).
*/
```
**2.** Комментарии могут быть сколь угодно подробными.
**3.** Комментарии пишутся до соответствующего кода. В редких случаях после, на той же строке.
```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
)
```
**4.** Комментарии следует писать только на английском языке.
**5.** При написании библиотеки, разместите подробный комментарий о том, что это такое, в самом главном заголовочном файле.
**6.** Нельзя писать комментарии, которые не дают дополнительной информации. В частности, нельзя писать пустые комментарии вроде этого:
```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:
*/
```
Пример взят с ресурса [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
```
## Имена
**1.** В именах переменных и членов класса используйте маленькие буквами с подчёркиванием.
```cpp
size_t max_block_size;
```
**2.** Имена функций (методов) camelCase с маленькой буквы.
```cpp
std::string getName() const override { return "Memory"; }
```
**3.** Имена классов (структур) - CamelCase с большой буквы. Префиксы кроме I для интерфейсов - не используются.
```cpp
class StorageMemory : public IStorage
```
**4.** `using` называются также, как классы, либо с `_t` на конце.
**5.** Имена типов — параметров шаблонов: в простых случаях - `T`; `T`, `U`; `T1`, `T2`.
В более сложных случаях - либо также, как имена классов, либо можно добавить в начало букву `T`.
```cpp
template <typename TKey, typename TValue>
struct AggregatedStatElement
```
**6.** Имена констант — параметров шаблонов: либо также, как имена переменных, либо `N` в простом случае.
```cpp
template <bool without_www>
struct ExtractDomain
```
**7.** Для абстрактных классов (интерфейсов) можно добавить в начало имени букву `I`.
```cpp
class IBlockInputStream
```
**8.** Если переменная используется достаточно локально, то можно использовать короткое имя.
В остальных случаях используйте имя, описывающее смысл.
```cpp
bool info_successfully_loaded = false;
```
**9.** В именах `define` и глобальных констант используется ALL_CAPS с подчёркиванием.
```cpp
#define MAX_SRC_TABLE_NAMES_TO_STORE 1000
```
**10.** Имена файлов с кодом называйте по стилю соответственно тому, что в них находится.
Если в файле находится один класс, назовите файл, как класс (CamelCase).
Если в файле находится одна функция, назовите файл, как функцию (camelCase).
**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.** Именование локальных переменных и членов класса никак не отличается (никакие префиксы не нужны).
```cpp
timer (not m_timer)
```
**14.** Константы в `enum` — CamelCase с большой буквы. Также допустим ALL_CAPS. Если `enum` не локален, то используйте `enum class`.
```cpp
enum class CompressionMethod
{
QuickLZ = 0,
LZ4 = 1,
};
```
**15.** Все имена - по английски. Транслит с русского использовать нельзя.
```
не Stroka
```
**16.** Сокращения (из нескольких букв разных слов) в именах можно использовать только если они являются общепринятыми (если для сокращения можно найти расшифровку в английской википедии или сделав поисковый запрос).
```
`AST`, `SQL`.
Не `NVDH` (что-то неведомое)
```
Сокращения в виде обрезанного слова можно использовать, только если такое сокращение является широко используемым.
Впрочем, сокращения также можно использовать, если расшифровка находится рядом в комментарии.
**17.** Имена файлов с исходниками на C++ должны иметь расширение только `.cpp`. Заголовочные файлы - только `.h`.
## Как писать код
**1.** Управление памятью.
Ручное освобождение памяти (`delete`) можно использовать только в библиотечном коде.
В свою очередь, в библиотечном коде, оператор `delete` можно использовать только в деструкторах.
В прикладном коде следует делать так, что память освобождается каким-либо объектом, который владеет ей.
Примеры:
- проще всего разместить объект на стеке, или сделать его членом другого класса.
- для большого количества маленьких объектов используйте контейнеры.
- для автоматического освобождения маленького количества объектов, выделенных на куче, используйте `shared_ptr/unique_ptr`.
**2.** Управление ресурсами.
Используйте `RAII` и см. пункт выше.
**3.** Обработка ошибок.
Используйте исключения. В большинстве случаев, нужно только кидать исключения, а ловить - не нужно (потому что `RAII`).
В программах офлайн обработки данных, зачастую, можно не ловить исключения.
В серверах, обрабатывающих пользовательские запросы, как правило, достаточно ловить исключения на самом верху обработчика соединения.
В функциях потока, следует ловить и запоминать все исключения, чтобы выкинуть их в основном потоке после `join`.
```cpp
/// Если вычислений ещё не было - вычислим первый блок синхронно
if (!started)
{
calculate();
started = true;
}
else /// Если вычисления уже идут - подождём результата
pool.wait();
if (exception)
exception->rethrow();
```
Ни в коем случае не «проглатывайте» исключения без разбора. Ни в коем случае, не превращайте все исключения без разбора в сообщения в логе.
```cpp
//Not correct
catch (...) {}
```
Если вам нужно проигнорировать какие-то исключения, то игнорируйте только конкретные, а остальные кидайте обратно.
```cpp
catch (const DB::Exception & e)
{
if (e.code() == ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION)
return nullptr;
else
throw;
}
```
При использовании функций, использующих коды возврата или `errno`, проверяйте результат и кидайте исключение.
```cpp
if (0 != close(fd))
throwFromErrno("Cannot close file " + file_name, ErrorCodes::CANNOT_CLOSE_FILE);
```
`assert` не используются.
**4.** Типы исключений.
В прикладном коде не требуется использовать сложную иерархию исключений. Желательно, чтобы текст исключения был понятен системному администратору.
**5.** Исключения, вылетающие из деструкторов.
Использовать не рекомендуется, но допустимо.
Используйте следующие варианты:
- Сделайте функцию (`done()` или `finalize()`), которая позволяет заранее выполнить всю работу, в процессе которой может возникнуть исключение. Если эта функция была вызвана, то затем в деструкторе не должно возникать исключений.
- Слишком сложную работу (например, отправку данных по сети) можно вообще не делать в деструкторе, рассчитывая, что пользователь заранее позовёт метод для завершения работы.
- Если в деструкторе возникло исключение, желательно не "проглатывать" его, а вывести информацию в лог (если в этом месте доступен логгер).
- В простых программах, если соответствующие исключения не ловятся, и приводят к завершению работы с записью информации в лог, можно не беспокоиться об исключениях, вылетающих из деструкторов, так как вызов `std::terminate` (в случае `noexcept` по умолчанию в C++11), является приемлимым способом обработки исключения.
**6.** Отдельные блоки кода.
Внутри одной функции, можно создать отдельный блок кода, для того, чтобы сделать некоторые переменные локальными в нём, и для того, чтобы соответствующие деструкторы были вызваны при выходе из блока.
```cpp
Block block = data.in->read();
{
std::lock_guard<std::mutex> lock(mutex);
data.ready = true;
data.block = block;
}
ready_any.set();
```
**7.** Многопоточность.
В программах офлайн обработки данных:
-ачала добейтесь более-менее максимальной производительности на одном процессорном ядре, потом можно распараллеливать код, но только если есть необходимость.
В программах - серверах:
- используйте пул потоков для обработки запросов. На данный момент, у нас не было задач, в которых была бы необходимость использовать userspace context switching.
Fork для распараллеливания не используется.
**8.** Синхронизация потоков.
Часто можно сделать так, чтобы отдельные потоки писали данные в разные ячейки памяти (лучше в разные кэш-линии), и не использовать синхронизацию потоков (кроме `joinAll`).
Если синхронизация нужна, то в большинстве случаев, достаточно использовать mutex под `lock_guard`.
В остальных случаях, используйте системные примитивы синхронизации. Не используйте busy wait.
Атомарные операции можно использовать только в простейших случаях.
Не нужно писать самостоятельно lock-free структуры данных, если вы не являетесь экспертом.
**9.** Ссылки и указатели.
В большинстве случаев, предпочитайте ссылки.
**10.** const.
Используйте константные ссылки, указатели на константу, `const_iterator`, константные методы.
Считайте, что `const` — вариант написания «по умолчанию», а отсутствие `const` только при необходимости.
Для переменных, передающихся по значению, использовать `const` обычно не имеет смысла.
**11.** unsigned.
Используйте `unsigned`, если нужно.
**12.** Числовые типы.
Используйте типы `UInt8`, `UInt16`, `UInt32`, `UInt64`, `Int8`, `Int16`, `Int32`, `Int64`, а также `size_t`, `ssize_t`, `ptrdiff_t`.
Не используйте для чисел типы `signed/unsigned long`, `long long`, `short`, `signed/unsigned char`, `char`.
**13.** Передача аргументов.
Сложные значения передавайте по ссылке (включая `std::string`).
Если функция захватывает владение объектом, созданным на куче, то сделайте типом аргумента `shared_ptr` или `unique_ptr`.
**14.** Возврат значений.
В большинстве случаев, просто возвращайте значение с помощью `return`. Не пишите `[return std::move(res)]{.strike}`.
Если внутри функции создаётся объект на куче и отдаётся наружу, то возвращайте `shared_ptr` или `unique_ptr`.
В некоторых редких случаях, может потребоваться возвращать значение через аргумент функции. В этом случае, аргументом будет ссылка.
```cpp
using AggregateFunctionPtr = std::shared_ptr<IAggregateFunction>;
/** Позволяет создать агрегатную функцию по её имени.
*/
class AggregateFunctionFactory
{
public:
AggregateFunctionFactory();
AggregateFunctionPtr get(const String & name, const DataTypes & argument_types) const;
```
**15.** namespace.
Для прикладного кода отдельный `namespace` использовать не нужно.
Для маленьких библиотек - не требуется.
Для не совсем маленьких библиотек - поместите всё в `namespace`.
Внутри библиотеки в `.h` файле можно использовать `namespace detail` для деталей реализации, не нужных прикладному коду.
В `.cpp` файле можно использовать `static` или анонимный namespace для скрытия символов.
Также, `namespace` можно использовать для `enum`, чтобы соответствующие имена не попали во внешний `namespace` (но лучше использовать `enum class`).
**16.** Отложенная инициализация.
Обычно, если для инициализации требуются аргументы, то не пишите конструктор по умолчанию.
Если потом вам потребовалась отложенная инициализация, то вы можете дописать конструктор по умолчанию (который создаст объект с некорректным состоянием). Или, для небольшого количества объектов, можно использовать `shared_ptr/unique_ptr`.
```cpp
Loader(DB::Connection * connection_, const std::string & query, size_t max_block_size_);
/// Для отложенной инициализации
Loader() {}
```
**17.** Виртуальные функции.
Если класс не предназначен для полиморфного использования, то не нужно делать функции виртуальными зря. Это относится и к деструктору.
**18.** Кодировки.
Везде используется UTF-8. Используется `std::string`, `char *`. Не используется `std::wstring`, `wchar_t`.
**19.** Логгирование.
См. примеры везде в коде.
Перед коммитом, удалите всё бессмысленное и отладочное логгирование, и другие виды отладочного вывода.
Не должно быть логгирования на каждую итерацию внутреннего цикла, даже уровня Trace.
При любом уровне логгирования, логи должно быть возможно читать.
Логгирование следует использовать, в основном, только в прикладном коде.
Сообщения в логе должны быть написаны на английском языке.
Желательно, чтобы лог был понятен системному администратору.
Не нужно писать ругательства в лог.
В логе используется кодировка 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`.
## Неиспользуемые возможности языка C++
**1.** Виртуальное наследование не используется.
**2.** Спецификаторы исключений из C++03 не используются.
## Платформа
**1.** Мы пишем код под конкретную платформу.
Хотя, при прочих равных условиях, предпочитается более-менее кроссплатформенный или легко портируемый код.
**2.** Язык - C++17.
**3.** Компилятор - `gcc`. На данный момент (декабрь 2017), код собирается версией 7.2. (Также код может быть собран `clang 5`)
Используется стандартная библиотека (реализация `libstdc++` или `libc++`).
**4.** ОС - Linux Ubuntu, не более старая, чем Precise.
**5.** Код пишется под процессор с архитектурой x86_64.
Набор инструкций минимальный из поддержаных нашими серверами. Сейчас это - SSE4.2.
**6.** Используются флаги компиляции `-Wall -Wextra -Werror`.
**7.** Используется статическая линковка со всеми библиотеками кроме тех, которые трудно подключить статически (см. вывод команды `ldd`).
**8.** Код разрабатывается и отлаживается с релизными параметрами сборки.
## Инструментарий
**1.** Хорошая среда разработки - KDevelop.
**2.** Для отладки используется `gdb`, `valgrind` (`memcheck`), `strace`, `-fsanitize=...`, `tcmalloc_minimal_debug`.
**3.** Для профилирования используется `Linux Perf`, `valgrind` (`callgrind`), `strace -cf`.
**4.** Исходники в Git.
**5.** Сборка с помощью `CMake`.
**6.** Программы выкладываются с помощью `deb` пакетов.
**7.** Коммиты в master не должны ломать сборку проекта.
А работоспособность собранных программ гарантируется только для отдельных ревизий.
**8.** Коммитьте как можно чаще, в том числе и нерабочий код.
Для этого следует использовать бранчи.
Если ваш код в ветке `master` ещё не собирается, исключите его из сборки перед `push`, также вы будете должны его доработать или удалить в течение нескольких дней.
**9.** Для нетривиальных изменений, используются бранчи. Следует загружать бранчи на сервер.
**10.** Ненужный код удаляется из исходников.
## Библиотеки
**1.** Используются стандартная библиотека C++14 (допустимо использовать экспериментальные расширения) а также фреймворки `boost`, `Poco`.
**2.** При необходимости, можно использовать любые известные библиотеки, доступные в ОС из пакетов.
Если есть хорошее готовое решение, то оно используется, даже если для этого придётся установить ещё одну библиотеку.
(Но будьте готовы к тому, что иногда вам придётся выкидывать плохие библиотеки из кода.)
**3.** Если в пакетах нет нужной библиотеки, или её версия достаточно старая, или если она собрана не так, как нужно, то можно использовать библиотеку, устанавливаемую не из пакетов.
**4.** Если библиотека достаточно маленькая и у неё нет своей системы сборки, то следует включить её файлы в проект, в директорию `contrib`.
**5.** Предпочтение всегда отдаётся уже использующимся библиотекам.
## Общее
**1.** Пишите как можно меньше кода.
**2.** Пробуйте самое простое решение.
**3.** Не нужно писать код, если вы ещё не знаете, что будет делать ваша программа, и как будет работать её внутренний цикл.
**4.** В простейших случаях, используйте `using` вместо классов/структур.
**5.** Если есть возможность - не пишите конструкторы копирования, операторы присваивания, деструктор (кроме виртуального, если класс содержит хотя бы одну виртуальную функцию), move-конструкторы и move-присваивания. То есть, чтобы соответствущие функции, генерируемые компилятором, работали правильно. Можно использовать `default`.
**6.** Приветствуется упрощение и уменьшение объёма кода.
## Дополнительно
**1.** Явное указание `std::` для типов из `stddef.h`.
Рекомендуется не указывать. То есть, рекомендуется писать `size_t` вместо `std::size_t`, это короче.
При желании, можно дописать `std::`, этот вариант допустим.
**2.** Явное указание `std::` для функций из стандартной библиотеки C.
Не рекомендуется. То есть, пишите `memcpy` вместо `std::memcpy`.
Причина - существуют похожие нестандартные функции, например, `memmem`. Мы можем использовать и изредка используем эти функции. Эти функции отсутствуют в `namespace std`.
Если вы везде напишете `std::memcpy` вместо `memcpy`, то будет неудобно смотреться `memmem` без `std::`.
Тем не менее, указывать `std::` тоже допустимо, если так больше нравится.
**3.** Использование функций из C при наличии аналогов в стандартной библиотеке C++.
Допустимо, если это использование эффективнее.
Для примера, для копирования длинных кусков памяти, используйте `memcpy` вместо `std::copy`.
**4.** Перенос длинных аргументов функций.
Допустимо использовать любой стиль переноса, похожий на приведённые ниже:
```cpp
function(
T1 x1,
T2 x2)
```
```cpp
function(
size_t left, size_t right,
const & RangesInDataParts ranges,
size_t limit)
```
```cpp
function(size_t left, size_t right,
const & RangesInDataParts ranges,
size_t limit)
```
```cpp
function(size_t left, size_t right,
const & RangesInDataParts ranges,
size_t limit)
```
```cpp
function(
size_t left,
size_t right,
const & RangesInDataParts ranges,
size_t limit)
```