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

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

Стандарт оформления кода (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;