Ещё одна статья от нашего друга ice, стиль авторский.
Хочу поговорить об оформлении кода, я считаю что если код правильно оформлен, то это уже заведомо хороший код. Если в вашем коде нет определенного стандарта и каждый ваш исходник оформляется по разному, то вы в скором времени не сможете просто прочитать свой код. При написании больших проектов вскоре вы забываете что писали, и смотрите на код будто на чужой.
Но если придерживаться определенного стандарта оформления, то код превращается в документацию, вам достаточно взглянуть на этот код, и вы понимаете что тут происходит, и необязательно этот код ваш. Очень хорошо придерживаться стандарта когда вы разрабатываете некоторый софт совместно. Вот только представьте если каждый программист будет воротить оформление кода как он захочет. Один кодит в виндовс стиле, это повсеместно используется верхний регистр и присутствие типов в переменных, другой кодит в юникс стиле, третий еще как то. То взглянув на такой код, ты будешь смотреть не как в документацию а как в темный тернистый лес.
Другой хорошей характеристикой оформления кода, это расстановка отладочных сообщений, там где это только возможно и невозможно, в любой момент можно будет логировать программу в месте падения, и это вас практически избавит от долгой кропотливой отладки.
Оформление кода я покажу на примере, так как лучше один раз показать, чем писать кучу букв тут. Данное оформление справедливо только для “cи” кода,
и оно хорошо применимо для любой платформы.
#ifndef __VFS__
#define __VFS__
#include <types.h>
#include <logger.h>
BOOLEAN
VfsAddModuleNative(
CHAR *pName);
#endif
BOOLEAN
VfsAddModuleNative(
CHAR *pName)
{
BOOLEAN Result;
CHAR Name[260];
VOID *pModule;
VOID (*InitPlugin)();
Result=FALSE;
sprintf(Name,"%s/%s.so",LIBPATH,pName);
pModule=dlopen(Name,RTLD_LAZY);
ASSERT(pModule);
if (pModule)
{
sprintf(Name,"init%s",pName);
InitPlugin=dlsym(pModule,Name);
ASSERT(InitPlugin);
if (InitPlugin)
{
InitPlugin();
Result=TRUE;
}
}
return Result;
}
Итак каких же стандартов я придерживаюсь в таком коде?
- Типы записываются в верхнем регистре
- Имена переменных, и всех ключевых слов начинаются с заглавной буквы
- Тип в именах переменных указывается только для указателей, pData или ppData (остальные типы считаю указывать избыточным)
- Все элементы функции (тип, имя, аргументы) начинаются с новой строки
- Между кодовыми блоками ({}) ставятся переносы строки
- Функции которые предполагается включать в заголовочные файлы, должны начинаться с имени “.с” файла (VfsSomeFunction)
- Функции которые не используются в интерфейсах, должны быть определены как static, что бы не захламлять пространство имен
- В заголовочных файлах использовать конструкции #ifndef __VFS__ #define __VFS__, в имени участвует имя модуля
- Переменные с именем в один литерал (i,j и т.д.) записываются в нижнем регистре
- Имена макросов записываются в верхнем регистре
- Весь код должен быть пронизан ASSERT
- Кодовые блоки ({ или }) или ключевые слова, начинаются всегда с новой строки
- Глобальные переменные именуются с “g_”, например g_SomeVariable
- Глобальные переменные не должны находиться в хидерах, если нужно передать такую переменную в другой модуль, то использовать для этого функцию
Так как код планируется кросплатформенный, то хорошо бы определиться с типами, так как если брать windows, то 4-байтную переменную можно определить кучей типов DWORD, UINT и еще не весть что.
Так как структуры планируется именовать в верхнем регистре то у меня создан такой файлик
#ifndef _TYPES_ #define _TYPES_ #include <stdio.h> #include <stdlib.h> #define TRUE 1 #define FALSE 0 typedef char CHAR; typedef unsigned char UCHAR; typedef short SHORT; typedef unsigned short USHORT; typedef int INT; typedef unsigned int UINT; typedef long LONG; typedef unsigned long ULONG; typedef long long LONG64; typedef unsigned long long ULONG64; typedef int BOOLEAN; typedef void VOID; typedef size_t SIZE_T; #define CONST const #define IN #define OUT #define OPTIONAL #endif
Тут заданы все необходимые простые типы, которые могут вам понадобиться и все в едином стандарте (верхний регистр)
Отдельный заголовочный файл для отладочных сообщений
#ifndef _LOGGER_
#define _LOGGER_
#ifdef _DEBUG_
#include <string.h>
#include <unistd.h>
#define LOG(pSpec, ...) \
{ \
CONST CHAR* pFile = strrchr(__FILE__, '\\'); \
printf("[PID:%5u]R3:%s!%s(%u): "pSpec"\n", \
(int)getpid(), \
pFile?pFile+1:__FILE__, \
__FUNCTION__, \
__LINE__, \
##__VA_ARGS__); \
}
#define LOG_INFO(pSpec, ...) LOG("INFO: " pSpec,##__VA_ARGS__)
#define LOG_ERROR(pSpec, ...) LOG("ERROR: " pSpec,##__VA_ARGS__)
#define LOG_DEBUG(pSpec, ...) LOG("DEBUG: " pSpec,##__VA_ARGS__)
#define ASSERT_F(Condition,pMessage,...) \
{ \
if (!(Condition)) \
{ \
LOG("ASSERT FAILED "pMessage,##__VA_ARGS__); \
/*__asm__ volatile("int $0x03");*/ \
} \
}
#define ASSERT(Condition) ASSERT_F(Condition,"")
#else
#define LOG(pSpec,...)
#define LOG_INFO(pSpec, ...)
#define LOG_ERROR(pSpec, ...)
#define LOG_DEBUG(pSpec, ...)
#define ASSERT_F(Condition,pMessage,...)
#define ASSERT(Condition)
#endif
#endif
Многие не используют ASSERT в своих программах, а используют отладочные сообщения. Но это не всегда оправдано, вот например возьмем код выше, и перепишем его в виде логирования с отладочными сообщениями
pModule=dlopen(Name,RTLD_LAZY);
if (pModule)
{
}
else
{
LOG_ERROR("Все пошло не так как хотели!!!!");
}
Нам пришлось ввести конструкцию else, которая создает лишний код, и когда мы сделаем релиз, то код с else останется.
Да и это некрасивый код. Гораздо приятнее он выглядит с ASSERT, в нем сразу реализовано это условие проверки, что если не так. То логирует номер строки и имя файла, и не нужно выдумывать сообщение о падении, все кратко и просто.
Запись Порядок в коде – порядок в голове впервые появилась VxLab.
from VxLab http://ift.tt/1KEqgFg
via IFTTT
Комментариев нет:
Отправить комментарий