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