> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-fbfa8bee.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Рекомендации по стилю кода для разработки ClickHouse на C++

# Рекомендации по стилю C++

<div id="general-recommendations">
  ## Общие рекомендации
</div>

Ниже приведены рекомендации, а не обязательные требования.
Если вы редактируете код, разумно придерживаться форматирования, принятого в существующем коде.
Стиль кода нужен для единообразия. Единообразие упрощает чтение кода, а также облегчает поиск по нему.
Многие из этих правил не имеют логического обоснования; они продиктованы сложившейся практикой.

<div id="formatting">
  ## Форматирование
</div>

**1.** Большая часть форматирования выполняется автоматически с помощью `clang-format`.

**2.** Отступ — 4 пробела. Настройте среду разработки так, чтобы нажатие Tab добавляло четыре пробела.

**3.** Открывающие и закрывающие фигурные скобки должны располагаться на отдельной строке.

```cpp theme={null}
inline void readBoolText(bool & x, ReadBuffer & buf)
{
    char tmp = '0';
    readChar(tmp, buf);
    x = tmp != '0';
}
```

**4.** Если всё тело функции представляет собой один `оператор`, его можно записать в одну строку. Ставьте пробелы вокруг фигурных скобок (кроме пробела в конце строки).

```cpp theme={null}
inline size_t mask() const                { return buf_size() - 1; }
inline size_t place(HashValue x) const    { return x & mask(); }
```

**5.** Для функций. Не ставьте пробелы вокруг скобок.

```cpp theme={null}
void reinsert(const Value & x)
```

```cpp theme={null}
memcpy(&buf[place_value], &x, sizeof(x));
```

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

```cpp theme={null}
for (size_t i = 0; i < rows; i += storage.index_granularity)
```

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

```cpp theme={null}
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 theme={null}
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 theme={null}
dst.ClickLogID         = click.LogID;
dst.ClickEventID       = click.EventID;
dst.ClickGoodEvent     = click.GoodEvent;
```

**10.** Не используйте пробелы вокруг операторов `.`, `->`.

При необходимости оператор можно перенести на следующую строку. В этом случае отступ перед ним увеличивают.

**11.** Не используйте пробел для отделения унарных операторов (`--`, `++`, `*`, `&`, ...) от аргумента.

**12.** Ставьте пробел после запятой, но не перед ней. То же правило распространяется на точку с запятой внутри выражения `for`.

**13.** Не используйте пробелы при написании оператора `[]`.

**14.** В выражении `template <...>` ставьте пробел между `template` и `<`; пробелы после `<` и перед `>` не ставятся.

```cpp theme={null}
template <typename TKey, typename TValue>
struct AggregatedStatElement
{}
```

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

```cpp theme={null}
template <typename T>
class MultiVersion
{
public:
    /// Версия объекта для использования. shared_ptr управляет временем жизни версии.
    using Version = std::shared_ptr<const T>;
    ...
}
```

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

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

Но если внутренний `оператор` содержит фигурные скобки или `else`, внешний блок необходимо заключить в фигурные скобки.

```cpp theme={null}
/// Завершить запись.
for (auto & stream : streams)
    stream.second->finalize();
```

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

**19.** Исходные файлы имеют кодировку UTF-8.

**20.** В строковых литералах допускается использование символов, не входящих в набор ASCII.

```cpp theme={null}
<< ", " << (timer.elapsed() / chunks_stats.hits) << " μsec/hit.";
```

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

**22.** Группируйте части кода внутри функций и разделяйте их не более чем одной пустой строкой.

**23.** Разделяйте функции, классы и т. д. одной или двумя пустыми строками.

**24.** `A const` (относящийся к значению) должен записываться перед именем типа.

```cpp theme={null}
//правильно
const char * pos
const std::string & s
//неправильно
char const * pos
```

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

```cpp theme={null}
//правильно
const char * pos
//неправильно
const char* pos
const char *pos
```

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

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

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

```cpp theme={null}
//правильно
using FileStreams = std::map<std::string, std::shared_ptr<Stream>>;
FileStreams streams;
//неправильно
std::map<std::string, std::shared_ptr<Stream>> streams;
```

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

```cpp theme={null}
//неправильно
int x, *y;
```

**28.** Не используйте приведение типов в стиле C.

```cpp theme={null}
//неправильно
std::cerr << (int)c <<; std::endl;
//правильно
std::cerr << static_cast<int>(c) << std::endl;
```

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

**30.** В небольших классах и `struct` не обязательно отделять объявление метода от его реализации.

То же самое верно и для небольших методов в любых классах или `struct`.

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

