1.Но если всё тело функции достаточно короткое (один statement) - при желании, его можно целиком разместить на одной строке. При этом, вокруг фигурных скобок ставятся пробелы (кроме пробела на конце строки).
15. Если на весь файл один namespace и кроме него ничего существенного нет - то отступ внутри namespace не нужен.
16. Если блок для выражения if, for, while... состоит из одного statement-а, то фигурные скобки писать не обязательно. Вместо этого поместите statement на отдельную строку. Этим statement-ом также может быть вложенный if, for, while... Но если внутренний statement содержит фигурные скобки или else, то у внешнего блок следует писать в фигурных скобках.
Аналогично для маленьких методов в любых классах/структурах.
Для шаблонных классов/структур, лучше не отделять объявления методов от реализации (так как иначе они всё равно должны быть определены в той же единице трансляции).
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. Для закомментированных кусков кода, используются обычные, не "документирующие" комментарии.
1. Удаляйте закомментированные куски кода перед коммитом.
11.Не нужно писать нецензурную брань в комментариях или коде.
12.Не пишите прописными буквами. Не используйте излишнее количество знаков препинания.
```cpp
/// WHAT THE FAIL???
```
13.Не составляйте из комментариев строки-разделители.
12. Параметры конструктора, использующиеся сразу же для инициализации соответствующих членов класса, следует назвать также, как и члены класса, добавив подчёркивание в конец.
Также можно называть параметры конструктора так же, как и члены класса (не добавлять подчёркивание), но только если этот параметр не используется в теле конструктора.
13. Именование локальных переменных и членов класса никак не отличается (никакие префиксы не нужны).
15.Все имена - по английски. Транслит с русского использовать нельзя.
```
не Stroka
```
16. Сокращения (из нескольких букв разных слов) в именах можно использовать только если они являются общепринятыми (если для сокращения можно найти расшифровку в английской википедии или сделав поисковый запрос).
`AST`, `SQL`.
Не`NVDH` (что-то неведомое)
Сокращения в виде обрезанного слова можно использовать, только если такое сокращение является широко используемым.
Впрочем, сокращения также можно использовать, если расшифровка находится рядом в комментарии.
17. Имена файлов с исходниками на C++ должны иметь расширение только .cpp. Заголовочные файлы - только .h.
## Как писать код
1. Управление памятью.
Ручное освобождение памяти (delete) можно использовать только в библиотечном коде.
В свою очередь, в библиотечном коде, оператор delete можно использовать только в деструкторах.
В прикладном коде следует делать так, что память освобождается каким-либо объектом, который владеет ей.
Примеры:
- проще всего разместить объект на стеке, или сделать его членом другого класса.
- для большого количества маленьких объектов используйте контейнеры.
- для автоматического освобождения маленького количества объектов, выделенных на куче, используйте shared_ptr/unique_ptr.
2. Управление ресурсами.
Используйте RAII и см. пункт выше.
3. Обработка ошибок.
Используйте исключения. В большинстве случаев, нужно только кидать исключения, а ловить - не нужно (потому что RAII).
В программах offline обработки данных, зачастую, можно не ловить исключения.
В серверах, обрабатывающих пользовательские запросы, как правило, достаточно ловить исключения на самом верху обработчика соединения.
В функциях потока, следует ловить и запоминать все исключения, чтобы выкинуть их в основном потоке после join.
```cpp
/// Если вычислений ещё не было - вычислим первый блок синхронно
if (!started)
{
calculate();
started = true;
}
else /// Если вычисления уже идут - подождём результата
pool.wait();
if (exception)
exception->rethrow();
```
Ни в коем случае не «проглатывайте» исключения без разбора. Ни в коем случае, не превращайте все исключения без разбора в сообщения в логе.
Не`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. Многопоточность.
В программах offline обработки данных:
- cначала добейтесь более-менее максимальной производительности на одном процессорном ядре, потом можно распараллеливать код, но только если есть необходимость.
В программах - серверах:
- используйте пул потоков для обработки запросов. На данный момент, у нас не было задач, в которых была бы необходимость использовать 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 char, 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>;
/** Позволяет создать агрегатную функцию по её имени.
Для прикладного кода отдельный namespace использовать не нужно.
Для маленьких библиотек - не требуется.
Для не совсем маленьких библиотек - поместите всё в namespace.
Внутри библиотеки в .h файле можно использовать namespace detail для деталей реализации, не нужных прикладному коду.
В .cpp файле можно использовать static или анонимный namespace для скрытия символов.
Также, namespace можно использовать для enum, чтобы соответствующие имена не попали во внешний namespace (но лучше использовать enum class).
16. Отложенная инициализация.
Обычно, если для инициализации требуются аргументы, то не пишите конструктор по умопчанию.
Если потом вам потребовалась отложенная инициализация, то вы можете дописать конструктор по умолчанию (который создаст объект с некорректным состоянием). Или, для небольшого количества объектов, можно использовать shared_ptr/unique_ptr.
Если класс не предназначен для полиморфного использования, то не нужно делать функции виртуальными зря. Это относится и к деструктору.
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 guard-ы писать не нужно.
23. using.
using namespace не используется.
using что-то конкретное - можно. Лучше локально - внутри класса или функции.
24.Не нужно использовать trailing return type для функций, если в этом нет необходимости.
[auto f() -> void;]{.strike}
25.Не нужно объявлять и инициализировать переменные так:
Если ваш код в master-е ещё не собирается, перед push-ем - исключите его из сборки, также вы будете должны его доработать или удалить в течение нескольких дней.
9. Для нетривиальных изменений, используются бранчи. Следует загружать бранчи на сервер.
3. Если в пакетах нет нужной библиотеки, или её версия достаточно старая, или если она собрана не так, как нужно, то можно использовать библиотеку, устанавливаемую не из пакетов.
4. Если библиотека достаточно маленькая и у неё нет своей системы сборки, то следует включить её файлы в проект, в директорию contrib.
5. Предпочтение всегда отдаётся уже использующимся библиотекам.
3.Не нужно писать код, если вы ещё не знаете, что будет делать ваша программа, и как будет работать её внутренний цикл.
4.В простейших случаях, используйте using вместо классов/структур.
5. Если есть возможность - не пишите конструкторы копирования, операторы присваивания, деструктор (кроме виртуального, если класс содержит хотя бы одну виртуальную функцию), move-конструкторы и move-присваивания. То есть, чтобы соответствущие функции, генерируемые компилятором, работали правильно. Можно использовать default.
6. Приветствуется упрощение и уменьшение объёма кода.
Не рекомендуется. То есть, пишите memcpy вместо std::memcpy.
Причина - существуют похожие нестандартные функции, например, memmem. Мы можем использовать и изредка используем эти функции. Эти функции отсутствуют в namespace std.
Если вы везде напишете std::memcpy вместо memcpy, то будет неудобно смотреться memmem без std::.
Тем не менее, указывать std:: тоже допустимо, если так больше нравится.
3. Использование функций из C при наличии аналогов в стандартной библиотеке C++.