А какую систему нотации используете вы? 0



Доброго времени суток, хабровчане.

После многих лет написания, дописания, рефакторинга, дебаггинга своего/чужого кода на C++, и не только, по ряду причин я в конце концов пришел к вот такой системе нотации. Возможно она во многом похожа на венгерскую.

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

И да, давно хотел узнать, кто какую нотацию использует. Кому интересно прошу под кат.

Префиксы в наименовании идентификаторов


Основная часть имени пишется с прописной буквы.

По порядку следования символа в префиксе:

  1. Пространство использования:

    • g — глобальная переменная;
    • p — параметр функции;
    • v — локальная переменная внутри функции;
    • атрибуты/данные класса не имеют метки пространства использования.

  2. Тип:

    • n — Целое число (integer);
    • f — Число с плавающей запятой (float);
    • c — Символ (character);
    • s — Текстовая строка (string);
    • b — Логический флаг, истина или ложь (boolean);
    • d — Дата и/или время (date/time);
    • m — Карта, набор пар «ключ»-«значение» (map).
    • l — Блокиратор (locker);
    • o — нетривиальный тип (object);

  3. Если это набор значений:

    • a — массив, список, множество (array).

  4. Если это указатель или неконстантная ссылка:

    • r — неконстантная ссылка (reference);
    • i — указатель на объект (pointer).


Правила именования для других идентификаторов


  • MAX_NAME_LEN — константные выражения;
  • E.. — перечисления;
  • i, j, x, y, v1, v2 — a..z0..9, единственный символ с возможно последующей цифрой, временная переменная без особого смысла;
  • getVal, get_value — названия функций/методов, первое слово со строчной буквы, остальные — с прописной, особо ядрёные малыми, через подчёркивание;
  • MyClass/MyType — названия типов/классов — каждое слово с прописной.

Если функция получается более менее громоздкая, то возвращаемое значение именую Ret или Res c сопутствующим префиксом. Например:

std::vector<int> vnaRet{0, 1, 2};

Пример использования
#include <string>

using uint = unsigned int;
using slong = signed long long;
using str = std::string;


// Returns length of textual representation of positive version of Val.
uint intWidth( slong pnVal )
{
  uint vnRet{1};
  slong vnAbs{pnVal < 0 ? ( -1 ) * pnVal : pnVal};
  pnVal = 10;

  while( pnVal <= vnAbs )
  {
    vnRet++;
    pnVal *= 10;
  }

  return vnRet;
}

/* Fills string Dest with textual representation of integer number Val.
 * Returns this Dest as a result.
 * Len will contain the length of result string.
 * Beware of using Dest that points to too short characters array.
 */
char *intToStrW( char *const pciDest, slong pnSour, uint &pnrLen )
{
  uint vnSigned{pnSour < 0 ? 1U : 0U};
  pnrLen = vnSigned + intWidth( pnSour );
  uint vnLen{pnrLen};

  if( vnSigned )
  {
    pciDest[0] = '-';
    pnSour *= -1;
  }

  pciDest[vnLen] = 0;

  while( pnSour )
  {
    vnLen--;
    pciDest[vnLen] = ( pnSour % 10 + '0' );
    pnSour /= 10;
  }

  return pciDest;
}



Мне очень интересны ваши комментарии.