**31.** Можно переносить строки по достижении 140 символов, а не 80.

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

```cpp theme={null}
for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it)
```

<div id="comments">
  ## Комментарии
</div>

**1.** Обязательно добавляйте комментарии ко всем нетривиальным участкам кода.

Это очень важно. Когда вы пишете комментарий, это может помочь вам понять, что код либо не нужен, либо спроектирован неправильно.

```cpp theme={null}
/** 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 theme={null}
/** Разбирает и выполняет запрос.
*/
void executeQuery(
    ReadBuffer & istr, /// Откуда читать запрос (и данные для INSERT, если применимо)
    WriteBuffer & ostr, /// Куда записывать результат
    Context & context, /// БД, таблицы, типы данных, движки, функции, агрегатные функции...
    BlockInputStreamPtr & query_plan, /// Здесь может быть записано описание того, как выполнялся запрос
    QueryProcessingStage::Enum stage = QueryProcessingStage::Complete /// До какой стадии обрабатывать запрос SELECT
    )
```

**4.** Комментарии следует писать только на английском языке.

**5.** Если вы пишете библиотеку, добавьте в основной файл заголовка подробные комментарии с её описанием.

**6.** Не добавляйте комментарии, не несущие дополнительной информации. В частности, не оставляйте пустые комментарии, подобные этому:

```cpp theme={null}
/*
* 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 theme={null}
/// WHAT THE FAIL???
```

**14.** Не используйте комментарии как разделители.

```cpp theme={null}
///******************************************************
```

**15.** Не разводите дискуссии в комментариях.

```cpp theme={null}
/// Why did you do this stuff?
```

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

```cpp theme={null}
/// for
```

<div id="names">
  ## Имена
</div>

**1.** Используйте в именах переменных и членов класса строчные буквы и нижние подчёркивания.

```cpp theme={null}
size_t max_block_size;
```

**2.** Названия функций (методов) записывайте в camelCase, начиная со строчной буквы.

```cpp theme={null}
std::string getName() const override { return "Memory"; }
```

**3.** Для названий классов (структур) используйте CamelCase с заглавной буквы. Для интерфейсов префиксы, кроме I, не используются.

```cpp theme={null}
class StorageMemory : public IStorage
```

**4.** `using` именуются так же, как и классы.

**5.** Имена аргументов шаблонных типов: в простых случаях используйте `T`; `T`, `U`; `T1`, `T2`.

В более сложных случаях либо следуйте правилам именования классов, либо добавляйте префикс `T`.

```cpp theme={null}
template <typename TKey, typename TValue>
struct AggregatedStatElement
```

**6.** Имена константных аргументов шаблона: либо соответствуют правилам именования переменных, либо в простых случаях используется `N`.

```cpp theme={null}
template <bool without_www>
struct ExtractDomain
```

**7.** Для абстрактных классов (интерфейсов) можно использовать префикс `I`.

```cpp theme={null}
class IProcessor
```

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

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

```cpp theme={null}
bool info_successfully_loaded = false;
```

**9.** Имена `define` и глобальных констант пишутся в стиле ALL\_CAPS с символами подчеркивания.

```cpp theme={null}
#define MAX_SRC_TABLE_NAMES_TO_STORE 1000
```

**10.** Имена файлов должны оформляться в том же стиле, что и их содержимое.

Если файл содержит только один класс, имя файла должно совпадать с именем класса (CamelCase).

Если файл содержит только одну функцию, имя файла должно совпадать с именем функции (camelCase).

**11.** Если имя содержит аббревиатуру, то:

* В именах переменных аббревиатура должна записываться строчными буквами: `mysql_connection` (не `mySQL_connection`).
* В именах классов и функций сохраняйте заглавные буквы в аббревиатуре: `MySQLConnection` (не `MySqlConnection`).

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

```cpp theme={null}
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 theme={null}
timer (not m_timer)
```

**14.** Для констант в `enum` используйте CamelCase с заглавной буквы. Также допустим стиль ALL\_CAPS. Если `enum` не является локальным, используйте `enum class`.

```cpp theme={null}
enum class CompressionMethod
{
    QuickLZ = 0,
    LZ4     = 1,
};
```

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

не `T&#95;PAAMAYIM&#95;NEKUDOTAYIM`

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

`AST`, `SQL`.

Не `NVDH` (какой-то случайный набор букв)

Усечённые слова допустимы, если сокращённый вариант широко используется.

Также можно использовать сокращение, если рядом с ним в комментариях указано полное название.

**17.** Имена файлов с исходным кодом C++ должны иметь расширение `.cpp`. Файлы заголовков должны иметь расширение `.h`.

