Перейти к основному содержимому

Стандарт оформления кода

Стандарт оформления кода (code style) — это набор определенных правил при написании исходного кода, на определенном языке программирования.

Данный стандарт распространяется на:

  • Исходный код движка;
  • Шейдеры.

Общее правило именования​

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

К примеру, класс CManagerTimes использует большое количество счетчиков времени (но кроме временных данных есть и другие). В этом случае логично предположить что «время» является ключевым фактором в именовании, значит, слово time будет наиболее приоритетным словом, и претендует на начало в имени, также многие параметры связаны со «скоростью» течения времени, но «скорость» описывает течение времени, значит «скорость» будет вторым по значимости словом, в итоге имя переменной будет: m_iTimeSpeed.

Имена в коде​

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

Префиксы​

Выделено 2 типа префиксов:

  • Контекстный - определяет контекст переменной, например глобальное пространство имен, член класса, константа, интерфейс и прочее;
  • Типовый - определяет тип переменной, строка, знаковое целочисленное и прочее.

Таблица контекстных префиксов

ПрефиксЗначения
g_префикс для глобальной переменной
m_префикс для переменной класса
c_константа (префикс для типа)
Iинтерфейс
Cкласс

Константы только для локального применения, глобальные константы должны быть в define или enum.

Таблица типовых префиксов

ПрефиксЗначенияТип
sстрокаобъекты типа string
szстрока, ограниченная нулевым символомchar []
chсимволchar
iцелочисленное значениеint
uunsigned int
i8, i16, i32int8_t, int16_t, int32_t
u8, u16, u32uint8_t, uint16_t, uint32_t
b, is, can, has, should, useis – является ли, can – может ли, has – имеет ли, should – должен ли, use – используется лиbool
aмассивarray
vвекторfloat2, float3, float4 и прочее
idидентификаторint32_t
sizeразмерsize_t
pуказатель*
oобъект, экземпляр класса (не указатель)
tвремя
dtдата (и время)
fnпеременная-функция
mxmutex
spSpinLock
mapассоциативный массив
mматрица

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

Встроенные и элементарные типы​

Встроенные (int, char и другие) и элементарные типы (float3, matrix и другие) должны писаться в нижнем регистре, желательно без разделения символом подчеркивания _.

Классы и структуры​

Имена классов/структур, должны быть обязательно написаны в смешанном регистре, начиная с верхнего, при этом структуры должны быть без префикса присущего классу.

Пример имен классов:

  • CPoint;
  • CCamera.

Пример имен структур:

  • VertexElement;
  • AdapterDesc.

Настоятельно рекомендуется не использовать префиксы имени проекта в именах типов/классов/структур, для их безотносительной идентификации во внутренней реализации программы/библиотеки. Во внешних файлах (подключаемых) допускается использование префикса проекта в именах.

Имена методов классов должны начинаться с нижнего регистра: getSpeedWalk.

Имена функций должны начинаться с верхнего регистра.

get/set должны быть использованы везде, где осуществляется прямой доступ к атрибуту. В данном случае get/set располагаются как можно ближе к началу имени, а возможно и в самом начале. Однако, все зависит от ситуации.

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

  • get/set;
  • add/remove;
  • create/destroy;
  • start/stop;
  • insert/delete;
  • increment/decrement;
  • old/new;
  • begin/end;
  • first/last;
  • up/down;
  • min/max;
  • next/previous;
  • old/new;
  • open/close;
  • show/hide;
  • suspend/resume.

Шаблоны​

Имена типов в шаблонах должны формироваться следующим образом: первое основное можно называть T, остальные осмысленные имена большими буквами с подчеркиванием.

Аббревиатуры​

Аббревиатуры в именах должны быть написаны в верхнем регистре, до и после должны быть буквы в нижнем регистре для явного выделения аббревиатуры: exportHTMLsource().

Массивы и коллекции​

Для представления массивов (наборов/коллекций) следует использовать множественное число объектов:

vector<Point> aPoints;

Итераторы​

Переменным-итераторам следует давать имена i, j, k, для каждого цикла можно создавать дополнительную переменную il jl kl сохраняя длину массива, чтобы не вычислять каждый раз.

Пространства имен​

Имена пространств имен следует записывать в нижнем регистре.

namespace gdata 
{};

Метки аргументов​

Для идентификации предназначения аргументов рекомендуется использовать пустые дефайны:

  • _out – выходной, будет произведена запись;
  • _in – входящий, без записи;
  • _opt - опциональный, значение может быть недействительным (к примеру вместо указателей можно отправить 0).

define и enum​

define и enum создают константы, поэтому должны быть написаны только в верхнем регистре с использованием нижнего пробела: OBJ_MAX_LEN. При этом имена enum пишутся слитно с префиксом (например: GXTEXTURE_ADDRESS_MODE), а имена define с разделением (например: GX_MAX_SAMPLERS).

Константы в перечислениях должны иметь префикс - общее имя типа, при этом название перечисления должно совпадать с префиксом:

enum COLOR {
COLOR_RED,
COLOR_GREEN,
COLOR_BLUE
};

Если определение дефайна должно вставляться в выражения, то оно должно оборачиваться в скобки:

#define A_ADD_B(a, b) (a + b)

Иначе если определение дефайна является конечной инструкцией, оно должно быть обернуто в фигурные скобки:

#define PRINT(str) {printf(str);}

