Соглашения о кодировании

1. Основные принципы

1. Целями внедрения настоящих соглашений являются стандартизация исходного кода проекта и приведение его к единому виду, что позволяет:

(i) снизить количество ошибок в программном коде и повысить его качество;

(ii) повысить эффективность работы команды разработчиков, снизить время онбординга новых разработчиков в команду;

(iii) формализовать критерии для выявления ошибок и улучшить автоматизированные механизмы, применяемые для контроля качества кода;

(iv) упростить восприятие кода и снизить нагрузку на память и зрение при чтении кода.

2. Настоящие соглашения обязательны к применению в коде Sky/IX.

3. В случае, если существующий код не соответствует настоящим соглашениям, он приводится в соответствие с ними при модификации.

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

5. Пункты соглашений, касающиеся структуры и семантики исходного кода, проверяются вручную на ревью.

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

7. Дополнение настоящих соглашений производится итеративно, по мере выявления проблемных мест и возможностей для улучшения. Такая практика позволяет лучше оценить те или иные изменения, нежели одновременное внедрение большого числа изменений.

2. Общие положения

2.1. Разработка производится на языке C++. Используемый стандарт языка — C++23 (ISO/IEC 14882:2024).

2.2. В случаях, когда применение языка C++ невозможно, допускается использование языка C. Используемый стандарт языка — C23 (ISO/IEC 9899:2023).

2.3. Использование расширений GNU не допускается. Используемые расширения должны поддерживаться как минимум компиляторами CLang, GCC и Intel.

2.4. В качестве основного компилятора используется CLang версии 19 или более новый.

2.5. Для документирования исходного кода используется Doxygen.

3. Правила оформления

3.1. Кодировка файлов с исходным кодом — UTF-8.

3.2. Используются окончания строк UNIX (LF).

3.3. Размер отступа устанавливается равным 4 символам. Для создания отступов используются пробельные символы вместо табуляции.

3.4. Максимальная длина строки — 120 символов, рекомендуемая в общем случае — не более 80 символов.

3.5. В редких случаях, когда попытки сократить строку, превышающую 120 символов, приводят к ухудшению читаемости, допускается превышение лимита. На таких участках кода следует отключать автоматическое форматирование кода.

3.6. Комментарии пишутся на английском языке.

3.7. Использование специальных символов Unicode и эмодзи в исходном коде и комментариях запрещено.

3.8. Для комментариев (за исключением блоков Doxygen) используется современный синтаксис (//). Начало комментария должно быть отделено одним пробелом от открывающей последовательности:

// This is a well-formed comment

3.9. Допускается выравнивание объявлений переменных и присваиваний:

int  a   = 1;
long xyz = 2;

3.10. Аргументы унарных арифметических операций не отделяются пробелом от оператора:

*ptr++ = --i;

3.11. Аргументы бинарных арифметических и логических операций отделяются от оператора символом пробела, при этом между скобками и выражением пробелы не ставятся:

int a = 2 * (b + c) / d[4];

4. Правила именования

4.1. Имена функций, переменных и типов состоят из строчных букв, разделяемых знаком подчеркивания: item, item_name и т.п.

4.2. Имена полей класса должны иметь префикс m_.

4.3. Имена интегральных типов должны иметь суффикс _t.

4.4. Для переменных, хранящих порядковый номер, следует использовать суффикс idx (disk_idx).

4.5. Следует избегать избыточных сокращений, кроме стандартных (см. "Стандартные сокращения").

4.6. Имена булевых переменных должны подразумевать значения true или false и позволять однозначно интерпретировать каждое из значений.

4.7. Имена булевых переменных должны быть утвердительными. Отрицание в имени переменной может привести к двойным отрицаниям в условиях. Для решения этой проблемы следует использовать пары антонимов (см. "Стандартные антономические пары").

5. Правила типизации

5.1. Для нулевого указателя используется ключевое слово nullptr. Использование макроса NULL не допускается.

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

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

5.4. Для размеров и смещений объектов, структур, файлов и т.п. должны использоваться стандартные типы size_t, ssize_t и off_t, определённые в заголовочном файле <stddef.h>.

5.5. В форматных функциях printf(), scanf() и т.д. для типов size_t и ssize_t следует использовать специальные спецификаторы %zu (%zx) и %zd (%zi) соответственно.

5.6. Для целых чисел со строго заданным размером в битах, либо используемых при взаимодействии с аппаратным обеспечением, должны использоваться стандартные целочисленные типы, определённые в заголовочном файле <stdint.h>, такие как int32_t, uint32_t, int_least32_t и т.п.

5.7. Если ситуация предусматривает семантический тип данных, должны использоваться семантические типы данных. Например, при парсинге данных, состоящих из октетов, в коде на C++ должен быть использован тип std::byte, а в коде на C - определён типа octet_t, эквивалентный uint8_t:

typedef uint8_t octet_t;

5.8. Если функция не имеет аргументов, список аргументов должен быть объявлен как void:

void some_func(void);

5.9. Если изменение данных не предусматривается, переменная, аргумент функции или её возвращаемое значение должны быть объявлены с модификатором const.

6. Структура и декомпозиция

6.1. Объявление нескольких переменных на одной строке запрещено.

6.2. Обратные сравнения с константой запрещены.

6.3. Переиспользование одной и той же переменной для двух разных целей нежелательно. Исключения:

  • итераторы (item, iter, node, …​) и переменные циклов (i, j, k, …​), объявление которых необходимо вне цикла (например, если необходимо иметь доступ к индексу, на котором цикл остановился);

  • общие переменные кода ошибки/возврата (err, rv).

7. Стандартные сокращения

arg

argument

avg

average

buf

buffer

cmd

command

ctx

context

config

configuration

cb

callback

crit

critical

ctor

constructor

dbg

debug

dev

device

dtor

destructor

err

error

env

environment

fd

file descriptor

fmt

format

func

function

id

identifier

idx

index

iter

iterator

impl

implementation

len

length

lhs

left-hand side

mem

memory

msg

message

num

number

obj

object

off

offset

out

output

param

parameter

phys

physical

ptr

pointer

prop

property

rv

return value

req

request

resp

response

rhs

right-hand side

sm

state machine

spec

specification

str

string

sys

system

thr

thread

ver

version

vol

volume

warn

warning

8. Стандартные антонимические пары

  • complete/incomplete

  • ok/err

  • found/missing

  • success/failure (succeeded/failed)

  • available/unavailable

  • allow/deny (allowed/denied)

  • done/undone

  • ready/not_ready

  • required/optional

  • secure/insecure

  • start/finish (started/finished)

  • force/relax (forced/relaxed)

  • active/passive