<div id="how-to-write-code">
  ## Как писать код
</div>

**1.** Управление памятью.

Ручное освобождение памяти (`delete`) можно использовать только в библиотечном коде.

В библиотечном коде оператор `delete` можно использовать только в деструкторах.

В прикладном коде память должен освобождать объект, который ею владеет.

Примеры:

* Проще всего разместить объект на стеке или сделать его членом другого класса.
* Для большого количества небольших объектов используйте контейнеры.
* Для автоматического освобождения небольшого числа объектов, размещённых в куче, используйте `shared_ptr/unique_ptr`.

**2.** Управление ресурсами.

Используйте `RAII`; см. выше.

**3.** Обработка ошибок.

Используйте исключения. В большинстве случаев достаточно просто сгенерировать исключение, а перехватывать его не требуется (благодаря `RAII`).

В приложениях для офлайн-обработки данных часто допустимо не перехватывать исключения.

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

В функциях потоков следует перехватывать и сохранять все исключения, чтобы повторно выбросить их в главном потоке после `join`.

```cpp theme={null}
/// Если вычисления ещё не начались, вычислить первый блок синхронно
if (!started)
{
    calculate();
    started = true;
}
else /// Если вычисления уже выполняются, дождаться результата
    pool.wait();

if (exception)
    exception->rethrow();
```

Никогда не скрывайте исключения, не обработав их. Никогда не пишите все исключения в лог без разбора.

```cpp theme={null}
//Неверно
catch (...) {}
```

Если вам нужно игнорировать некоторые исключения, делайте это только для конкретных случаев, а остальные исключения перебрасывайте.

```cpp theme={null}
catch (const DB::Exception & e)
{
    if (e.code() == ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION)
        return nullptr;
    else
        throw;
}
```

При использовании функций с кодами ответа или `errno` всегда проверяйте результат и в случае ошибки сгенерируйте исключение.

```cpp theme={null}
if (0 != close(fd))
    throw ErrnoException(ErrorCodes::CANNOT_CLOSE_FILE, "Cannot close file {}", file_name);
```

Вы можете использовать assert для проверки инварианта в коде.

**4.** Типы исключений.

Нет необходимости использовать сложную иерархию исключений в прикладном коде. Текст исключения должен быть понятен системному администратору.

**5.** Генерация исключений в деструкторах.

Это не рекомендуется, но допускается.

Используйте следующие варианты:

* Создайте функцию (`done()` или `finalize()`), которая заранее выполнит всю работу, способную привести к исключению. Если эта функция была вызвана, позже в деструкторе исключений быть не должно.
* Слишком сложные задачи (например, отправку сообщений по сети) можно вынести в отдельный метод, который пользователь класса должен будет вызвать до уничтожения объекта.
* Если в деструкторе возникает исключение, его лучше записать в лог, чем скрывать (если доступен логгер).
* В простых приложениях допустимо полагаться на `std::terminate` (для случаев с `noexcept` по умолчанию в C++11) для обработки исключений.

**6.** Анонимные блоки кода.

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

```cpp theme={null}
Block block = data.in->read();

{
    std::lock_guard<std::mutex> lock(mutex);
    data.ready = true;
    data.block = block;
}

ready_any.set();
```

**7.** Многопоточность.

В программах для офлайн-обработки данных:

* Старайтесь добиться максимально возможной производительности на одном ядре CPU. Затем при необходимости распараллельте код.

В серверных приложениях:

* Используйте пул потоков для обработки запросов. Пока у нас не было задач, требующих переключения контекста в пространстве пользователя.

Fork не используют для распараллеливания.

**8.** Синхронизация потоков.

Часто можно сделать так, чтобы разные потоки использовали разные ячейки памяти (а ещё лучше — разные линии кэша) и вообще не использовать синхронизацию потоков (кроме `joinAll`).

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

В остальных случаях используйте системные примитивы синхронизации. Не используйте активное ожидание.

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

Не пытайтесь реализовывать lock-free-структуры данных, если это не ваша основная область экспертизы.

**9.** Указатели и ссылки.

В большинстве случаев предпочитайте ссылки.

**10.** `const`.

Используйте константные ссылки, указатели на константы, `const_iterator` и `const`-методы.

Считайте `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::move`; передавайте по ссылке, если хотите обновлять значение в цикле.

Если функция принимает владение объектом, созданным в куче, используйте для аргумента тип `shared_ptr` или `unique_ptr`.

**14.** Возвращаемые значения.

В большинстве случаев просто используйте `return`. Не пишите `return std::move(res)`.