enum порождает префикс (опционально без префикса/постфикса названия enum'а), префикс можно сокращать до удобно читаемого/пишушегося названия. Примеры:

NPC_STATE_MOVE stateMove;
S4GTYPE typeLastCallFunc;
S4GVM_COMMAND vm_command;
GXBLEND blendSrcColor;

Блоки, операции, отступы​

Логические блоки в коде следует отделять пустой строкой

Отступы в блоках {} равны одному значению табуляции (4 пробела), дополнительные отступы в блоках категорически запрещены.

Каждый символ открытия { и закрытия } блока должен быть расположен на новой строке.

Бинарные операторы окружаются двумя пробелами:

a + b

Унарные операторы пишутся без пробелов:

-2

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

Указатели и ссылки должны быть привязаны только к переменным:

int *a;
int &a;

В случае приведения типов, и при объявлении функций и методов, звездочка привязывается к имени типа:

int *a = (int*)b;
int* GetIntPtr();

Операции разыменования и взятие адреса должны оборачивать в скобки выражение:

&(aArr[0])
*(obj->GetPoint())

При вызове функции после имени функции сразу идет открывающая скобка.

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

Инициализация переменной всегда должны быть соответствующим типом, к примеру: float fNum = 1.0f; а не 1.0.

Приведение типов неявное, если не требуется сделать это явно, либо потеря точности недопустима.

Если в операторе if, for, while, do...while находится выражение, занимающее больше одной строки, оно должно быть взято в скобки блока. Если в операторе if имеется блок else, оба блока должны быть заключены в скобки блока:

if(i > 1)
{
for(int i = 0; i < 10; ++i)
{
printf("%d ", i);
}
}

//Нельзя:
if(i > 1)
for(int i = 0; i < 10; ++i)
{
printf("%d ", i);
}

Примеры оформления конструкций​

Оператор ветвления

if(i > 1)
{
pIndices[iCurIdx++] = iStopIdx;
pIndices[iCurIdx++] = iCurVtx;
pIndices[iCurIdx++] = iCurVtx - 1;
}
else
{
break;
}

Оператор множественного выбора

switch(pNode->Val->type)
{
case CVAR_STRING:
printf("Default: \"%s\"\n", pNode->Val->default_val.c);
break;
case CVAR_INT:
printf("Default: \"%d\"\n", pNode->Val->default_val.i);
break;
case CVAR_FLOAT:
printf("Default: \"%f\"\n", pNode->Val->default_val.f);
break;
case CVAR_BOOL:
printf("Default: \"%s\"\n", pNode->Val->default_val.b ? "true" : "false");
break;
default:
printf("Default: none");
}

Операторы циклов

for(int i = 0; i < 10; ++i)
{
printf("%d ", i);
}
int i = 0;
while(i < 10)
{
printf("%d ", i++);
}
int i = 0;
do
{
printf("%d ", i++);
}
while(i < 10);

Файлы исходного кода​

Имена файлов рекомендуется писать относительно предметной области, без дополнительных префиксов проекта. Префиксы имени проекта необходимы для:

  • библиотек;
  • h и cpp файлов прослоек библиотек (для предоставления функций по управлению функционалом библиотеки);
  • файлов с main функцией, для четкой их идентификации и соотношения с проектом;
  • внешних интерфейсов.

Если в файле (cpp/h) описание одного класса - то файл называется как класс (без свойственного именам классов префикса C) с точностью до регистра букв, если более одного класса, или не классы вовсе – то в соответствии со смыслом предоставляемого функционала. Рекомендуется в h файлах только заголовки, в cpp реализация. Заголовочные файлы должны содержать защиту от вложенного включения:

#idndef __ИМЯ_ФАЙЛА_H
#define __ИМЯ_ФАЙЛА_H
...
#endif __ИМЯ_ФАЙЛА_H

Директивы включения (#include) должны располагаться только в начале файла. В пути допустимы только прямые слэши. Путь до подключаемого файла и имя файла должны быть указаны в соответствии с регистром в файловой системе (например core/Buffer.h).

Каждый файл с должен иметь в конце пустую строку.

Классы, структуры, интерфейсы​

Структуры должны иметь только поля, наличие методов недопустимо. Именование полей в структурах без префикса m_.

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

Классы могут иметь поля только в protected/private разделах.

Разделы класса public, protected и private должны быть отсортированы. Все разделы должны быть явно указаны. Сначала раздел public, затем protected/private.

Прочее​

Использование goto не рекомендуется.

using namespace недопустимо.

Исключения из правил​

Дефайны, использование которых достаточно частое и необходимо максимально облегчить их запоминанием и написание, к примеру: mem_delete, mem_delete_a, mem_release.

Документирование​

Однострочный комментарий начинается с //! после пробела текст комментария.

Многострочный комментарий начинается с /*! после пробела текст комментария.

Метки:

  • note - заметка;
  • warning - предупреждение;
  • param - описание параметра, после метки следует пробел, затем название имя параметра из функции, а после, через пробел, описание;
  • return - описание возвращаемого значения;
  • deprecated - обозначение устаревшего кода;
  • todo - сделать что-то;
  • fixme - пepeдeлaть что-то.

Метки могут быть только в многострочном комментрии.

Каждая метка начинается с новой строки, с пробела и с символа @ за которым следует название метки, затем через пробел текст метки. Метка deprecated может быть без текста.

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

Например:

/*! инициализация контекста
@note В оконном режиме (windowed) можно указать произвольный размер окна, а в полноэкранном (fullscreen) допустимы только поддерживаемые режимы
@note В область рендера будет растянута/сжата по размерам окна
@param hWnd - нативный дескриптов окна
@param iWidth - ширина области рендера в пикселях
@param iHeight - высота области рендера в пикселях
@param isWindowed - true-оконный режим, false-полноэкранный
*/
virtual BOOL initContext(SXWINDOW hWnd, int iWidth, int iHeight, bool isWindowed) = 0;