Стиль С/С++. Соглашения о именах.

Опубликовано в рубрике "Статьи", 8 марта, 2010.

Предлагаю цикл статей по оформлению кода на C/C++ для встраиваемых систем. Написать о стиле целиком – довольно большая работа, поэтому я решил разбить ее на цикл статей. После того, как весь стандарт будет написан, я скомпилирую pdf с квинтэссенцией (О_о), который можно будет распечатать и пользоваться как руководство в вашей работе.

Объединим эмбеддеров Руси!

 style

1. Цель стандарта

Создание вменяемого стандарта (стиля) написания и оформления кода – это, пожалуй, первый и самый простой способ повысить его качество.

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

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

Я решил не изобретать велосипед. За основу взят стандарт оформления кода google:

http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml

Я лишь уточняю моменты, не описанные в этом стандарте. И адаптирую стандарт для применения во встраиваемых системах.

 

2. Зачем?

Зачем новый стандарт? Чем от отличается от других, к примеру, от стандарта Micrium?

Во-первых, этот стандарт не является новым и является fork’ом или клоном (кому как удобнее) стандарта гугла. Я лишь уточнаяю некоторые моменты.

Во-вторых, этот стандарт больше ориентирован на скорость написания кода при сохранении качества, чем на бескомпромиссное качество при очень низкой скорости написания кода.

Во-третьих, этот стандарт является родным, русскоязычным и именно вы, да,-да, вы принимаете участие в его создании.

В-четвертых, этот стандарт (в отличии от многих других стандартов кодирования для встраиваемых систем) допускает применение С++.

В любом случае (даже, если этот стандарт не станет распространенным), как минимум я для себя формализую свой стиль написания кода, что поможет мне в будущем.

Итак, поехали!

Это первая, тестовая часть и сегодня мы опишем самое важное – имена.

 

3. Имена

Самый важный момент в стиле – это стиль обозначения имен переменных. В идеале, прочитав некое название, мы должны иметь возможность определить – что именно оно обозначает – тип, переменную, константу, функцию, или что-то еще.

 

3.1 Общие правила

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

Никогда! Никогда! Не называйте переменные и функции транслитом!

Пример хорошо выбранных имен переменных:

int num_errors;                           // Хорошо.
int num_completed_connections;   // Хорошо.

Пример плохо выбранных имен переменных:

int n;                                         // Плохо — бессмысленно.
int nerr;                                     // Плохо – двусмысленная аббревиатура.
int n_comp_conns;                     // Плохо – двусмысленная аббревиатура.

Имена переменных и типов должны быть существительными MidiHeader, num_of_errors

Имена функций должны быть “командами”: OpenFile(), SetUartBaudRate(). Активно используйте слова Set и Get

 

Стоит избегать аббревеатур, за исключением общеизвестных.

// Хорошо
int num_dns_connections;  // Большинство людей знает, что такое DNS

// Плохо
int wgc_connections;  // Только ваша команда знает, о чем речь.
int pc_reader;            // Много разных вещей можно обозначить как “pc”

Если вам нужно использовать аббревиатуру в названии функции или типа, поступайте как и с остальными словами – первая буква большая, остальные маленькиед

// Хорошо

class SpiDriver;

// Плохо

uint8 SPIGetChar();

3.1 Названия типов

В названиях всех типов – классов, структур, enum’ов и typedef’ов следует каждое новое слово начинать с большой буквы, не разделяя слова.

class UrlTable { …
class UrlTableTester { …
struct UrlTableProperties { …

// typedefs
typedef hash_map<UrlTableProperties *, string> PropertiesMap;

// enums
enum UrlTableErrors { …

 

3.2 Названия переменных

Имена переменных состоят из слов с подчерками между ними. К названиям переменных – членов класса добавляется подчерк.

К примеру:

Обычные переменные:

// Хорошо
string table_name;

// Плохо
string tableName;

Переменные-члены класса:

string table_name_;  // Подчерк

Переменные в структурах обозначаются как обычные переменные:

struct UrlTableProperties
{
  string name;
  int num_entries;
};


Глобальные переменные. К названию глобальной переменной следует добавить префикс g_

 

3.3 Названия функций

Название функций точно такое-же, как и название типов. Каждое новое слово – с большей буквы

AddTableEntry()
DeleteUrl()

Если – член класса используется только для получения доступа к переменной, она обозначается так-же как и переменная.

class MyClass {
public:
  …