Если функция выделяет объект в куче и возвращает его, используйте `shared_ptr` или `unique_ptr`.

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

```cpp theme={null}
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 theme={null}
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.** Логирование.

См. примеры в коде.

Перед commit удаляйте все бессмысленные и отладочные записи в журнале, а также любые другие виды отладочного вывода.

Следует избегать логирования в циклах, даже на уровне 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 theme={null}
auto f() -> void
```

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

```cpp theme={null}
//правильно
std::string s = "Hello";
std::string s{"Hello"};

//неправильно
auto s = std::string{"Hello"};
```

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

<div id="unused-features-of-c">
  ## Неиспользуемые возможности C++
</div>

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

**2.** Конструкции, для которых в современном C++ предусмотрен удобный синтаксический сахар, например:

```cpp theme={null}
// Традиционный способ без синтаксического сахара
template <typename G, typename = std::enable_if_t<std::is_same<G, F>::value, void>> // SFINAE через std::enable_if, использование ::value
std::pair<int, int> func(const E<G> & e) // явно указанный тип возвращаемого значения
{
    if (elements.count(e)) // проверка вхождения через .count()
    {
        // ...
    }

    elements.erase(
        std::remove_if(
            elements.begin(), elements.end(),
            [&](const auto x){
                return x == 1;
            }),
        elements.end()); // идиома remove-erase

    return std::make_pair(1, 2); // создание пары через make_pair()
}

// С синтаксическим сахаром (C++14/17/20)
template <typename G>
requires std::same_v<G, F> // SFINAE через концепт C++20, использование псевдонима шаблона C++14
auto func(const E<G> & e) // вывод типа возвращаемого значения через auto (C++14)
{
    if (elements.contains(e)) // проверка вхождения через .contains в C++20
    {
        // ...
    }

    elements.erase_if(
        elements,
        [&](const auto x){
            return x == 1;
        }); // std::erase_if в C++20

    return {1, 2}; // или: return std::pair(1, 2); // создание пары через список инициализации или инициализацию значением (C++17)
}
```

<div id="platform">
  ## Платформа
</div>

**1.** Мы пишем код для конкретной платформы.

Но при прочих равных предпочтение отдаётся кроссплатформенному или переносимому коду.

**2.** Язык: C++20 (см. список доступных [возможностей C++20](https://en.cppreference.com/w/cpp/compiler_support#C.2B.2B20_features)).

**3.** Компилятор: `clang`. На момент написания (март 2025 года) код компилируется с помощью clang версии >= 19.

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

**4.** ОС: Ubuntu Linux, не старее Precise.

**5.** Код пишется для архитектуры CPU x86\_64.

Набор инструкций CPU — минимально поддерживаемый среди наших серверов. В настоящее время это SSE 4.2.

**6.** Используйте флаги компиляции `-Wall -Wextra -Werror -Weverything` с несколькими исключениями.

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

**8.** Код разрабатывается и отлаживается в release-конфигурации.

<div id="tools">
  ## Инструменты
</div>

**1.** KDevelop — хорошая IDE.

**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.** Неиспользуемый код удаляется из репозитория.

<div id="libraries">
  ## Библиотеки
</div>

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

**2.** Запрещено использовать библиотеки из пакетов операционной системы. Также запрещено использовать предустановленные библиотеки. Все библиотеки должны поставляться в виде исходного кода в каталоге `contrib` и собираться вместе с ClickHouse. Подробности см. в [рекомендациях по добавлению новых сторонних библиотек](/ru/resources/develop-contribute/contribute/contrib#adding-and-maintaining-third-party-libraries).

**3.** Предпочтение всегда отдается библиотекам, которые уже используются.

<div id="general-recommendations">
  ## Общие рекомендации
</div>

**1.** Пишите как можно меньше кода.

**2.** Старайтесь выбирать самое простое решение.

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

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

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

**6.** Упрощать код полезно. По возможности уменьшайте его объем.

<div id="additional-recommendations">
  ## Дополнительные рекомендации
</div>

**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 theme={null}
function(
  T1 x1,
  T2 x2)
```

```cpp theme={null}
function(
  size_t left, size_t right,
  const & RangesInDataParts ranges,
  size_t limit)
```

```cpp theme={null}
function(size_t left, size_t right,
  const & RangesInDataParts ranges,
  size_t limit)
```

```cpp theme={null}
function(size_t left, size_t right,
      const & RangesInDataParts ranges,
      size_t limit)
```

```cpp theme={null}
function(
      size_t left,
      size_t right,
      const & RangesInDataParts ranges,
      size_t limit)
```