Спасибо за внимание.

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (57):

  1. Tyiler
    /#19139839

    1. префикс 'g_' — глобальная переменная
    постфикс '_' — атрибуты/данные класса

    2. Типы никак

    3. Никак

    4. префикс 'p' (не всегда)

  2. i360u
    /#19139895

    Я, после своих многих лет написания, пришел к следующим выводам:


    1. Постфиксы лучше чем префиксы
    2. Односимвольные идентификаторы только мешают читать код, лучше 2-3 символа
    3. Более одного идентификатора на одну переменную — плохо
    4. Верблюды — рулят

    • AntonSazonov
      /#19140977

      Я, почему-то, пришел почти к обратным выводам. На половину…
      Чем постфикс лучше? Префикс видно сразу, т.к. он вначале.
      Про один хороший недостаток CamelCase недавно писали в блоге PVS-Studio. Предпочитаю snake_case.

      • cooltonbek
        /#19141157

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

      • i360u
        /#19141623

        Чем постфикс лучше?

        Тем, что конкретная суть, выраженная в именовании, важнее типа.

        • AntonSazonov
          /#19142067

          Собственно, я и говорил про именование. Про тип я ничего не говорил.

          • i360u
            /#19142569

            Ну тут мы ведь вариацию венгерской нотации обсуждаем а не именования вообще.

      • AntonSazonov
        /#19141987

        А за что минусуют?
        Я просто высказал своё мнение и никому ничего не навязываю.

        • cooltonbek
          /#19142007

          Не обращайте внимания на минусы. Оно того не стоит.

  3. CoolCmd
    /#19139897

    vnaRet

    я бы написал vanRet

    в каких случаях помогает префикс v?

    • cooltonbek
      /#19139983

      Признак локальности переменной...

      • AntonSazonov
        /#19140909

        Почему не l (local)?

        • cooltonbek
          /#19140927

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

          • AntonSazonov
            /#19141067

            На самом деле, для локальных я просто не использую ни префиксов, ни постфиксов (постфиксы вообще не использую). Для всего остального есть префиксы: g — global, m — member/method и т.д. и т.п…
            Так мне проще понять что переменная без пре/пост — это именно локальная переменная, но есть и исключения…
            Для локальных указателей и ссылок я использую префиксы p и r соответственно.

            • cooltonbek
              /#19141123

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

  4. Wilk
    /#19139939

    Здравствуйте!

    Последнее время по большей части следую Google C++ Style Guide с некоторыми модификациями в именовании файлов, в ряде случаев использую статические объекты нетривиальных типов (если уверен, что ничего не сломается — кодовая база детских размеров). Единственные префиксы, которые использую, применяются к параметрам функции:

    • i_ — входные параметры;
    • o_ — выходные параметры;
    • io_ — параметры с двухсторонним потоком информации, если можно так сказать.


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

    • cooltonbek
      /#19140463

      Когда-то давно, когда писал логику внутри оракловой БД на PL/SQL, тоже ставил такое.

      • Wilk
        /#19140487

        Здравствуйте!

        Мой код на plpgsql придерживается сходной нотации. Разве что io_ префикс не используется, а локальные переменные имеют префикс _.

        • cooltonbek
          /#19140527

          Здравствуйте.
          Вполне логично.
          Спасибо за ответ.

  5. Tiendil
    /#19139947 / +3

    Вносить тип данных в имя переменной — моветон.

    • cooltonbek
      /#19140449

      Не пользуюсь правилами, которые оказываются на поверку неэффективными.
      Опубликовал это только после 3-4 лет использования, поняв, что так всё таки в перспективе эффективнее.
      Спасибо, что высказали ваше мнение.

      • sith
        /#19143435

        Если говорить, про c#, то это рекомендации от Microsoft

        Одна из проблем — необходимость переименовывать переменную, после изменения типа данных.

        • Lofer
          /#19143523

          Одна из проблем — необходимость переименовывать переменную, после изменения типа данных.

          Проблема в том, что заставляет подумать и «объяснить» коллегам, что ты хотел сделать :)
          В общем-то нету «рекомендаций от микрософта», есть рекомендации от разных комманд из Микрософта. На этой неделе пару штук «от Микрософта» пересматривал.

        • cooltonbek
          /#19145293

          После того, как в некоторых случаях изменение типа повлекло за собой некоторые довольно труднодиагностируемые баги, я сознательно и намеренно внес тип в название. Как раз чтобы посмотреть все случаи использования этой переменной и предотвратить неверное толкование или нверное использование с изменившимся типом. Не люблю автоматизацию в некоторых местах. Т.е. это в моём случае получается не бага, а фича.
          C++ знаете ли он такой. За ним глаз да глаз нужен. Особеннов во всяких узких местах, оптимизированных на производительность и т.п.

  6. dipsy
    /#19140039

    Стараюсь придерживаться чего-то такого:
    type_name, class_name, function_name()
    VariableName, ObjectName, ConstDefinedValue
    eEnumName { eEnumValue, (или просто EnumValue) }

    За некоторыми исключениями, например имя енума могу написать как и type_name, если оно по смыслу ближе к «типу» значения.
    Пристыковывать префиксы из типа и прочих характеристик не считаю нужным, во первых тип может и поменяться, во вторых, очень некрасиво выглядит, хзДобавлять йфМусор к именам, в третьих простые типы почти не использую, вместо int я скорее задефайню typedef int my_type и буду везде использовать my_type, в выбранном контексте, например если он обозначает количество колес: wheels_count WheelsCount= 4.

  7. avost
    /#19140311 / +1

    Зачем это все? Редакторы с подсветкой синтаксиса (а это все редакторы), позволяют прекрасно отделять глобальные переменные от локальных и те и другие от параметров. Выносить в имя переменной информацию о простом типе — вообще какая-то дичь. Зачем? Вы, посмотрев на метод, тут же забываете что у вас там за переменные? Или у вас методы настолько большие, что к середине забываете, что было в начале? Тогда у вас проблема в другом. Плюс имя должно отображать предназначение переменной из которого должен быть понятен тип. Если не ясен, то у вас проблема с именованием, а не с префиксами. А если и этого мало и все равно потребовалось уточнить — опять же редактор подскажет. Ну, и до кучи — если все равно хочется этого головняка с префиксами, то, на мой взгляд, лучше использовать наиболее широко представленный в индустрии вариант, а не изобретать свой велосипед. По армейскому принципу — пусть безобразно, зато единообразно.

    • cooltonbek
      /#19140577

      Я тоже вначале так думал, поверьте.
      Жизнь Продакшн внёс свои коррективы, причем вносил очень долго и упорно.
      При работе с кучей людей, с тоннами чужого кода, с тоннами своего старого кода, который не можешь быстро отредактировать, в конце концов приходишь пусть не к идеальному, но к определенному своду правил, которые помогают не утонуть и не застрять.
      Свои велосипеды изобретаю постоянно, и пришел к выводу что это нормально.
      Думаю, что плохо бездумно следовать где-то прочитанным правилам, не испытав их в практике.
      Конечно никому не навязываю, просто делюсь своим опытом.
      Как раз и хотел узнать, кто и что думает об этом.
      Спасибо за ваш ответ, пусть и содержащий вопрос.

      • sith
        /#19143451

        При работе с кучей людей

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

        Не можешь прийти к определённому своду правил, потому, что этот свод у каждой компании свой. Но, вообще, хорошие компании, имеют документ, где эти правила описаны, и, чаще всего, рекомендации в этом документе совпадают с рекомендациями от Microsoft.

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

        • Serge78rus
          /#19144201

          У Вас начало и конец предпоследнего абзаца несколько противоречат друг другу.

  8. mcferden
    /#19140347 / +1

    Майерс говорил, что надо писать type-agnostic код, т.е. так, чтобы не зная ничего о типах данных, я мог понять, что он делает, и какой контракт выполняет. Идентификаторы должны точно и однозначно описывать семантику своего значения. Если соблюдать это правило, то никакие иные соглашения будут не нужны.

    То, что вы приводите в пример — это, извините, совершенно не читаемая и непонятная криптография. Если мне информация о типах не нужна, то все эти префиксы создают только визуальный шум. А если бы она мне понадобилась, то я бы посмотрел на сигнатуру функции или на объявление переменной, потому что это быстрее, чем расшифровывать очередной vnaRes.

    • cooltonbek
      /#19140393

      У всех свой опыт использования, свои убеждения.
      Мейерса, Саттера, Страуструпа, Александреску и других отличных авторов уважаю и ценю, пользуюсь многими их советами.
      Тем не менее пришел после многих лет к этому.
      Просто делюсь своим видением. И интересуюсь мнением хабралюдей.
      Никому не навязываю и не говорю, что нужно и другим так писать.
      Возможно когда-то окажется, что я неправ, не знаю.
      Очень часто приходится рефакторить, и это меня спасло.
      Просто факт из моей практики, и всё.

  9. Serge78rus
    /#19140743

    Когда-то давно тоже искал «свой стиль», Интернета тогда не было, поэтому ориентировался на книги и чужой код. В какой-то момент даже показалось, что нашел то, что мне удобно. Потом ко мне попал документ от Борланда о принятых у них правилах именования, очень вдохновило то, что это во многом совпадало с тем, к чему пришел самостоятельно. Одно время даже дошел до маразма переделывать чужой код, с которым предстояло серьезно разбираться, под свой стиль. Со временем это прошло и сейчас читаю чужой код, написанный в любом стиле и это не доставляет мне никакого неудобства.
    Сам придерживаюсь очень ограниченного свода правил, похожих на принятые в Java (пишу в основном на C++ и C, изредка JavaScript):
    -свои типы с большой буквы (библиотечные — как уж есть)
    -все переменные и функции — с маленькой
    -префиксов не использую за редким исключением для указателей (в основном в низкоуровневом C коде) — ptr
    -основное внимание на содержательность имен (иногда даже рефакторю код только из-за переименования неудачно выбранных имен)
    -многословные имена в основном верблюжим стилем
    -однобуквенные имена только для переменных в пределах нескольких строк.

    • cooltonbek
      /#19140899

      Спасибо за ответ, познавательно.
      У меня на самом деле примерно такая же история.
      Просто когда нужно рефакторить чужой код, когда он вписывается в мой проект, он достаточно нетривиален, и непонятно, что к чему, приходится немного поработать с наименованиями переменных.
      И дальше вдруг открывается видение смысла написанного кода, что, как, откуда и куда.
      И понятно, где и что надо исправлять, оптимизировать и т.п.
      А насчет своего кода я уже автоматически ставлю префиксы, уже не замечая.
      При чтении вижу только основное название переменной, которое начинается с прописной.
      Когда при анализе или редактировании нужно уточнить, что, откуда и куда, глаз фокусируется на префиксе. И в 95% случаев я получаю ответ на свой вопрос и иду дальше.
      Иногда при процессе «правильного» именования всплывают сразу недочеты и даже ошибки, которые просто еще не успели «выстрелить».

      • Serge78rus
        /#19141125

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

  10. geher
    /#19141855

    Обычно просто приспосабливаюсь к нотации, принятой в проекте.
    Если же есть возможность выбирать (для проектов, исполняемых в одно лицо), то никаких префиксов, суффиксов и иных элементов, описывающих тип переменной. Редактор тип подсветит, если надо. А нотация типов в именах переменных только усложняет. Одна-две буквы — совершенно не интуитивно (например, почему целое — это n, а не i, почему один на все целые, а не на каждый размер целого с пометкой наличия знака). Писать тип полностью — перегружается имя, и акцент сильно смещается со смысла переменной на ее тип.
    Использую префиксы — T (тип) и P (тип указателя) для выделения типов, чтобы отличить их от переменных (приучился в Delphi, как-то свыкся и нахожу для себя удобным).
    Предпочитаю верблюжью нотацию, поскольку мной (и не только) она воспринимается лучше альтернатив.
    Не считаю зазорным использовать однобуквенные и сокращенные имена переменных в качестве счетчиков цикла, если нет глубокой вложенности и в случаях, когда оно так давно принято (например, i в качестве счетчика цикла, sin — вместо Sinus, n в качестве количества, если оно именно так прописано в математической модели).

    • cooltonbek
      /#19142045

      Ого, как много вопросов.
      Всё просто.
      Потому что у каждого своё видение, свой способ творчества, свой метод логических построений и выводов, да и мне так оказалось удобным с точки зрения моего опыта.
      Я так попробовал, долго экспериментировал и на этом остановился.
      Если вы прочтёте мою заметку еще раз, то увидите, что я ничего не высказывал о «верблюжьей» нотации, и не имел ничего против однобуквенных названий. Они там присутствуют.
      Просто из-за простоты функции в примере верблюжья нотация не пригодилась. Извиняюсь за каламбур.
      Никаких магических чисел не существует.

  11. SUA
    /#19142113

    CamelCase (и да… регистронезависимый ЯП избавляет от его минуса)
    Типизация в имени переменной в век современных IDE уже немного лишнее, для коллекций есть множественное число

  12. berez
    /#19142173 / +1

    В текущем моем проекте такие соглашения:
    — никаких префиксов.
    — из постфиксов — только подчеркивание для дата-мемберов класса (localData_).
    — СamelCase. При этом регистр первой буквы имеет значение: с большой буквы начинаются имена типов, статических и глобальных переменных, статических функций класса. С маленькой буквы — обычные (нестатические) функции-члены класса и всяческие переменные.
    — геттеры и сеттеры оформляются как value()/setValue(newValue) — т. е. никаких get_value() или getValue().
    — для совсем очевидных вещей типа счетчиков цикла разрешено использовать однобуквенные переменные (обычно i). В остальном даже временные переменные должны иметь осмысленное имя.

    • cooltonbek
      /#19142235 / -1

      Ну что могу сказать, это тоже система, она кому-то когда-то понравилась, и теперь её придерживаются.
      Как говорится, на вкус и цвет фломастеры разные.
      Спасибо за ответ.

  13. Tishka17
    /#19142389

    Как хорошо, что в питоне есть pep8


    А в целом согласен с комментариями, что идентификатор должен отражать семантику, а не тип и другую информацию, которая и так доступна в IDE.

    • nad_oby
      /#19152837

      О да pep8 и pylint спасают просто.
      А для языков на которых почти не пишу или рекомендации (если под них линтер настраивается легко) или стиль проекта.

  14. Sun-ami
    /#19142425

    Придерживаюсь мнения, что следует стремиться к тому, чтобы код читался как естественный текст. Поэтому в случае, когда я не ограничен корпоративными требованиями — не использую префиксов на C++, за исключением значений emun, где использую сокращение имени типа как префикс. Префиксы сильно мешают произнесению имён, и тем самым затрудняют чтение. Если приходится писать на С (а это бывает редко)- использую префиксы в именах функций и структур вместо имён классов для получения некого подобия C++. Для интерфейсных и долго живущих переменных, и особенно констант, имеющих физическую размерность часто использую суффикс размерности — _ms, _mV, и т.п. Для локальных переменных использую боле короткие имена, чем меньше область использования — тем короче. Использую нотацию CamelCase — с большой буквы для типов, с маленькой — для переменных и функций. getValue setValue.

    • Serge78rus
      /#19142589

      С тех пор, как в C++ появилась возможность использования enum как полноценного типа, надобность в префиксах и для него отпала.

      • Sun-ami
        /#19142845

        Компиляторы, которые я использую для микроконтроллеров, не поддерживают это.

        • Serge78rus
          /#19144177

          Заинтересовало, кто из компиляторов C++ в 2018 году не поддерживает спецификации C++11.

          • Sun-ami
            /#19144449

            Не все используют последние версии компиляторов — в корпоративной среде для этого есть множество причин. А, к примеру IAR стал поддерживать C++11 не очень давно, и с последними версиями есть другие проблемы. Зато оптимизирует хорошо.

            • Serge78rus
              /#19144571

              Гораздо чаще приходилось сталкиваться с неприятием во встроенных системах C++ вообще (только C и ассемблер). Тем более странно, что нашедшие в себе силы отбросить эти предрассудки, держатся за устаревшие компиляторы. Впрочем — в каждой избушке свои погремушки.

  15. MacIn
    /#19142861 / -1

    Догматикам — бой.

    Префиксы по области видимости, такие как
    l (local) — локальная переменная
    g (global) — глобальная переменная
    f (field) — поле данных класса
    u (unit) — глобальная в рамках модуля
    c (constant) или ABC_DEF стиль
    s (string constant)
    A — формальный параметр

    Префиксы очень помогают. Видишь какой-нибудь ItemIndex — а что это — формальный параметр? Property? Локальная переменная? А хз, ищи давай. А так

    lItemIndex
    — локальная, все красиво, понятно с одного взгляда.

    Ах, и как упомянули выше
    T (type) — для названия типа или класса
    P (pointer) для имени типа указателя.

    Для себя лично давно принял за правило оформлять код так, чтобы он читался легко и однозначно даже будучи распечатанным на бумаге, не говоря о простых редакторах или веб-страницах.
    Подобные правила именования просто делают чтение и понимание кода более легким. Еще более легким, чем подсветка/автоподсказка и т.п.

    • cooltonbek
      /#19142981 / -2

      Спасибо за ответ, отлично оформили свою позицию.
      Всё ясно и понятно.

  16. Lofer
    /#19143507

    Критерии достаточно простые:
    1. Должно быть читаемо и понимаемо вне среды разработки и специальных инструментальных средств.
    2. Должно быть самоописываемое и как можно меньше заставлять «расчесывать мозг». В идеале интуитивно поняно.
    3. Должы быть понятны области видимости переменных их типы и жизненный цикл.
    4. Должны быть различимы переменные и функции/методы
    5. Не должны смешиваться символы в описаниях переменных и методов: Iil1.
    6. Локальные переменные и аргументы можно не различать, поскольку на уровне языков или компилятор отловит ошибку или аргумент будет себя вести как локальная переменная (кроме специальных случаев)
    7. Минимально зависимо от языка.

    Простой визуальный тест — открыл написанный код в блокноте и попытался понять, что ты сам писал пару недель назад или твой коллега.
    Обычно используемая нотация: [Scope]['_' delimiter][LifeCircle][(S/s)hortTypeName](VariableName)
    g — global: g_VariableName
    m — module /class: m_VariableName. сокращенный вариант _VariableName
    -local: VariableName

    m_constStrVariableName = «XYZ»
    m_lMaxRandom = 123;
    byte[] btArrIndexes = byte[...]
    double dblAmount= 222
    User userXyz = GetCurrentUser()

    Сейчас применяется в проекте нотация (по требованию заказчика):
    _ — аргументы функций
    l — локальные
    m — класс
    Честно говоря — никакой проблемы такая нотация не решила, а головной боли подкинула, из за смешения символов:
    lIdentity — не воспринимается визуально, поскольку мозг реагирует как на IInterface.
    if (lId ==_id) заставляет «морщит мозг» что бы вчитаться в имена переменных и «на ходу визуально откомпилировать» код. Синтаксически lId & _id ведут себя одинаково в области видимости, но именованы по разному.

  17. Habra_nik
    /#19145281

    Интересно, я один такой? За последние пять лет не могу вспомнить места, где не требовали бы писать в строгом соответствии со style guide и рьяно комментили его нарушения во время кода ревью. Я часто оставлял мелкие косячки там-сям, отступ там, чтобы коллегам было что сказать :-).

  18. domix32
    /#19148205

    Запилили бы какой-нибудь препроцессор, который атоматически выводил тип из имени переменных

    • MacIn
      /#19152475

      Как-то на ум сразу приходят Бейсик и Фортран.