  void DoCountEntries();
  int num_entries() const { return num_entries_; }
  void set_num_entries(int num_entries) { num_entries_ = num_entries; }

private:
  int num_entries_;
};

3.4 Названия констант

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

К примеру:

const int kDaysInAWeek = 7;

3.5 Названия перечислений

Перечисления именуются так-же, как и константы.

enum UrlTableErrors
{
  kOK = 0,
  kErrorOutOfMemory,
  kErrorMalformedInput,
};

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

enum AlternateUrlTableErrors
{
  OK = 0,
  OUT_OF_MEMORY = 1,
  MALFORMED_INPUT = 2,
}

 

3.6 Названия макросов

Макросы называются следующим образом:

#define ROUND(x) …
#define PI_ROUNDED 3.0

Стоит избегать использования макросов где это возможно и заменять их константами или inline функциями!

 

3.7 Имена файлов

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

Файлы, содержащие С++ код должны иметь расширение .cpp, файлы содержащие с код — .с заголовочные файлы — .h

Если вам необходимо создать большую inline функцию, или много коротких inline функций, создайте для них файл с окончанием –inl.h

Примеры:

url_table.h      // Обьявление класса или интерфейс модуля.
url_table.cpp   // Реализация класса или модуля.
url_table-inl.h  // Множество inline – функций

 

3.8 Исключения

Если в стандартной библиотеке С/C++ существует аналог того, что вы хотите назвать, используйте соглашение, принятое в библиотеках.

К примеру:

myfopen()                         // функция, подобная стандартной fopen()
uint                                  // typedef от int
sparse_hash_map             // STL-подобная сущность
UINT8_MAX                      // константа, как INT_MAX

 

На сегодня – все. Ваша критика, ваши дополнения и ваша благодарность приветствуются!




Комментарии
  1. doommi написал(а) 9 марта, 2010 в 10:18

    по-моему, если добавить к именам букву имени типа, то будет понятней.. хотя, кому как.. лично я всегда обозначаю переменные с буквой-идентификатором типа и никогда не использую нижнее подчеркивание в именах переменных.. да и вообще в каких-либо других именах.
    Например, переменные обзываю так: iNumber — сразу видно, что переменная типа int,
    или: sTableName — переменная типа стринг )))
    и никогда не сокращаю! т.е. не какой-то там num, а полностью — number.
    В именах функций и процедур так же не сокращаю слова..
    и еще в именах булевых функций в начале добавляю is, например: isNullArray()

