Стандарт оформления кода — различия между версиями

Материал из SkyXEngine wiki
Перейти к: навигация, поиск
м (define и enum)
(Имена в коде)
Строка 12: Строка 12:
  
 
== Имена в коде ==
 
== Имена в коде ==
Имена переменных формируются в соответствии с венгерской нотацией, то есть сначала префикс (как в таблице) за префиксом следует имя переменной в смешанном регистре, начиная с верхнего.
+
Имена переменных формируются в соответствии с венгерской нотацией, то есть сначала префикс/префиксы (как в таблицых) за префиксом/префиксами следует имя переменной в смешанном регистре, начиная с верхнего. Выделено 2 типа префиксов:
 +
* контекстный - определяет контекст переменной, например глобальное пространство имен, член класса, константа, интерфейс и прочее
 +
* типовый - определяет тип переменной, строка, знаковое целочисленное и прочее
  
 
{| class="wikitable" border="1" cellpadding="2"
 
{| class="wikitable" border="1" cellpadding="2"
  |+Таблица префиксов
+
  |+Таблица контекстных префиксов
 
  |-
 
  |-
  !Префикс !! Значения !! Тип
+
  !Префикс !! Значения
 
  |-
 
  |-
 
  !g_
 
  !g_
  | префикс для глобальной переменной ||
+
  | префикс для глобальной переменной
 
  |-
 
  |-
 
  !m_
 
  !m_
  | префикс для переменной класса ||
+
  | префикс для переменной класса
 
  |-
 
  |-
 
  !c_
 
  !c_
  | константа (префикс для типа) ||
+
  | константа (префикс для типа)
 +
|-
 +
!I
 +
| интерфейс
 +
|-
 +
!C
 +
| класс
 +
|}
 +
 
 +
Константы только для локального применения, глобальные константы должны быть в define или enum.
 +
 
 +
{| class="wikitable" border="1" cellpadding="2"
 +
|+Таблица типовых префиксов
 +
|-
 +
!Префикс !! Значения !! Тип
 
  |-
 
  |-
 
  !s
 
  !s
Строка 39: Строка 55:
 
  !i
 
  !i
 
  | целочисленное значение || int
 
  | целочисленное значение || int
|-
 
!i8
 
| || int8_t
 
|-
 
!i16
 
| || int16_t
 
|-
 
!i32
 
| || int32_t
 
 
  |-
 
  |-
 
  !u
 
  !u
 
  | || unsigned int
 
  | || unsigned int
 
  |-
 
  |-
  !u8
+
  !i8, i16, i32
  | || uint8_t
+
  | || int8_t, int16_t, int32_t
 
  |-
 
  |-
  !u16
+
  !u8, u16, u32
  | || uint16_t
+
  | || uint8_t, uint16_t, uint32_t
|-
 
!u32
 
| || uint32_t
 
 
  |-
 
  |-
 
  !b, is, can, has, should, use
 
  !b, is, can, has, should, use
 
  | is – является ли, can – может ли, has – имеет ли, should – должен ли, use – используется ли || bool
 
  | is – является ли, can – может ли, has – имеет ли, should – должен ли, use – используется ли || bool
|-
 
!l
 
| || long
 
|-
 
!ul
 
| || unsigned long
 
 
  |-
 
  |-
 
  !a
 
  !a
Строка 87: Строка 85:
 
  !o
 
  !o
 
  | объект, экземпляр класса (не указатель) ||  
 
  | объект, экземпляр класса (не указатель) ||  
|-
 
!I
 
| интерфейс ||
 
|-
 
!C
 
| класс || class
 
 
  |-
 
  |-
 
  !t
 
  !t
Строка 99: Строка 91:
 
  !dt
 
  !dt
 
  | дата (и время) ||  
 
  | дата (и время) ||  
|-
 
!r
 
| ссылка || &
 
 
  |-
 
  |-
 
  !fn
 
  !fn

Версия 08:32, 20 июня 2019

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

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

  • исходный код движка,
  • шейдеры.


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

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

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

Имена в коде

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

  • контекстный - определяет контекст переменной, например глобальное пространство имен, член класса, константа, интерфейс и прочее
  • типовый - определяет тип переменной, строка, знаковое целочисленное и прочее
Таблица контекстных префиксов
Префикс Значения
g_ префикс для глобальной переменной
m_ префикс для переменной класса
c_ константа (префикс для типа)
I интерфейс
C класс

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

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

Префикс fn используется для переменной указателя который ссылается на функцию.

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

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

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

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

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

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

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

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

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

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

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

vector<Point>  aPoints;

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

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

namespace gdata 
{};


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

  • 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

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

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

Директивы включения (#include) должны располагаться только в начале файла.

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

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

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

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

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

Прочее

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

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

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

  • типы данных которым префикс класса не применим, к примеру float3, Quaternion, matrix
  • дефайны, использование которых достаточно частое и необходимо максимально облегчить их запоминанием и написание, к примеру: 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;