    ну это так.. издержки моего стиля ))

    doommi Reply:

    поправочка: не isNullArray, а IsNullArray — с большой буквы. С маленькой пишу только переменные ))
    ps: использовать где-то «приставку» my — идиотизм, по-моему )) это так студенты на первых курсах пту обзывают все переопределенное.. Х)

    BSVi Reply:

    >именам букву имени типа, то будет понятней. Вот это, ИМХО, худшее что можно только придумать ) В тиоге получается некрасивости типа.
    char **lppzsMyCoolStr
    Где-то я видел аналог на русском языке

    Я программирую на Си = местоимЯ глагПрограммирую частНа сущСи

    >никогда не использую нижнее подчеркивание в именах переменных
    Почему? Это-ж классный способ разделить сущности и сделать их более читаемыми!

    сравни — мой вариант — я_программирую_на_си
    и венгерский аналог — действЯПрограммируюНаСи

    >и никогда не сокращаю!
    Вот это правильно! Полностью согласен. Ингда, правда в лом писать что-то типа
    SynchronizeMultipleEvents()

    >в начале добавляю is
    Тоже полностью согласен, хорошая практика. Вот правда предложение типа
    if ( isArrayEmpty() ) немного хуже читается, чем if ( ArrayIsEmpty() )

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

    doommi Reply:

    >if ( isArrayEmpty() ) немного хуже читается, чем if ( ArrayIsEmpty() )
    а если использовать хорошую ide, то ArrayIsEmpty — заколебешся искать )) если только все имена держать в памяти %))))
    проще же: ввел is, вызвал хелпера и посмотрел, что из булевых функций у тебя имеется.. 🙂

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

    а у меня вот память девичья.

    BSVi Reply:

    Да, насчет is — ты права. Но насчет венгерской нотации — я всетаки не согласен. Она сложно читается. Более того, когда захочешь поменять тип переменной, прийдется менять все ее включения, некоторые из которых могут и не найтись — получится каша.

    А если хочешь использовать переменную, то держать обьявления в голове всеравно прийдется. Более того, прийдется держать в голове, что эта переменная обозначает, пределы ее изменения и прочую чушь.

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

    Вечером добавлю, кстате про единицы измерения — их всетаки стоит добавлять в названия и про is тоже добавлю.

  2. Ray написал(а) 9 марта, 2010 в 12:40

    > class SpiDriver; // Хорошо
    > uint8 SPIGetChar(); // Плохо

    Согласен со всем, что предложено в этой статье кроме вышеупомянутого момента. Если в названии переменной, а особенно – функции содержится аббревиатура, а не общее слово, думаю, надо писать все буквы большими, как LCDInit(); или SPIGetChar();, желательно даже подчеркиванием отделить аббревиатуру – LCD_Init(); или SPI_GetChar(); так как сразу становится понятно с каким модулем работает функция, это особенно важно в программировании встроенных систем, так как под этими же аббревиатурами некоторые модули записаны в даташитах и юзер-мануалах. Это точно так же, как аббревиатурами названы регистры и биты в них, и их всегда принято писать большими буквами.

    BSVi Reply:

    Насчет аббервиатур — вот хороший пример что бует, если писать все большими буквами, то получится:

    GetHTTPXMLURI

    Прочитать не возможно, хотя все аббревиатуры общеизвестны.

    Если разделять подчерком, то

    Get_HTTP_XML_URI();

    Смотриться ужасно, правда? И такое название будет слишком похоже на макрос.

    А теперь мой вариант

    GetHttpXmlUri();
    Вроде даже не вызывает отвращения.

    Имя модуля действительно удобно ставить в начале к примеру,
    LcdPutString();

    выглядит не намнго хуже
    LСD_PutString();

    В принципе, запись LCD_PutChar() это аналог обращения к экземпляру класса LCD -lcd.PutChar()).

    Согласно остальным соглашениям оно должно быть маленькими буквами. В итоге получатся, что стоит писать

    lcd_PutStr();

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

    Поэтому, мне кажется, что вариант LcdPutString() — наилучший. Более того, этот вариант проверен гуглом, микриумом и прочими монстрами. Они конечно тоже могут заблуждаться, но что-то мне подсказывает, что их опыту можно доверять.

    Примеры названий функций от микрума
    OSTaskCreateExt
    OSSemPendAbort
    OSTaskNameSet
    OSStart

    название переменных:
    OS_TCB *ptcb;
    INT8U i;
    OS_CPU_SR cpu_sr = 0u;

    от гугла:
    void BrowserTitlebar::Init()
    void BrowserTitlebar::Observe(NotificationType type,
    const NotificationSource& source,
    const NotificationDetails& details)

    в исходниках гугла на яве можно нати довольно красивые выражения:

    getScaledHeight(DisplayMetrics metrics)
    isRecycled()
    hasAlpha()

    и, для сравнения — венгерская нотация от микрософта

    INT GetAddressByName(
    DWORD dwNameSpace, // name space to query for service address information
    LPGUID lpServiceType, // the type of the service
    LPTSTR lpServiceName, // the name of the service
    LPINT lpiProtocols, // points to array of protocol identifiers
    DWORD dwResolution, // set of bit flags that specify aspects of name resolution
    LPSERVICE_ASYNC_INFO lpServiceAsyncInfo, // reserved for future use, must be NULL
    LPVOID lpCsaddrBuffer, // points to buffer to receive address information
    LPDWORD lpdwBufferLength, // points to variable with address buffer size information
    LPTSTR lpAliasBuffer, // points to buffer to receive alias information
    LPDWORD lpdwAliasBufferLength // points to variable with alias buffer size information
    );

    Пример оформления кода согласно тому, что я тут написал от самого гугла легко найти в тырнете, к примеру в исходниках их браузера — хрома
    http://www.google.com/codesearch/p?hl=ru#h0RrPvyPu-c/chrome/browser/gtk/browser_titlebar.cc&q=google%20chrome%20lang:c++&d=3

  3. Electroniq написал(а) 10 марта, 2010 в 16:30

    Хорошую тему поднял, очень полезно 🙂 Только я вот одно понять не могу, почему рекомендуется не юзать макросы, ведь мне кажется очень удобно писать

    #define TSTBIT1(ADDRESS,B) (ADDRESS & (BIT(B)))// проверка бита на единицу
    #define BUTTON_2_ON TSTBIT1(PIND,7)

    Еще конечно проблема как лучше писать например SpiInit() или InitSpi(), LcdPutString() или PutStringLcd()

    BSVi Reply:

    Макросы не рекомендуется юзать, потому, что они небезопасны.

    Альтернатива — inline функции с константными аргументами. И константные переменные.

    Хотя, конечно, для встраиваемых систем можно сделать исключение, потому как тут случайная не inline’изация может дорого обойтись.

    >Еще конечно проблема как лучше писать например SpiInit() или InitSpi(), >LcdPutString() или PutStringLcd()
    Проблемы никакой нет. Сначала название модуля, потом действие
    Правильные варианты —
    SpiInit()
    LcdPutString()

    Electroniq Reply:

    То есть положение глагола может быть любым в названии функции? Как его правильно выбрать? В примере приведено OpenFile(), можно ли написать FileOpen() или UartBaudRateSet(), что нелогично ?

    BSVi Reply:

    В варианте с фалом лушче FileOpen(), так как первое слово указывает на модуль — файл.

    С уартом — глагол лушче стаивть так, как это читается по английски —
    UartSetBaudRate()

    doommi Reply:

    имхо, лучше OpenFile(), т.к. это — функция/процедура, т.е. главное — действие. Т.е. она открывает, а вот что открывает — это уже второстепенное.

    BSVi Reply:

    >это уже второстепенное
    Что первостепенно — вопрос спорный. Функция — это всегда действие.

    OpenFile() может оказаться лучше, так как это элемент библиотеки VCL. Да и читается лучше. А вот большинство фаловых систем экспортируют функцию
    f_open(), что вяляется аналогом FileOpen().

    Я, все-таки, считаю, что модуль, к которому относитьсся функция должен быть на первом месте. Меня поддерживает и Страуструп, к примеру, плюсовая запись FileOpen():

    ExcitingFileSystem::awesome_file.Open();

  4. lord.tiran написал(а) 15 марта, 2011 в 23:55

    всегда везде все пытаются придумать свой coding style … я например программист и пишу на с++ … могу предложить общепринятый и проверенный временем coding style … имя ему KDE coding style — этого стиля придерживаются разработчики linux … и Qt … глупо придумывать свое свое когда есть уже что-то чего придерживается огромное количество людей … например table_name — выглядит ужасно .. куда приятнее читать tableName … да и вообще лучше писать с маленькой буквы начало имени переменной — так читабельнее … как по мне KDE coding style — вот чего стоит придерживаться …

    lord.tiran Reply:

    да у и конечно венгерская нотация это ****** если кто-то хочет ее предложить …

    BSVi Reply:

    А я и не придумывал свой стиль. Это — перевод coding style от google, в статье это явно указано.

  5. lord.tiran написал(а) 16 марта, 2011 в 8:36

    да … видимо в 2 часа ночи лучше внимательнее вчитываться в текст — буду внимательнее … но мне все равно не нравиться знак нижнего подчеркивания в именах переменных … tableName — куда нагляднее

    BSVi Reply:

    Знак подчеркивания — штука правильная, особенно, если бы он был в начале слова, к примеру _table_name — так автоподстановка сразу покажет все локальные переменные класса. К сожалению, подчеркивание в начале класса зарезервировано для внутренних нужд си-библиотек.

    lord.tiran Reply:

    Думаю … это уже дело вкуса … его ставить не удобно — тянуться далеко … в KDE coding stule нижнее подчеркивание ставиться только в именах файлов … например v_robot_facade.h в этом фале лежит класс VRobotFacade …

    по опыту могу сказать что нужно и приходиться придерживаться того стиля который либо если в библиотеке если вы ее расширяете … либо стиля выбранного в начале очередного программного проекта … ибо нет ничего ужаснее чем огромный проект написанный в разных стилях …

    да и еще я бы добавил что все члены класса ДОЛЖНЫ быть с префиксом m_ … с ним куда удобнее читать и понимать чужой код — а это то для чего и придуман стиль … чтобы быстрее в коде разобраться …

    BSVi Reply:

    Ну, соглашение целиком и полностью — дело вкуса. В гугльстайле вместо превикса используется постфикс. Я, кстате, согласен, что префикс — лучше.

Создать новую ветку комментариев


Вы должны войти или зарегистрироваться чтобы оставить комментарий.